%command.name% Send all visits created before 2024: %command.name% --until 2023-12-31 Send all visits created after a specific day: %command.name% --since 2022-03-27 Send all visits created during 2022: %command.name% --since 2022-01-01 --until 2022-12-31 HELP, )] class MatomoSendVisitsCommand extends Command implements VisitSendingProgressTrackerInterface { public const string NAME = 'integration:matomo:send-visits'; private readonly bool $matomoEnabled; private SymfonyStyle $io; public function __construct(MatomoOptions $matomoOptions, private readonly MatomoVisitSenderInterface $visitSender) { $this->matomoEnabled = $matomoOptions->enabled; parent::__construct(); } public function __invoke( SymfonyStyle $io, InputInterface $input, #[Option('Only visits created since this date, inclusively, will be sent to Matomo', shortcut: 's')] string|null $since = null, #[Option('Only visits created until this date, inclusively, will be sent to Matomo', shortcut: 'u')] string|null $until = null, ): int { $this->io = $io; if (! $this->matomoEnabled) { $this->io->warning('Matomo integration is not enabled in this Shlink instance'); return self::INVALID; } // TODO Validate provided date formats $dateRange = buildDateRange( startDate: normalizeOptionalDate($since), endDate: normalizeOptionalDate($until), ); if ($input->isInteractive()) { $this->io->warning([ 'You are about to send visits from this Shlink instance to Matomo', 'Resolved date range -> ' . dateRangeToHumanFriendly($dateRange), 'Shlink will not check for already sent visits, which could result in some duplications. Make sure ' . 'you have verified only visits in the right date range are going to be sent.', ]); if (! $this->io->confirm('Continue?', default: false)) { return self::INVALID; } } $result = $this->visitSender->sendVisitsInDateRange($dateRange, $this); match (true) { $result->hasFailures() && $result->hasSuccesses() => $this->io->warning( sprintf('%s visits sent to Matomo. %s failed.', $result->successfulVisits, $result->failedVisits), ), $result->hasFailures() => $this->io->error( sprintf('Failed to send %s visits to Matomo.', $result->failedVisits), ), $result->hasSuccesses() => $this->io->success( sprintf('%s visits sent to Matomo.', $result->successfulVisits), ), default => $this->io->info('There was no visits matching provided date range.'), }; return self::SUCCESS; } public function success(int $index): void { $this->io->write('.'); } public function error(int $index, Throwable $e): void { $this->io->write('E'); if ($this->io->isVerbose()) { $this->getApplication()?->renderThrowable($e, $this->io); } } }