diff --git a/module/CLI/src/Command/RedirectRule/ManageRedirectRulesCommand.php b/module/CLI/src/Command/RedirectRule/ManageRedirectRulesCommand.php index e36fcf59..13b6d1cc 100644 --- a/module/CLI/src/Command/RedirectRule/ManageRedirectRulesCommand.php +++ b/module/CLI/src/Command/RedirectRule/ManageRedirectRulesCommand.php @@ -4,16 +4,14 @@ declare(strict_types=1); namespace Shlinkio\Shlink\CLI\Command\RedirectRule; +use Shlinkio\Shlink\CLI\Input\ShortUrlIdentifierInput; use Shlinkio\Shlink\CLI\RedirectRule\RedirectRuleHandlerInterface; use Shlinkio\Shlink\CLI\Util\ExitCode; use Shlinkio\Shlink\Core\Exception\ShortUrlNotFoundException; use Shlinkio\Shlink\Core\RedirectRule\ShortUrlRedirectRuleServiceInterface; -use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlIdentifier; use Shlinkio\Shlink\Core\ShortUrl\ShortUrlResolverInterface; use Symfony\Component\Console\Command\Command; -use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; @@ -23,27 +21,32 @@ class ManageRedirectRulesCommand extends Command { public const NAME = 'short-url:manage-rules'; + private readonly ShortUrlIdentifierInput $shortUrlIdentifierInput; + public function __construct( protected readonly ShortUrlResolverInterface $shortUrlResolver, protected readonly ShortUrlRedirectRuleServiceInterface $ruleService, protected readonly RedirectRuleHandlerInterface $ruleHandler, ) { parent::__construct(); + $this->shortUrlIdentifierInput = new ShortUrlIdentifierInput( + $this, + shortCodeDesc: 'The short code which rules we want to set.', + domainDesc: 'The domain for the short code.', + ); } protected function configure(): void { $this ->setName(self::NAME) - ->setDescription('Set redirect rules for a short URL') - ->addArgument('shortCode', InputArgument::REQUIRED, 'The short code which rules we want to set.') - ->addOption('domain', 'd', InputOption::VALUE_REQUIRED, 'The domain for the short code.'); + ->setDescription('Set redirect rules for a short URL'); } protected function execute(InputInterface $input, OutputInterface $output): int { $io = new SymfonyStyle($input, $output); - $identifier = ShortUrlIdentifier::fromCli($input); + $identifier = $this->shortUrlIdentifierInput->toShortUrlIdentifier($input); try { $shortUrl = $this->shortUrlResolver->resolveShortUrl($identifier); diff --git a/module/CLI/src/Command/ShortUrl/DeleteShortUrlCommand.php b/module/CLI/src/Command/ShortUrl/DeleteShortUrlCommand.php index 8196bbfe..63e9dab5 100644 --- a/module/CLI/src/Command/ShortUrl/DeleteShortUrlCommand.php +++ b/module/CLI/src/Command/ShortUrl/DeleteShortUrlCommand.php @@ -4,12 +4,12 @@ declare(strict_types=1); namespace Shlinkio\Shlink\CLI\Command\ShortUrl; +use Shlinkio\Shlink\CLI\Input\ShortUrlIdentifierInput; use Shlinkio\Shlink\CLI\Util\ExitCode; use Shlinkio\Shlink\Core\Exception; use Shlinkio\Shlink\Core\ShortUrl\DeleteShortUrlServiceInterface; use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlIdentifier; use Symfony\Component\Console\Command\Command; -use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; @@ -21,9 +21,16 @@ class DeleteShortUrlCommand extends Command { public const NAME = 'short-url:delete'; + private readonly ShortUrlIdentifierInput $shortUrlIdentifierInput; + public function __construct(private readonly DeleteShortUrlServiceInterface $deleteShortUrlService) { parent::__construct(); + $this->shortUrlIdentifierInput = new ShortUrlIdentifierInput( + $this, + shortCodeDesc: 'The short code for the short URL to be deleted', + domainDesc: 'The domain if the short code does not belong to the default one', + ); } protected function configure(): void @@ -31,26 +38,19 @@ class DeleteShortUrlCommand extends Command $this ->setName(self::NAME) ->setDescription('Deletes a short URL') - ->addArgument('shortCode', InputArgument::REQUIRED, 'The short code for the short URL to be deleted') ->addOption( 'ignore-threshold', 'i', InputOption::VALUE_NONE, 'Ignores the safety visits threshold check, which could make short URLs with many visits to be ' . 'accidentally deleted', - ) - ->addOption( - 'domain', - 'd', - InputOption::VALUE_REQUIRED, - 'The domain if the short code does not belong to the default one', ); } protected function execute(InputInterface $input, OutputInterface $output): int { $io = new SymfonyStyle($input, $output); - $identifier = ShortUrlIdentifier::fromCli($input); + $identifier = $this->shortUrlIdentifierInput->toShortUrlIdentifier($input); $ignoreThreshold = $input->getOption('ignore-threshold'); try { diff --git a/module/CLI/src/Command/ShortUrl/DeleteShortUrlVisitsCommand.php b/module/CLI/src/Command/ShortUrl/DeleteShortUrlVisitsCommand.php index 5d122ea7..a720e12d 100644 --- a/module/CLI/src/Command/ShortUrl/DeleteShortUrlVisitsCommand.php +++ b/module/CLI/src/Command/ShortUrl/DeleteShortUrlVisitsCommand.php @@ -5,13 +5,11 @@ declare(strict_types=1); namespace Shlinkio\Shlink\CLI\Command\ShortUrl; use Shlinkio\Shlink\CLI\Command\Visit\AbstractDeleteVisitsCommand; +use Shlinkio\Shlink\CLI\Input\ShortUrlIdentifierInput; use Shlinkio\Shlink\CLI\Util\ExitCode; use Shlinkio\Shlink\Core\Exception\ShortUrlNotFoundException; -use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlIdentifier; use Shlinkio\Shlink\Core\ShortUrl\ShortUrlVisitsDeleterInterface; -use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Style\SymfonyStyle; use function sprintf; @@ -20,32 +18,28 @@ class DeleteShortUrlVisitsCommand extends AbstractDeleteVisitsCommand { public const NAME = 'short-url:visits-delete'; + private readonly ShortUrlIdentifierInput $shortUrlIdentifierInput; + public function __construct(private readonly ShortUrlVisitsDeleterInterface $deleter) { parent::__construct(); + $this->shortUrlIdentifierInput = new ShortUrlIdentifierInput( + $this, + shortCodeDesc: 'The short code for the short URL which visits will be deleted', + domainDesc: 'The domain if the short code does not belong to the default one', + ); } protected function configure(): void { $this ->setName(self::NAME) - ->setDescription('Deletes visits from a short URL') - ->addArgument( - 'shortCode', - InputArgument::REQUIRED, - 'The short code for the short URL which visits will be deleted', - ) - ->addOption( - 'domain', - 'd', - InputOption::VALUE_REQUIRED, - 'The domain if the short code does not belong to the default one', - ); + ->setDescription('Deletes visits from a short URL'); } protected function doExecute(InputInterface $input, SymfonyStyle $io): int { - $identifier = ShortUrlIdentifier::fromCli($input); + $identifier = $this->shortUrlIdentifierInput->toShortUrlIdentifier($input); try { $result = $this->deleter->deleteShortUrlVisits($identifier); $io->success(sprintf('Successfully deleted %s visits', $result->affectedItems)); diff --git a/module/CLI/src/Command/ShortUrl/GetShortUrlVisitsCommand.php b/module/CLI/src/Command/ShortUrl/GetShortUrlVisitsCommand.php index a6a4f31d..8a662209 100644 --- a/module/CLI/src/Command/ShortUrl/GetShortUrlVisitsCommand.php +++ b/module/CLI/src/Command/ShortUrl/GetShortUrlVisitsCommand.php @@ -5,14 +5,12 @@ declare(strict_types=1); namespace Shlinkio\Shlink\CLI\Command\ShortUrl; use Shlinkio\Shlink\CLI\Command\Visit\AbstractVisitsListCommand; +use Shlinkio\Shlink\CLI\Input\ShortUrlIdentifierInput; use Shlinkio\Shlink\Common\Paginator\Paginator; use Shlinkio\Shlink\Common\Util\DateRange; -use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlIdentifier; use Shlinkio\Shlink\Core\Visit\Entity\Visit; use Shlinkio\Shlink\Core\Visit\Model\VisitsParams; -use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; @@ -20,18 +18,23 @@ class GetShortUrlVisitsCommand extends AbstractVisitsListCommand { public const NAME = 'short-url:visits'; + private ShortUrlIdentifierInput $shortUrlIdentifierInput; + protected function configure(): void { $this ->setName(self::NAME) - ->setDescription('Returns the detailed visits information for provided short code') - ->addArgument('shortCode', InputArgument::REQUIRED, 'The short code which visits we want to get.') - ->addOption('domain', 'd', InputOption::VALUE_REQUIRED, 'The domain for the short code.'); + ->setDescription('Returns the detailed visits information for provided short code'); + $this->shortUrlIdentifierInput = new ShortUrlIdentifierInput( + $this, + shortCodeDesc: 'The short code which visits we want to get.', + domainDesc: 'The domain for the short code.', + ); } protected function interact(InputInterface $input, OutputInterface $output): void { - $shortCode = $input->getArgument('shortCode'); + $shortCode = $this->shortUrlIdentifierInput->shortCode($input); if (! empty($shortCode)) { return; } @@ -45,7 +48,7 @@ class GetShortUrlVisitsCommand extends AbstractVisitsListCommand protected function getVisitsPaginator(InputInterface $input, DateRange $dateRange): Paginator { - $identifier = ShortUrlIdentifier::fromCli($input); + $identifier = $this->shortUrlIdentifierInput->toShortUrlIdentifier($input); return $this->visitsHelper->visitsForShortUrl($identifier, new VisitsParams($dateRange)); } diff --git a/module/CLI/src/Command/ShortUrl/ResolveUrlCommand.php b/module/CLI/src/Command/ShortUrl/ResolveUrlCommand.php index d41d292e..0a207b68 100644 --- a/module/CLI/src/Command/ShortUrl/ResolveUrlCommand.php +++ b/module/CLI/src/Command/ShortUrl/ResolveUrlCommand.php @@ -4,14 +4,12 @@ declare(strict_types=1); namespace Shlinkio\Shlink\CLI\Command\ShortUrl; +use Shlinkio\Shlink\CLI\Input\ShortUrlIdentifierInput; use Shlinkio\Shlink\CLI\Util\ExitCode; use Shlinkio\Shlink\Core\Exception\ShortUrlNotFoundException; -use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlIdentifier; use Shlinkio\Shlink\Core\ShortUrl\ShortUrlResolverInterface; use Symfony\Component\Console\Command\Command; -use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; @@ -21,23 +19,28 @@ class ResolveUrlCommand extends Command { public const NAME = 'short-url:parse'; + private readonly ShortUrlIdentifierInput $shortUrlIdentifierInput; + public function __construct(private readonly ShortUrlResolverInterface $urlResolver) { parent::__construct(); + $this->shortUrlIdentifierInput = new ShortUrlIdentifierInput( + $this, + shortCodeDesc: 'The short code to parse', + domainDesc: 'The domain to which the short URL is attached.', + ); } protected function configure(): void { $this ->setName(self::NAME) - ->setDescription('Returns the long URL behind a short code') - ->addArgument('shortCode', InputArgument::REQUIRED, 'The short code to parse') - ->addOption('domain', 'd', InputOption::VALUE_REQUIRED, 'The domain to which the short URL is attached.'); + ->setDescription('Returns the long URL behind a short code'); } protected function interact(InputInterface $input, OutputInterface $output): void { - $shortCode = $input->getArgument('shortCode'); + $shortCode = $this->shortUrlIdentifierInput->shortCode($input); if (! empty($shortCode)) { return; } @@ -54,7 +57,7 @@ class ResolveUrlCommand extends Command $io = new SymfonyStyle($input, $output); try { - $url = $this->urlResolver->resolveShortUrl(ShortUrlIdentifier::fromCli($input)); + $url = $this->urlResolver->resolveShortUrl($this->shortUrlIdentifierInput->toShortUrlIdentifier($input)); $output->writeln(sprintf('Long URL: %s', $url->getLongUrl())); return ExitCode::EXIT_SUCCESS; } catch (ShortUrlNotFoundException $e) { diff --git a/module/CLI/src/Input/EndDateOption.php b/module/CLI/src/Input/EndDateOption.php index 000a135e..8e6df28a 100644 --- a/module/CLI/src/Input/EndDateOption.php +++ b/module/CLI/src/Input/EndDateOption.php @@ -11,9 +11,9 @@ use Symfony\Component\Console\Output\OutputInterface; use function sprintf; -class EndDateOption +readonly final class EndDateOption { - private readonly DateOption $dateOption; + private DateOption $dateOption; public function __construct(Command $command, string $descriptionHint) { diff --git a/module/CLI/src/Input/ShortUrlIdentifierInput.php b/module/CLI/src/Input/ShortUrlIdentifierInput.php new file mode 100644 index 00000000..c07de779 --- /dev/null +++ b/module/CLI/src/Input/ShortUrlIdentifierInput.php @@ -0,0 +1,34 @@ +addArgument('shortCode', InputArgument::REQUIRED, $shortCodeDesc) + ->addOption('domain', 'd', InputOption::VALUE_REQUIRED, $domainDesc); + } + + public function shortCode(InputInterface $input): ?string + { + return $input->getArgument('shortCode'); + } + + public function toShortUrlIdentifier(InputInterface $input): ShortUrlIdentifier + { + $shortCode = $input->getArgument('shortCode'); + $domain = $input->getOption('domain'); + + return ShortUrlIdentifier::fromShortCodeAndDomain($shortCode, $domain); + } +} diff --git a/module/CLI/src/Input/StartDateOption.php b/module/CLI/src/Input/StartDateOption.php index 0954e82f..6a7857d7 100644 --- a/module/CLI/src/Input/StartDateOption.php +++ b/module/CLI/src/Input/StartDateOption.php @@ -11,9 +11,9 @@ use Symfony\Component\Console\Output\OutputInterface; use function sprintf; -class StartDateOption +readonly final class StartDateOption { - private readonly DateOption $dateOption; + private DateOption $dateOption; public function __construct(Command $command, string $descriptionHint) { diff --git a/module/Core/src/ShortUrl/Model/ShortUrlIdentifier.php b/module/Core/src/ShortUrl/Model/ShortUrlIdentifier.php index 7ec19df6..a7c2e2ff 100644 --- a/module/Core/src/ShortUrl/Model/ShortUrlIdentifier.php +++ b/module/Core/src/ShortUrl/Model/ShortUrlIdentifier.php @@ -6,7 +6,6 @@ namespace Shlinkio\Shlink\Core\ShortUrl\Model; use Psr\Http\Message\ServerRequestInterface; use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl; -use Symfony\Component\Console\Input\InputInterface; use function sprintf; @@ -32,18 +31,6 @@ final readonly class ShortUrlIdentifier return new self($shortCode, $domain); } - public static function fromCli(InputInterface $input): self - { - // Using getArguments and getOptions instead of getArgument(...) and getOption(...) because - // the later throw an exception if requested options are not defined - /** @var string $shortCode */ - $shortCode = $input->getArguments()['shortCode'] ?? ''; - /** @var string|null $domain */ - $domain = $input->getOptions()['domain'] ?? null; - - return new self($shortCode, $domain); - } - public static function fromShortUrl(ShortUrl $shortUrl): self { $domain = $shortUrl->getDomain();