Passed
Pull Request — develop (#579)
by Alejandro
05:16
created

ListShortUrlsCommand   A

Complexity

Total Complexity 15

Size/Duplication

Total Lines 154
Duplicated Lines 0 %

Test Coverage

Coverage 98.75%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 88
c 2
b 0
f 0
dl 0
loc 154
ccs 79
cts 80
cp 0.9875
rs 10
wmc 15

7 Methods

Rating   Name   Duplication   Size   Complexity  
A getEndDateDesc() 0 3 1
A getStartDateDesc() 0 3 1
A processOrderBy() 0 9 3
A doConfigure() 0 32 1
A __construct() 0 5 1
A execute() 0 26 4
A renderPage() 0 41 4
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Shlinkio\Shlink\CLI\Command\ShortUrl;
6
7
use Cake\Chronos\Chronos;
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\Common\Util\DateRange;
13
use Shlinkio\Shlink\Core\Paginator\Adapter\ShortUrlRepositoryAdapter;
14
use Shlinkio\Shlink\Core\Service\ShortUrlServiceInterface;
15
use Shlinkio\Shlink\Core\Transformer\ShortUrlDataTransformer;
16
use Symfony\Component\Console\Input\InputInterface;
17
use Symfony\Component\Console\Input\InputOption;
18
use Symfony\Component\Console\Output\OutputInterface;
19
use Symfony\Component\Console\Style\SymfonyStyle;
20
use Zend\Paginator\Paginator;
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 ALIASES = ['shortcode:list', 'short-code:list'];
36
    private const COLUMNS_WHITELIST = [
37
        'shortCode',
38
        'shortUrl',
39
        'longUrl',
40
        'dateCreated',
41
        'visitsCount',
42
        'tags',
43
    ];
44
45
    /** @var ShortUrlServiceInterface */
46
    private $shortUrlService;
47
    /** @var ShortUrlDataTransformer */
48
    private $transformer;
49
50 15
    public function __construct(ShortUrlServiceInterface $shortUrlService, array $domainConfig)
51
    {
52 15
        parent::__construct();
53 15
        $this->shortUrlService = $shortUrlService;
54 15
        $this->transformer = new ShortUrlDataTransformer($domainConfig);
55
    }
56
57 15
    protected function doConfigure(): void
58
    {
59
        $this
60 15
            ->setName(self::NAME)
61 15
            ->setAliases(self::ALIASES)
62 15
            ->setDescription('List all short URLs')
63 15
            ->addOption(
64 15
                'page',
65 15
                'p',
66 15
                InputOption::VALUE_REQUIRED,
67 15
                sprintf('The first page to list (%s items per page)', ShortUrlRepositoryAdapter::ITEMS_PER_PAGE),
68 15
                '1'
69
            )
70 15
            ->addOption(
71 15
                'searchTerm',
72 15
                'st',
73 15
                InputOption::VALUE_REQUIRED,
74 15
                'A query used to filter results by searching for it on the longUrl and shortCode fields'
75
            )
76 15
            ->addOption(
77 15
                'tags',
78 15
                't',
79 15
                InputOption::VALUE_REQUIRED,
80 15
                'A comma-separated list of tags to filter results'
81
            )
82 15
            ->addOption(
83 15
                'orderBy',
84 15
                'o',
85 15
                InputOption::VALUE_REQUIRED,
86 15
                'The field from which we want to order by. Pass ASC or DESC separated by a comma'
87
            )
88 15
            ->addOption('showTags', null, InputOption::VALUE_NONE, 'Whether to display the tags or not');
89
    }
90
91 15
    protected function getStartDateDesc(): string
92
    {
93 15
        return 'Allows to filter short URLs, returning only those created after "startDate"';
94
    }
95
96 15
    protected function getEndDateDesc(): string
97
    {
98 15
        return 'Allows to filter short URLs, returning only those created before "endDate"';
99
    }
100
101 15
    protected function execute(InputInterface $input, OutputInterface $output): ?int
102
    {
103 15
        $io = new SymfonyStyle($input, $output);
104
105 15
        $page = (int) $input->getOption('page');
106 15
        $searchTerm = $input->getOption('searchTerm');
107 15
        $tags = $input->getOption('tags');
108 15
        $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

108
        $tags = ! empty($tags) ? explode(',', /** @scrutinizer ignore-type */ $tags) : [];
Loading history...
109 15
        $showTags = (bool) $input->getOption('showTags');
110 15
        $startDate = $this->getDateOption($input, $output, 'startDate');
111 15
        $endDate = $this->getDateOption($input, $output, 'endDate');
112 15
        $orderBy = $this->processOrderBy($input);
113
114
        do {
115 15
            $result = $this->renderPage($output, $page, $searchTerm, $tags, $showTags, $startDate, $endDate, $orderBy);
116 15
            $page++;
117
118 15
            $continue = $this->isLastPage($result)
119 13
                ? false
120 15
                : $io->confirm(sprintf('Continue with page <options=bold>%s</>?', $page), false);
121 15
        } while ($continue);
122
123 15
        $io->newLine();
124 15
        $io->success('Short URLs properly listed');
125
126 15
        return ExitCodes::EXIT_SUCCESS;
127
    }
128
129 15
    private function renderPage(
130
        OutputInterface $output,
131
        int $page,
132
        ?string $searchTerm,
133
        array $tags,
134
        bool $showTags,
135
        ?Chronos $startDate,
136
        ?Chronos $endDate,
137
        $orderBy
138
    ): Paginator {
139 15
        $result = $this->shortUrlService->listShortUrls(
140 15
            $page,
141 15
            $searchTerm,
142 15
            $tags,
143 15
            $orderBy,
144 15
            new DateRange($startDate, $endDate)
145
        );
146
147 15
        $headers = ['Short code', 'Short URL', 'Long URL', 'Date created', 'Visits count'];
148 15
        if ($showTags) {
149 1
            $headers[] = 'Tags';
150
        }
151
152 15
        $rows = [];
153 15
        foreach ($result as $row) {
154 2
            $shortUrl = $this->transformer->transform($row);
155 2
            if ($showTags) {
156
                $shortUrl['tags'] = implode(', ', $shortUrl['tags']);
157
            } else {
158 2
                unset($shortUrl['tags']);
159
            }
160
161 2
            $rows[] = array_values(array_intersect_key($shortUrl, array_flip(self::COLUMNS_WHITELIST)));
162
        }
163
164 15
        ShlinkTable::fromOutput($output)->render($headers, $rows, $this->formatCurrentPageMessage(
165 15
            $result,
166 15
            'Page %s of %s'
167
        ));
168
169 15
        return $result;
170
    }
171
172
    /**
173
     * @return array|string|null
174
     */
175 15
    private function processOrderBy(InputInterface $input)
176
    {
177 15
        $orderBy = $input->getOption('orderBy');
178 15
        if (empty($orderBy)) {
179 12
            return null;
180
        }
181
182 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

182
        $orderBy = explode(',', /** @scrutinizer ignore-type */ $orderBy);
Loading history...
183 3
        return count($orderBy) === 1 ? $orderBy[0] : [$orderBy[0] => $orderBy[1]];
184
    }
185
}
186