ListShortUrlsCommand   A
last analyzed

Complexity

Total Complexity 19

Size/Duplication

Total Lines 158
Duplicated Lines 0 %

Test Coverage

Coverage 98.88%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
wmc 19
eloc 97
c 2
b 0
f 0
dl 0
loc 158
rs 10
ccs 88
cts 89
cp 0.9888

7 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A getEndDateDesc() 0 3 1
A getStartDateDesc() 0 3 1
A processOrderBy() 0 9 3
A doConfigure() 0 37 1
A renderPage() 0 27 5
B execute() 0 41 7
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Shlinkio\Shlink\CLI\Command\ShortUrl;
6
7
use Laminas\Paginator\Paginator;
8
use Shlinkio\Shlink\CLI\Command\Util\AbstractWithDateRangeCommand;
9
use Shlinkio\Shlink\CLI\Util\ExitCodes;
10
use Shlinkio\Shlink\CLI\Util\ShlinkTable;
11
use Shlinkio\Shlink\Common\Paginator\Util\PaginatorUtilsTrait;
12
use Shlinkio\Shlink\Core\Model\ShortUrlsOrdering;
13
use Shlinkio\Shlink\Core\Model\ShortUrlsParams;
14
use Shlinkio\Shlink\Core\Service\ShortUrlServiceInterface;
15
use Shlinkio\Shlink\Core\Transformer\ShortUrlDataTransformer;
16
use Shlinkio\Shlink\Core\Validation\ShortUrlsParamsInputFilter;
17
use Symfony\Component\Console\Input\InputInterface;
18
use Symfony\Component\Console\Input\InputOption;
19
use Symfony\Component\Console\Output\OutputInterface;
20
use Symfony\Component\Console\Style\SymfonyStyle;
21
22
use function array_flip;
23
use function array_intersect_key;
24
use function array_values;
25
use function count;
26
use function explode;
27
use function implode;
28
use function sprintf;
29
30
class ListShortUrlsCommand extends AbstractWithDateRangeCommand
31
{
32
    use PaginatorUtilsTrait;
33
34
    public const NAME = 'short-url:list';
35
    private const COLUMNS_WHITELIST = [
36
        'shortCode',
37
        'shortUrl',
38
        'longUrl',
39
        'dateCreated',
40
        'visitsCount',
41
        'tags',
42
    ];
43
44
    private ShortUrlServiceInterface $shortUrlService;
45
    private ShortUrlDataTransformer $transformer;
46
47 16
    public function __construct(ShortUrlServiceInterface $shortUrlService, array $domainConfig)
48
    {
49 16
        parent::__construct();
50 16
        $this->shortUrlService = $shortUrlService;
51 16
        $this->transformer = new ShortUrlDataTransformer($domainConfig);
52 16
    }
53
54 16
    protected function doConfigure(): void
55
    {
56
        $this
57 16
            ->setName(self::NAME)
58 16
            ->setDescription('List all short URLs')
59 16
            ->addOption(
60 16
                'page',
61 16
                'p',
62 16
                InputOption::VALUE_REQUIRED,
63 16
                'The first page to list (10 items per page unless "--all" is provided)',
64 16
                '1',
65
            )
66 16
            ->addOption(
67 16
                'searchTerm',
68 16
                'st',
69 16
                InputOption::VALUE_REQUIRED,
70 16
                'A query used to filter results by searching for it on the longUrl and shortCode fields',
71
            )
72 16
            ->addOption(
73 16
                'tags',
74 16
                't',
75 16
                InputOption::VALUE_REQUIRED,
76 16
                'A comma-separated list of tags to filter results',
77
            )
78 16
            ->addOption(
79 16
                'orderBy',
80 16
                'o',
81 16
                InputOption::VALUE_REQUIRED,
82 16
                'The field from which we want to order by. Pass ASC or DESC separated by a comma',
83
            )
84 16
            ->addOption('showTags', null, InputOption::VALUE_NONE, 'Whether to display the tags or not')
85 16
            ->addOption(
86 16
                'all',
87 16
                'a',
88 16
                InputOption::VALUE_NONE,
89
                'Disables pagination and just displays all existing URLs. Caution! If the amount of short URLs is big,'
90 16
                . ' this may end up failing due to memory usage.',
91
            );
92 16
    }
93
94 16
    protected function getStartDateDesc(): string
95
    {
96 16
        return 'Allows to filter short URLs, returning only those created after "startDate"';
97
    }
98
99 16
    protected function getEndDateDesc(): string
100
    {
101 16
        return 'Allows to filter short URLs, returning only those created before "endDate"';
102
    }
103
104 16
    protected function execute(InputInterface $input, OutputInterface $output): ?int
105
    {
106 16
        $io = new SymfonyStyle($input, $output);
107
108 16
        $page = (int) $input->getOption('page');
109 16
        $searchTerm = $input->getOption('searchTerm');
110 16
        $tags = $input->getOption('tags');
111 16
        $tags = ! empty($tags) ? explode(',', $tags) : [];
0 ignored issues
show
Bug introduced by
It seems like $tags can also be of type string[]; however, parameter $string of explode() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

111
        $tags = ! empty($tags) ? explode(',', /** @scrutinizer ignore-type */ $tags) : [];
Loading history...
112 16
        $showTags = (bool) $input->getOption('showTags');
113 16
        $all = (bool) $input->getOption('all');
114 16
        $startDate = $this->getDateOption($input, $output, 'startDate');
115 16
        $endDate = $this->getDateOption($input, $output, 'endDate');
116 16
        $orderBy = $this->processOrderBy($input);
117
118
        $data = [
119 16
            ShortUrlsParamsInputFilter::SEARCH_TERM => $searchTerm,
120 16
            ShortUrlsParamsInputFilter::TAGS => $tags,
121 16
            ShortUrlsOrdering::ORDER_BY => $orderBy,
122 16
            ShortUrlsParamsInputFilter::START_DATE => $startDate !== null ? $startDate->toAtomString() : null,
123 16
            ShortUrlsParamsInputFilter::END_DATE => $endDate !== null ? $endDate->toAtomString() : null,
124
        ];
125
126 16
        if ($all) {
127 1
            $data[ShortUrlsParamsInputFilter::ITEMS_PER_PAGE] = -1;
128
        }
129
130
        do {
131 16
            $data[ShortUrlsParamsInputFilter::PAGE] = $page;
132 16
            $result = $this->renderPage($output, $showTags, ShortUrlsParams::fromRawData($data), $all);
133 16
            $page++;
134
135 16
            $continue = ! $this->isLastPage($result) && $io->confirm(
136 2
                sprintf('Continue with page <options=bold>%s</>?', $page),
137 16
                false,
138
            );
139 16
        } while ($continue);
140
141 16
        $io->newLine();
142 16
        $io->success('Short URLs properly listed');
143
144 16
        return ExitCodes::EXIT_SUCCESS;
145
    }
146
147 16
    private function renderPage(OutputInterface $output, bool $showTags, ShortUrlsParams $params, bool $all): Paginator
148
    {
149 16
        $result = $this->shortUrlService->listShortUrls($params);
150
151 16
        $headers = ['Short code', 'Short URL', 'Long URL', 'Date created', 'Visits count'];
152 16
        if ($showTags) {
153 1
            $headers[] = 'Tags';
154
        }
155
156 16
        $rows = [];
157 16
        foreach ($result as $row) {
158 2
            $shortUrl = $this->transformer->transform($row);
159 2
            if ($showTags) {
160
                $shortUrl['tags'] = implode(', ', $shortUrl['tags']);
161
            } else {
162 2
                unset($shortUrl['tags']);
163
            }
164
165 2
            $rows[] = array_values(array_intersect_key($shortUrl, array_flip(self::COLUMNS_WHITELIST)));
166
        }
167
168 16
        ShlinkTable::fromOutput($output)->render($headers, $rows, $all ? null : $this->formatCurrentPageMessage(
169
            $result,
170 16
            'Page %s of %s',
171
        ));
172
173 16
        return $result;
174
    }
175
176
    /**
177
     * @return array|string|null
178
     */
179 16
    private function processOrderBy(InputInterface $input)
180
    {
181 16
        $orderBy = $input->getOption('orderBy');
182 16
        if (empty($orderBy)) {
183 13
            return null;
184
        }
185
186 3
        $orderBy = explode(',', $orderBy);
0 ignored issues
show
Bug introduced by
It seems like $orderBy can also be of type string[]; however, parameter $string of explode() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

186
        $orderBy = explode(',', /** @scrutinizer ignore-type */ $orderBy);
Loading history...
187 3
        return count($orderBy) === 1 ? $orderBy[0] : [$orderBy[0] => $orderBy[1]];
188
    }
189
}
190