mirror of
https://github.com/AnimeThemes/animethemes-server.git
synced 2026-07-11 01:24:46 +02:00
130 lines
4.3 KiB
PHP
130 lines
4.3 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Http\Api\Field\Aggregate;
|
|
|
|
use App\Concerns\Actions\Http\Api\FiltersModels;
|
|
use App\Contracts\Http\Api\Field\FilterableField;
|
|
use App\Contracts\Http\Api\Field\RenderableField;
|
|
use App\Contracts\Http\Api\Field\SortableField;
|
|
use App\Enums\Http\Api\Field\AggregateFunction;
|
|
use App\Enums\Http\Api\QualifyColumn;
|
|
use App\Http\Api\Criteria\Field\Criteria;
|
|
use App\Http\Api\Criteria\Sort\Criteria as SortCriteria;
|
|
use App\Http\Api\Field\Field;
|
|
use App\Http\Api\Query\Query;
|
|
use App\Http\Api\Schema\Schema;
|
|
use App\Http\Api\Scope\ScopeParser;
|
|
use App\Http\Api\Sort\Sort;
|
|
use Illuminate\Database\Eloquent\Builder;
|
|
use Illuminate\Database\Eloquent\Model;
|
|
use Illuminate\Support\Str;
|
|
|
|
abstract class AggregateField extends Field implements FilterableField, RenderableField, SortableField
|
|
{
|
|
use FiltersModels;
|
|
|
|
public function __construct(
|
|
Schema $schema,
|
|
public readonly string $relation,
|
|
protected readonly AggregateFunction $function,
|
|
protected readonly string $aggregateColumn
|
|
) {
|
|
parent::__construct($schema, $this->alias());
|
|
}
|
|
|
|
public function shouldRender(Query $query): bool
|
|
{
|
|
$criteria = $query->getFieldCriteria($this->schema->type());
|
|
|
|
return $criteria instanceof Criteria && $criteria->isAllowedField($this->getKey());
|
|
}
|
|
|
|
public function render(Model $model): mixed
|
|
{
|
|
return $model->getAttribute($this->alias());
|
|
}
|
|
|
|
public function getSort(): Sort
|
|
{
|
|
return new Sort(key: $this->alias(), qualifyColumn: QualifyColumn::NO);
|
|
}
|
|
|
|
/**
|
|
* Determine if the aggregate value should be included in the select clause of our query.
|
|
*/
|
|
public function shouldAggregate(Query $query): bool
|
|
{
|
|
// Select aggregate if explicitly included in sparse fieldsets
|
|
$fieldCriteria = $query->getFieldCriteria($this->schema->type());
|
|
if ($fieldCriteria instanceof Criteria && $fieldCriteria->isAllowedField($this->getKey())) {
|
|
return true;
|
|
}
|
|
|
|
$scope = ScopeParser::parse($this->schema->type());
|
|
|
|
// Select aggregate if filtering on the aggregate value
|
|
$filter = $this->getFilter();
|
|
foreach ($query->getFilterCriteria() as $criteria) {
|
|
if ($criteria->shouldFilter($filter, $scope)) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// Select aggregate if sorting on the aggregate value
|
|
$sort = $this->getSort();
|
|
|
|
return array_any($query->getSortCriteria(), fn (SortCriteria $sortCriterion): bool => $sortCriterion->shouldSort($sort, $scope));
|
|
}
|
|
|
|
/**
|
|
* Load the aggregate field value for the model.
|
|
*/
|
|
public function load(Query $query, Model $model): Model
|
|
{
|
|
$constrainedRelation = [];
|
|
|
|
$relationSchema = $this->schema->relation($this->relation);
|
|
$constrainedRelation[$this->relation] = function (Builder $relationBuilder) use ($query, $relationSchema): void {
|
|
if ($relationSchema instanceof Schema) {
|
|
// TODO: distinguish scope from type
|
|
$scope = ScopeParser::parse($this->relation);
|
|
$this->filter($relationBuilder, $query, $relationSchema, $scope);
|
|
}
|
|
};
|
|
|
|
return $model->loadAggregate($constrainedRelation, $this->aggregateColumn, $this->function->value);
|
|
}
|
|
|
|
/**
|
|
* Eager load the aggregate value for the query builder.
|
|
*/
|
|
public function with(Query $query, Builder $builder): Builder
|
|
{
|
|
$constrainedRelation = [];
|
|
|
|
$relationSchema = $this->schema->relation($this->relation);
|
|
$constrainedRelation[$this->relation] = function (Builder $relationBuilder) use ($query, $relationSchema): void {
|
|
if ($relationSchema instanceof Schema) {
|
|
// TODO: distinguish scope from type
|
|
$scope = ScopeParser::parse($this->relation);
|
|
$this->filter($relationBuilder, $query, $relationSchema, $scope);
|
|
}
|
|
};
|
|
|
|
return $builder->withAggregate($constrainedRelation, $this->aggregateColumn, $this->function->value);
|
|
}
|
|
|
|
/**
|
|
* Format the aggregate value to its sub-select alias / model attribute.
|
|
*/
|
|
public function alias(): string
|
|
{
|
|
return Str::of($this->relation)
|
|
->append('_')
|
|
->append($this->function->value)
|
|
->__toString();
|
|
}
|
|
}
|