authorizeResource(UrlStats::class); } /** @var array Colors used in charts */ private $colors = []; /** * Display a resource. * * @param \App\Http\Requests\Bo\StatsRequest $request * @return \Illuminate\Contracts\View\View */ public function index(StatsRequest $request): \Illuminate\Contracts\View\View { /** @var \App\Models\User $user */ $user = auth('backend')->user(); $brandModels = $user->getRelatedBrands(); // Check if it's a post request or not to get data. if ($request->post()) { $requestValidated = $request->validated(); $start = $requestValidated['start'] ? Carbon::parse($requestValidated['start']) : null; $end = $requestValidated['end'] ? Carbon::parse($requestValidated['end']) : null; $chart = $this->getChart($requestValidated); } return view('bo.pages.stats', [ 'brandModels' => $brandModels, 'chart' => $chart ?? null, 'start' => $start ?? null, 'end' => $end ?? null, ]); } /** * Get a chart from ChartJs. * * @param array $requestValidated * @return \IcehouseVentures\LaravelChartjs\Builder */ private function getChart(array $requestValidated): \IcehouseVentures\LaravelChartjs\Builder { // Get PeriodEnum from Request. Default = PeriodEnum::day. $periodEnum = PeriodEnum::tryFrom($requestValidated['period']) ?? PeriodEnum::day; $periodInterval = $this->getDateInterval( $requestValidated['start'], $requestValidated['end'], $periodEnum, ); // Get all labels contained in the interval period. $labels = []; foreach ($periodInterval as $date) { $labels[] = $this->getChartLabels($date, $periodEnum); } /** * Get data from an entity : * - total of all urls from a brand * - total of specific url from a brand * - total of all urls for each brand */ $datasets = match (true) { !$requestValidated['brand'] => $this->buildDatasets( Brand::all(), $periodInterval, $periodEnum, fn($brand, $date, $period) => $this->getCallsSum($brand, $date, $period), fn($brand) => $brand->name ), !$requestValidated['url'] => $this->buildDatasets( Brand::find($requestValidated['brand'])->urls, $periodInterval, $periodEnum, fn($url, $date, $period) => $this->getCallsSum($url, $date, $period), fn($url) => $url->value ), default => $this->buildDatasets( [Url::find($requestValidated['url'])], $periodInterval, $periodEnum, fn($url, $date, $period) => $this->getCallsSum($url, $date, $period), fn($url) => $url->value ), }; //end match // Get the type of graph when there are several data or only one. $chartType = count(Arr::first($datasets)['data'] ?? []) > 1 ? 'line' : 'bar'; return (new ChartJsHelper()) ->name('stats') ->type($chartType) ->labels($labels) ->datasets($datasets); } /** * Get data from an entity. * * @param iterable $entities * @param \Carbon\CarbonPeriod $periodInterval * @param \App\Enums\PeriodEnum $period * @param callable $getStats * @param callable $getLabel * @return array */ private function buildDatasets( iterable $entities, CarbonPeriod $periodInterval, PeriodEnum $period, callable $getStats, callable $getLabel ): array { $datasets = []; foreach ($entities as $entity) { $data = []; foreach ($periodInterval as $date) { $data[] = $getStats($entity, $date, $period); } $color = $this->getUniqueColor(); $datasets[] = [ 'label' => $getLabel($entity), 'backgroundColor' => $color, 'borderColor' => $color, 'pointBorderColor' => $color, 'pointBackgroundColor' => $color, 'data' => $data, 'lineTension' => 0.4, ]; } return $datasets; } /** * Get the sum of all calls. * * @param \Illuminate\Database\Eloquent\Model $entity * @param \Carbon\Carbon $date * @param \App\Enums\PeriodEnum $period * @return integer */ private function getCallsSum(Model $entity, Carbon $date, PeriodEnum $period): int { $urlKeys = method_exists($entity, 'urls') && $entity->urls()->exists() ? $entity->urls()->pluck('key') : collect([$entity->key]); return UrlStats::query() ->when( $period === PeriodEnum::day, fn($query) => $query->whereDate('date', $date) ) ->when( $period === PeriodEnum::week, fn($query) => $query->whereBetween('date', [ $date->copy()->startOfWeek(), $date->copy()->endOfWeek() ]) ) ->when( $period === PeriodEnum::month, fn($query) => $query->whereBetween('date', [ $date->copy()->startOfMonth(), $date->copy()->endOfMonth() ]) ) ->whereIn('url_key', $urlKeys) ->sum('calls'); } /** * Get the CarbonPeriod for the given period. * * @param string $start * @param string $end * @param \App\Enums\PeriodEnum $period * @return \Carbon\CarbonPeriod */ private function getDateInterval(string $start, string $end, PeriodEnum $period): \Carbon\CarbonPeriod { $startDate = Carbon::parse($start); $endDate = Carbon::parse($end); /** * Set config for each enum names. * methods: specific methods needed. * interval: specific interval defined. */ $unitMap = [ PeriodEnum::day->value => [ 'methods' => null, 'interval' => null ], PeriodEnum::week->value => [ 'methods' => ['startOfWeek', 'endOfWeek'], 'interval' => '1 week' ], PeriodEnum::month->value => [ 'methods' => ['startOfMonth', 'endOfMonth'], 'interval' => '1 month' ], ]; // Check if config exist for the given period, get the day config by default. $config = $unitMap[$period->value] ?? $unitMap[PeriodEnum::day->value]; // Execute configuration methods where available. if ($config['methods']) { [$startMethod, $endMethod] = $config['methods']; $startDate = $startDate->$startMethod(); $endDate = $endDate->$endMethod(); } return CarbonPeriod::create($startDate, $config['interval'], $endDate); } /** * Get labels for charts. * * @param \Carbon\Carbon $date * @param \App\Enums\PeriodEnum $period * @return array */ private function getChartLabels(Carbon $date, PeriodEnum $period): array { // Match given period with enum names. return match ($period) { PeriodEnum::week => array_unique([sprintf( 'Du %s au %s', $date->startOfWeek()->format('Y-m-d'), $date->endOfWeek()->format('Y-m-d') )]), PeriodEnum::month => array_unique([ Str::ucfirst($date->monthName) ]), default => array_unique([$date->format('d-m-Y')]), }; } /** * Get unique color. * * @return string */ private function getUniqueColor(): string { do { $color = RandomColor::one(); // Check if color is already used. } while (in_array($color, $this->colors)); $this->colors[] = $color; return $color; } }