276 lines
9.1 KiB
PHP
276 lines
9.1 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\Bo;
|
|
|
|
use App\Enums\PeriodEnum;
|
|
use App\Http\Controllers\Controller;
|
|
use App\Http\Requests\Bo\StatsRequest;
|
|
use App\Lib\Helpers\ChartJsHelper;
|
|
use App\Models\Brand;
|
|
use App\Models\Url;
|
|
use App\Models\UrlStats;
|
|
use Carbon\Carbon;
|
|
use Carbon\CarbonPeriod;
|
|
use Illuminate\Support\Arr;
|
|
use Illuminate\Support\Str;
|
|
use Colors\RandomColor;
|
|
use Illuminate\Database\Eloquent\Model;
|
|
|
|
class StatsController extends Controller
|
|
{
|
|
/** @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
|
|
{
|
|
$periodInterval = $this->getDateInterval(
|
|
$requestValidated['start'],
|
|
$requestValidated['end'],
|
|
$requestValidated['period'],
|
|
);
|
|
|
|
// Get all labels contained in the interval period.
|
|
$labels = [];
|
|
foreach ($periodInterval as $date) {
|
|
$labels[] = $this->getChartLabels($date, $requestValidated['period']);
|
|
}
|
|
|
|
/**
|
|
* 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,
|
|
$requestValidated['period'],
|
|
fn($brand, $date, $period) => $this->getCallsSum($brand, $date, $period),
|
|
fn($brand) => $brand->name
|
|
),
|
|
!$requestValidated['url'] => $this->buildDatasets(
|
|
Brand::find($requestValidated['brand'])->urls,
|
|
$periodInterval,
|
|
$requestValidated['period'],
|
|
fn($url, $date, $period) => $this->getCallsSum($url, $date, $period),
|
|
fn($url) => $url->value
|
|
),
|
|
default => $this->buildDatasets(
|
|
[Url::find($requestValidated['url'])],
|
|
$periodInterval,
|
|
$requestValidated['period'],
|
|
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 string $period
|
|
* @param callable $getStats
|
|
* @param callable $getLabel
|
|
* @return array
|
|
*/
|
|
private function buildDatasets(
|
|
iterable $entities,
|
|
CarbonPeriod $periodInterval,
|
|
string $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 string $period
|
|
* @return integer
|
|
*/
|
|
private function getCallsSum(Model $entity, Carbon $date, string $period): int
|
|
{
|
|
$urlIds = method_exists($entity, 'urls') && $entity->urls()->exists()
|
|
? $entity->urls()->pluck('id')
|
|
: collect([$entity->getKey()]);
|
|
|
|
return UrlStats::query()
|
|
->when(
|
|
$period === PeriodEnum::day->name,
|
|
fn($query) => $query->whereDate('date', $date)
|
|
)
|
|
->when(
|
|
$period === PeriodEnum::week->name,
|
|
fn($query) => $query->whereBetween('date', [$date->copy()->startOfWeek(), $date->copy()->endOfWeek()])
|
|
)
|
|
->when(
|
|
$period === PeriodEnum::month->name,
|
|
fn($query) => $query->whereBetween('date', [$date->copy()->startOfMonth(), $date->copy()->endOfMonth()])
|
|
)
|
|
->whereIn('url_id', $urlIds)
|
|
->sum('calls');
|
|
}
|
|
|
|
/**
|
|
* Get the CarbonPeriod for the given period.
|
|
*
|
|
* @param string $start
|
|
* @param string $end
|
|
* @param string $period Can be day|week|month.
|
|
* @return \Carbon\CarbonPeriod
|
|
*/
|
|
private function getDateInterval(string $start, string $end, string $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 = [
|
|
$this->getEnumName(PeriodEnum::day) => [
|
|
'methods' => null,
|
|
'interval' => null
|
|
],
|
|
$this->getEnumName(PeriodEnum::week) => [
|
|
'methods' => ['startOfWeek', 'endOfWeek'],
|
|
'interval' => '1 week'
|
|
],
|
|
$this->getEnumName(PeriodEnum::month) => [
|
|
'methods' => ['startOfMonth', 'endOfMonth'],
|
|
'interval' => '1 month'
|
|
],
|
|
];
|
|
|
|
// Check if config exist for the given period, get the day config by default.
|
|
$config = $unitMap[$period] ?? $unitMap[$this->getEnumName(PeriodEnum::day)];
|
|
|
|
// 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);
|
|
}
|
|
|
|
/**
|
|
* Hack to get the name of a php 7.3 enum.
|
|
*
|
|
* @param \App\Enums\PeriodEnum $enum
|
|
* @return string
|
|
*/
|
|
private function getEnumName(PeriodEnum $enum): string
|
|
{
|
|
/** @var array $enumFlip */
|
|
$enumFlip = array_flip(PeriodEnum::toValues());
|
|
return $enumFlip[$enum->value];
|
|
}
|
|
|
|
/**
|
|
* Get labels for charts.
|
|
*
|
|
* @param \Carbon\Carbon $date
|
|
* @param string $period Can be day|week|month.
|
|
* @return array
|
|
*/
|
|
private function getChartLabels(Carbon $date, string $period): array
|
|
{
|
|
// Match given period with enum names.
|
|
return match ($period) {
|
|
$this->getEnumName(PeriodEnum::week) => array_unique([sprintf(
|
|
'Du %s au %s',
|
|
$date->startOfWeek()->format('Y-m-d'),
|
|
$date->endOfWeek()->format('Y-m-d')
|
|
)]),
|
|
$this->getEnumName(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;
|
|
}
|
|
}
|