Files
animethemes-server/app/GraphQL/Directives/SortCustomDirective.php
T

95 lines
3.2 KiB
PHP

<?php
declare(strict_types=1);
namespace App\GraphQL\Directives;
use App\Enums\GraphQL\SortType;
use App\Exceptions\GraphQL\ClientValidationException;
use App\GraphQL\Support\Sort\RandomSort;
use App\GraphQL\Support\Sort\Sort;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Illuminate\Database\Eloquent\Relations\Relation;
use Illuminate\Database\Query\Builder as QueryBuilder;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
use InvalidArgumentException;
use Nuwave\Lighthouse\Schema\Directives\BaseDirective;
use Nuwave\Lighthouse\Support\Contracts\ArgBuilderDirective;
class SortCustomDirective extends BaseDirective implements ArgBuilderDirective
{
final public const INPUT_COLUMN = 'column';
final public const INPUT_VALUE = 'value';
final public const INPUT_SORT_TYPE = 'sortType';
final public const INPUT_RELATION = 'relation';
public static function definition(): string
{
return /** @lang GraphQL */ <<<'GRAPHQL'
"""
Sort a result by the given argument.
"""
directive @sortCustom(columns: [SortByColumnInput!]) on ARGUMENT_DEFINITION
"""
This input is used to coordinate the code to get the client argument and matches with the column sortable.
"""
input SortByColumnInput {
column: String
value: String!
sortType: Int! = 1
relation: String
}
GRAPHQL;
}
/**
* @param array<string, string> $enumCases
*
* @throws ClientValidationException
* @throws InvalidArgumentException
*/
public function handleBuilder(QueryBuilder|EloquentBuilder|Relation $builder, $enumCases): QueryBuilder|EloquentBuilder|Relation
{
$sortByColumnInputArray = json_decode($this->directiveArgValue('columns'), true);
foreach ($enumCases as $enumCase) {
if ($enumCase === RandomSort::CASE) {
$builder->inRandomOrder();
continue;
}
$direction = Sort::resolveFromEnumCase($enumCase);
$sortByColumnValue = Str::remove('_DESC', $enumCase);
$sortByColumnInput = Arr::first($sortByColumnInputArray, fn ($sortByColumn) => $sortByColumn[self::INPUT_VALUE] === $sortByColumnValue);
$column = Arr::get($sortByColumnInput, self::INPUT_COLUMN);
$sortType = SortType::from(Arr::get($sortByColumnInput, self::INPUT_SORT_TYPE));
if ($sortType === SortType::ROOT) {
$builder->orderBy($column, $direction);
}
if ($sortType === SortType::AGGREGATE) {
$relation = Arr::get($sortByColumnInput, self::INPUT_RELATION);
if ($relation === null) {
throw new InvalidArgumentException("The 'relation' argument is required for the @{$this->name()} directive with aggregate sort type.");
}
$builder->withAggregate([
"$relation as {$relation}_value" => function ($query) use ($direction) {
$query->orderBy('value', $direction);
},
], 'value');
$builder->orderBy("{$relation}_value", $direction);
}
}
return $builder;
}
}