Completed
Push — master ( 35950a...014eb2 )
by Alejandro
25s queued 11s
created

ListShortUrlsCommand::processOrderBy()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 3.3332

Importance

Changes 0
Metric Value
cc 3
eloc 5
nc 3
nop 1
dl 0
loc 9
rs 10
c 0
b 0
f 0
ccs 4
cts 6
cp 0.6667
crap 3.3332
1
<?php
2
declare(strict_types=1);
3
4
namespace Shlinkio\Shlink\CLI\Command\ShortUrl;
5
6
use Shlinkio\Shlink\CLI\Util\ExitCodes;
7
use Shlinkio\Shlink\Common\Console\ShlinkTable;
8
use Shlinkio\Shlink\Common\Paginator\Adapter\PaginableRepositoryAdapter;
9
use Shlinkio\Shlink\Common\Paginator\Util\PaginatorUtilsTrait;
10
use Shlinkio\Shlink\Common\Rest\DataTransformerInterface;
11
use Shlinkio\Shlink\Core\Service\ShortUrlServiceInterface;
12
use Shlinkio\Shlink\Core\Transformer\ShortUrlDataTransformer;
13
use Symfony\Component\Console\Command\Command;
14
use Symfony\Component\Console\Input\InputInterface;
15
use Symfony\Component\Console\Input\InputOption;
16
use Symfony\Component\Console\Output\OutputInterface;
17
use Symfony\Component\Console\Style\SymfonyStyle;
18
use Zend\Paginator\Paginator;
19
20
use function array_flip;
21
use function array_intersect_key;
22
use function array_values;
23
use function count;
24
use function explode;
25
use function implode;
26
use function sprintf;
27
28
class ListShortUrlsCommand extends Command
29
{
30
    use PaginatorUtilsTrait;
31
32
    public const NAME = 'short-url:list';
33
    private const ALIASES = ['shortcode:list', 'short-code:list'];
34
    private const COLUMNS_WHITELIST = [
35
        'shortCode',
36
        'shortUrl',
37
        'longUrl',
38
        'dateCreated',
39
        'visitsCount',
40
        'tags',
41
    ];
42
43
    /** @var ShortUrlServiceInterface */
44
    private $shortUrlService;
45
    /** @var array */
46
    private $domainConfig;
47
48 5
    public function __construct(ShortUrlServiceInterface $shortUrlService, array $domainConfig)
49
    {
50 5
        parent::__construct();
51 5
        $this->shortUrlService = $shortUrlService;
52 5
        $this->domainConfig = $domainConfig;
53
    }
54
55 5
    protected function configure(): void
56
    {
57
        $this
58 5
            ->setName(self::NAME)
59 5
            ->setAliases(self::ALIASES)
60 5
            ->setDescription('List all short URLs')
61 5
            ->addOption(
62 5
                'page',
63 5
                'p',
64 5
                InputOption::VALUE_OPTIONAL,
65 5
                sprintf('The first page to list (%s items per page)', PaginableRepositoryAdapter::ITEMS_PER_PAGE),
66 5
                '1'
67
            )
68 5
            ->addOption(
69 5
                'searchTerm',
70 5
                's',
71 5
                InputOption::VALUE_OPTIONAL,
72 5
                'A query used to filter results by searching for it on the longUrl and shortCode fields'
73
            )
74 5
            ->addOption(
75 5
                'tags',
76 5
                't',
77 5
                InputOption::VALUE_OPTIONAL,
78 5
                'A comma-separated list of tags to filter results'
79
            )
80 5
            ->addOption(
81 5
                'orderBy',
82 5
                'o',
83 5
                InputOption::VALUE_OPTIONAL,
84 5
                'The field from which we want to order by. Pass ASC or DESC separated by a comma'
85
            )
86 5
            ->addOption('showTags', null, InputOption::VALUE_NONE, 'Whether to display the tags or not');
87
    }
88
89 5
    protected function execute(InputInterface $input, OutputInterface $output): ?int
90
    {
91 5
        $io = new SymfonyStyle($input, $output);
92 5
        $page = (int) $input->getOption('page');
93 5
        $searchTerm = $input->getOption('searchTerm');
94 5
        $tags = $input->getOption('tags');
95 5
        $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

95
        $tags = ! empty($tags) ? explode(',', /** @scrutinizer ignore-type */ $tags) : [];
Loading history...
96 5
        $showTags = (bool) $input->getOption('showTags');
97 5
        $transformer = new ShortUrlDataTransformer($this->domainConfig);
98
99
        do {
100 5
            $result = $this->renderPage($input, $output, $page, $searchTerm, $tags, $showTags, $transformer);
101 5
            $page++;
102
103 5
            $continue = $this->isLastPage($result)
104 3
                ? false
105 5
                : $io->confirm(sprintf('Continue with page <options=bold>%s</>?', $page), false);
106 5
        } while ($continue);
107
108 5
        $io->newLine();
109 5
        $io->success('Short URLs properly listed');
110 5
        return ExitCodes::EXIT_SUCCESS;
111
    }
112
113 5
    private function renderPage(
114
        InputInterface $input,
115
        OutputInterface $output,
116
        int $page,
117
        ?string $searchTerm,
118
        array $tags,
119
        bool $showTags,
120
        DataTransformerInterface $transformer
121
    ): Paginator {
122 5
        $result = $this->shortUrlService->listShortUrls($page, $searchTerm, $tags, $this->processOrderBy($input));
123
124 5
        $headers = ['Short code', 'Short URL', 'Long URL', 'Date created', 'Visits count'];
125 5
        if ($showTags) {
126 1
            $headers[] = 'Tags';
127
        }
128
129 5
        $rows = [];
130 5
        foreach ($result as $row) {
131 2
            $shortUrl = $transformer->transform($row);
132 2
            if ($showTags) {
133
                $shortUrl['tags'] = implode(', ', $shortUrl['tags']);
134
            } else {
135 2
                unset($shortUrl['tags']);
136
            }
137
138 2
            $rows[] = array_values(array_intersect_key($shortUrl, array_flip(self::COLUMNS_WHITELIST)));
139
        }
140
141 5
        ShlinkTable::fromOutput($output)->render($headers, $rows, $this->formatCurrentPageMessage(
142 5
            $result,
143 5
            'Page %s of %s'
144
        ));
145 5
        return $result;
146
    }
147
148 5
    private function processOrderBy(InputInterface $input)
149
    {
150 5
        $orderBy = $input->getOption('orderBy');
151 5
        if (empty($orderBy)) {
152 5
            return null;
153
        }
154
155
        $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

155
        $orderBy = explode(',', /** @scrutinizer ignore-type */ $orderBy);
Loading history...
156
        return count($orderBy) === 1 ? $orderBy[0] : [$orderBy[0] => $orderBy[1]];
157
    }
158
}
159