Completed
Push — develop ( 9d3653...f17214 )
by Alejandro
16s queued 13s
created

ListShortUrlsCommand::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 3
nc 1
nop 2
dl 0
loc 5
ccs 4
cts 4
cp 1
crap 1
rs 10
c 0
b 0
f 0
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 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 15
    public function __construct(ShortUrlServiceInterface $shortUrlService, array $domainConfig)
48
    {
49 15
        parent::__construct();
50 15
        $this->shortUrlService = $shortUrlService;
51 15
        $this->transformer = new ShortUrlDataTransformer($domainConfig);
52
    }
53
54 15
    protected function doConfigure(): void
55
    {
56
        $this
57 15
            ->setName(self::NAME)
58 15
            ->setDescription('List all short URLs')
59 15
            ->addOption(
60 15
                'page',
61 15
                'p',
62 15
                InputOption::VALUE_REQUIRED,
63 15
                sprintf('The first page to list (%s items per page)', ShortUrlRepositoryAdapter::ITEMS_PER_PAGE),
64 15
                '1'
65
            )
66 15
            ->addOption(
67 15
                'searchTerm',
68 15
                'st',
69 15
                InputOption::VALUE_REQUIRED,
70 15
                'A query used to filter results by searching for it on the longUrl and shortCode fields'
71
            )
72 15
            ->addOption(
73 15
                'tags',
74 15
                't',
75 15
                InputOption::VALUE_REQUIRED,
76 15
                'A comma-separated list of tags to filter results'
77
            )
78 15
            ->addOption(
79 15
                'orderBy',
80 15
                'o',
81 15
                InputOption::VALUE_REQUIRED,
82 15
                'The field from which we want to order by. Pass ASC or DESC separated by a comma'
83
            )
84 15
            ->addOption('showTags', null, InputOption::VALUE_NONE, 'Whether to display the tags or not');
85
    }
86
87 15
    protected function getStartDateDesc(): string
88
    {
89 15
        return 'Allows to filter short URLs, returning only those created after "startDate"';
90
    }
91
92 15
    protected function getEndDateDesc(): string
93
    {
94 15
        return 'Allows to filter short URLs, returning only those created before "endDate"';
95
    }
96
97 15
    protected function execute(InputInterface $input, OutputInterface $output): ?int
98
    {
99 15
        $io = new SymfonyStyle($input, $output);
100
101 15
        $page = (int) $input->getOption('page');
102 15
        $searchTerm = $input->getOption('searchTerm');
103 15
        $tags = $input->getOption('tags');
104 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

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

178
        $orderBy = explode(',', /** @scrutinizer ignore-type */ $orderBy);
Loading history...
179 3
        return count($orderBy) === 1 ? $orderBy[0] : [$orderBy[0] => $orderBy[1]];
180
    }
181
}
182