getStartDateOption($input, $output); $endDate = $this->getEndDateOption($input, $output); $paginator = $this->getVisitsPaginator($input, buildDateRange($startDate, $endDate)); [$rows, $headers] = $this->resolveRowsAndHeaders($paginator); ShlinkTable::default($output)->render($headers, $rows); return ExitCodes::EXIT_SUCCESS; } private function resolveRowsAndHeaders(Paginator $paginator): array { $extraKeys = []; $rows = map($paginator->getCurrentPageResults(), function (Visit $visit) use (&$extraKeys) { $extraFields = $this->mapExtraFields($visit); $extraKeys = array_keys($extraFields); $rowData = [ ...$visit->jsonSerialize(), 'country' => $visit->getVisitLocation()?->getCountryName() ?? 'Unknown', 'city' => $visit->getVisitLocation()?->getCityName() ?? 'Unknown', ...$extraFields, ]; return select_keys($rowData, ['referer', 'date', 'userAgent', 'country', 'city', ...$extraKeys]); }); $extra = map($extraKeys, camelCaseToHumanFriendly(...)); return [ $rows, ['Referer', 'Date', 'User agent', 'Country', 'City', ...$extra], ]; } abstract protected function getVisitsPaginator(InputInterface $input, DateRange $dateRange): Paginator; /** * @return array */ abstract protected function mapExtraFields(Visit $visit): array; }