refactor(graphql): changed how filter directives are build (#827)

This commit is contained in:
Kyrch
2025-06-04 15:15:21 -03:00
committed by GitHub
parent f9c932fd30
commit 6fc78d3709
30 changed files with 192 additions and 297 deletions
+5 -2
View File
@@ -6,6 +6,7 @@ namespace App\Concerns\GraphQL;
use App\Contracts\GraphQL\FilterableField;
use App\GraphQL\Definition\Fields\Field;
use App\GraphQL\Definition\Directives\Filters\FilterDirective;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
@@ -24,7 +25,7 @@ trait ResolvesArguments
{
if (filled($arguments)) {
return Str::of('(')
->append(implode("\n ", Arr::flatten($arguments)))
->append(implode("\n", Arr::flatten($arguments)))
->append(')')
->toString();
}
@@ -43,7 +44,9 @@ trait ResolvesArguments
return collect($fields)
->map(function (Field $field) {
if ($field instanceof FilterableField) {
return $field->getFilter()->toArray();
return collect($field->filterDirectives())
->map(fn (FilterDirective $directive) => $directive->toString())
->toArray();
}
})
->flatten()
+3 -3
View File
@@ -10,16 +10,16 @@ namespace App\Concerns\GraphQL;
trait ResolvesDirectives
{
/**
* Resolve the directives into a string format for GraphQL.
* Convert the array of directives into a string format for GraphQL.
*
* @param array $directives
* @param array<string, array> $directives
* @return string
*/
public function resolveDirectives(array $directives): string
{
return collect($directives)
->map(function ($args, $directive) {
if (is_array($args) && filled($args)) {
if (filled($args)) {
$argsString = collect($args)
->map(fn ($value, $key) => sprintf('%s: %s', $key, json_encode($value)))
->implode(', ');
+4 -4
View File
@@ -4,7 +4,7 @@ declare(strict_types=1);
namespace App\Contracts\GraphQL;
use App\GraphQL\Definition\Filters\Filter;
use App\GraphQL\Definition\Directives\Filters\FilterDirective;
/**
* Interface FilterableField.
@@ -12,9 +12,9 @@ use App\GraphQL\Definition\Filters\Filter;
interface FilterableField
{
/**
* Get the filter for this field.
* The directives available for this field.
*
* @return Filter
* @return array<int, FilterDirective>
*/
public function getFilter(): Filter;
public function filterDirectives(): array;
}
+25 -14
View File
@@ -19,24 +19,35 @@ enum RelationType
/**
* Get the directive of the relation type.
*
* @param array $parameters
* @return string
* @param array<string, mixed> $parameters
* @return array<string, array>
*/
public function getDirective(array $parameters): string
public function getDirective(array $parameters): array
{
$args = collect($parameters)
->filter(fn ($value) => filled($value))
->map(fn ($value, $key) => "$key: $value")
->values()
->implode(', ');
$parameters = array_filter($parameters, fn ($value) => filled($value));
return match ($this) {
RelationType::BELONGS_TO => "@belongsTo($args)",
RelationType::BELONGS_TO_MANY => "@belongsToMany(type: CONNECTION, $args)",
RelationType::HAS_MANY => "@hasMany($args)",
RelationType::HAS_ONE => "@hasOne($args)",
RelationType::MORPH_MANY => "@morphMany($args)",
RelationType::MORPH_TO => "@morphTo($args)",
RelationType::BELONGS_TO => [
'belongsTo' => $parameters,
],
RelationType::BELONGS_TO_MANY => [
'belongsToMany' => [
'type' => 'CONNECTION',
...$parameters,
],
],
RelationType::HAS_MANY => [
'hasMany' => $parameters,
],
RelationType::HAS_ONE => [
'hasOne' => $parameters,
],
RelationType::MORPH_MANY => [
'morphMany' => $parameters,
],
RelationType::MORPH_TO => [
'morphTo' => $parameters,
],
};
}
}
@@ -2,7 +2,7 @@
declare(strict_types=1);
namespace App\GraphQL\Definition\Filters\Directives;
namespace App\GraphQL\Definition\Directives\Filters;
use Illuminate\Support\Str;
@@ -2,7 +2,7 @@
declare(strict_types=1);
namespace App\GraphQL\Definition\Filters\Directives;
namespace App\GraphQL\Definition\Directives\Filters;
use App\GraphQL\Definition\Fields\Field;
use GraphQL\Type\Definition\Type;
@@ -2,7 +2,7 @@
declare(strict_types=1);
namespace App\GraphQL\Definition\Filters\Directives;
namespace App\GraphQL\Definition\Directives\Filters;
use Illuminate\Support\Str;
@@ -2,7 +2,7 @@
declare(strict_types=1);
namespace App\GraphQL\Definition\Filters\Directives;
namespace App\GraphQL\Definition\Directives\Filters;
use GraphQL\Type\Definition\Type;
use Illuminate\Support\Str;
@@ -2,7 +2,7 @@
declare(strict_types=1);
namespace App\GraphQL\Definition\Filters\Directives;
namespace App\GraphQL\Definition\Directives\Filters;
use Illuminate\Support\Str;
@@ -2,7 +2,7 @@
declare(strict_types=1);
namespace App\GraphQL\Definition\Filters\Directives;
namespace App\GraphQL\Definition\Directives\Filters;
use Illuminate\Support\Str;
@@ -2,7 +2,7 @@
declare(strict_types=1);
namespace App\GraphQL\Definition\Filters\Directives;
namespace App\GraphQL\Definition\Directives\Filters;
use GraphQL\Type\Definition\Type;
use Illuminate\Support\Str;
@@ -5,8 +5,8 @@ declare(strict_types=1);
namespace App\GraphQL\Definition\Fields;
use App\Contracts\GraphQL\FilterableField;
use App\GraphQL\Definition\Filters\BooleanFilter;
use App\GraphQL\Definition\Filters\Filter;
use App\GraphQL\Definition\Directives\Filters\EqFilterDirective;
use App\GraphQL\Definition\Directives\Filters\FilterDirective;
use GraphQL\Type\Definition\Type;
/**
@@ -25,12 +25,14 @@ abstract class BooleanField extends Field implements FilterableField
}
/**
* Get the filter for this field.
* The directives available for this filter.
*
* @return Filter
* @return array<int, FilterDirective>
*/
public function getFilter(): Filter
public function filterDirectives(): array
{
return new BooleanFilter($this);
return [
new EqFilterDirective($this, $this->type()),
];
}
}
@@ -5,8 +5,9 @@ declare(strict_types=1);
namespace App\GraphQL\Definition\Fields;
use App\Contracts\GraphQL\FilterableField;
use App\GraphQL\Definition\Filters\DateTimeTzFilter;
use App\GraphQL\Definition\Filters\Filter;
use App\GraphQL\Definition\Directives\Filters\FilterDirective;
use App\GraphQL\Definition\Directives\Filters\GreaterFilterDirective;
use App\GraphQL\Definition\Directives\Filters\LesserFilterDirective;
use GraphQL\Type\Definition\Type;
use Nuwave\Lighthouse\Schema\TypeRegistry;
@@ -26,12 +27,15 @@ abstract class DateTimeTzField extends Field implements FilterableField
}
/**
* Get the filter for this field.
* The directives available for this filter.
*
* @return Filter
* @return array<int, FilterDirective>
*/
public function getFilter(): Filter
public function filterDirectives(): array
{
return new DateTimeTzFilter($this);
return [
new LesserFilterDirective($this, $this->type()),
new GreaterFilterDirective($this, $this->type()),
];
}
}
+10 -6
View File
@@ -5,8 +5,9 @@ declare(strict_types=1);
namespace App\GraphQL\Definition\Fields;
use App\Contracts\GraphQL\FilterableField;
use App\GraphQL\Definition\Filters\EnumFilter;
use App\GraphQL\Definition\Filters\Filter;
use App\GraphQL\Definition\Directives\Filters\FilterDirective;
use App\GraphQL\Definition\Directives\Filters\InFilterDirective;
use App\GraphQL\Definition\Directives\Filters\NotInFilterDirective;
use GraphQL\Type\Definition\Type;
use Illuminate\Support\Arr;
use Nuwave\Lighthouse\Schema\TypeRegistry;
@@ -67,12 +68,15 @@ abstract class EnumField extends Field implements FilterableField
}
/**
* Get the filter for this field.
* The directives available for this filter.
*
* @return Filter
* @return array<int, FilterDirective>
*/
public function getFilter(): Filter
public function filterDirectives(): array
{
return new EnumFilter($this);
return [
new InFilterDirective($this, $this->type()),
new NotInFilterDirective($this, $this->type()),
];
}
}
+14 -6
View File
@@ -5,8 +5,11 @@ declare(strict_types=1);
namespace App\GraphQL\Definition\Fields;
use App\Contracts\GraphQL\FilterableField;
use App\GraphQL\Definition\Filters\Filter;
use App\GraphQL\Definition\Filters\FloatFilter;
use App\GraphQL\Definition\Directives\Filters\FilterDirective;
use App\GraphQL\Definition\Directives\Filters\GreaterFilterDirective;
use App\GraphQL\Definition\Directives\Filters\InFilterDirective;
use App\GraphQL\Definition\Directives\Filters\LesserFilterDirective;
use App\GraphQL\Definition\Directives\Filters\NotInFilterDirective;
use GraphQL\Type\Definition\Type;
/**
@@ -25,12 +28,17 @@ abstract class FloatField extends Field implements FilterableField
}
/**
* Get the filter for this field.
* The directives available for this filter.
*
* @return Filter
* @return array<int, FilterDirective>
*/
public function getFilter(): Filter
public function filterDirectives(): array
{
return new FloatFilter($this);
return [
new InFilterDirective($this, $this->type()),
new NotInFilterDirective($this, $this->type()),
new LesserFilterDirective($this, $this->type()),
new GreaterFilterDirective($this, $this->type()),
];
}
}
+14 -6
View File
@@ -5,8 +5,11 @@ declare(strict_types=1);
namespace App\GraphQL\Definition\Fields;
use App\Contracts\GraphQL\FilterableField;
use App\GraphQL\Definition\Filters\Filter;
use App\GraphQL\Definition\Filters\IntFilter;
use App\GraphQL\Definition\Directives\Filters\FilterDirective;
use App\GraphQL\Definition\Directives\Filters\GreaterFilterDirective;
use App\GraphQL\Definition\Directives\Filters\InFilterDirective;
use App\GraphQL\Definition\Directives\Filters\LesserFilterDirective;
use App\GraphQL\Definition\Directives\Filters\NotInFilterDirective;
use GraphQL\Type\Definition\Type;
/**
@@ -25,12 +28,17 @@ abstract class IntField extends Field implements FilterableField
}
/**
* Get the filter for this field.
* The directives available for this filter.
*
* @return Filter
* @return array<int, FilterDirective>
*/
public function getFilter(): Filter
public function filterDirectives(): array
{
return new IntFilter($this);
return [
new InFilterDirective($this, $this->type()),
new NotInFilterDirective($this, $this->type()),
new LesserFilterDirective($this, $this->type()),
new GreaterFilterDirective($this, $this->type()),
];
}
}
+10 -6
View File
@@ -5,8 +5,9 @@ declare(strict_types=1);
namespace App\GraphQL\Definition\Fields;
use App\Contracts\GraphQL\FilterableField;
use App\GraphQL\Definition\Filters\Filter;
use App\GraphQL\Definition\Filters\StringFilter;
use App\GraphQL\Definition\Directives\Filters\EqFilterDirective;
use App\GraphQL\Definition\Directives\Filters\FilterDirective;
use App\GraphQL\Definition\Directives\Filters\LikeFilterDirective;
use GraphQL\Type\Definition\Type;
/**
@@ -25,12 +26,15 @@ abstract class StringField extends Field implements FilterableField
}
/**
* Get the filter for this field.
* The directives available for this filter.
*
* @return Filter
* @return array<int, FilterDirective>
*/
public function getFilter(): Filter
public function filterDirectives(): array
{
return new StringFilter($this);
return [
new EqFilterDirective($this, $this->type()),
new LikeFilterDirective($this, $this->type()),
];
}
}
@@ -17,7 +17,7 @@ class AnimeThemeSequenceField extends IntField
*/
public function __construct()
{
parent::__construct(AnimeTheme::ATTRIBUTE_TYPE);
parent::__construct(AnimeTheme::ATTRIBUTE_SEQUENCE);
}
/**
@@ -1,27 +0,0 @@
<?php
declare(strict_types=1);
namespace App\GraphQL\Definition\Filters;
use App\GraphQL\Definition\Filters\Directives\EqFilterDirective;
use App\GraphQL\Definition\Filters\Directives\FilterDirective;
use GraphQL\Type\Definition\Type;
/**
* Class BooleanFilter.
*/
class BooleanFilter extends Filter
{
/**
* The directives available for this filter.
*
* @return array<int, FilterDirective>
*/
protected function directives(): array
{
return [
new EqFilterDirective($this->field, Type::boolean()),
];
}
}
@@ -1,31 +0,0 @@
<?php
declare(strict_types=1);
namespace App\GraphQL\Definition\Filters;
use App\GraphQL\Definition\Filters\Directives\FilterDirective;
use App\GraphQL\Definition\Filters\Directives\GreaterFilterDirective;
use App\GraphQL\Definition\Filters\Directives\LesserFilterDirective;
use Nuwave\Lighthouse\Schema\TypeRegistry;
/**
* Class DateTimeTzFilter.
*/
class DateTimeTzFilter extends Filter
{
/**
* The directives available for this filter.
*
* @return array<int, FilterDirective>
*/
protected function directives(): array
{
$type = app(TypeRegistry::class)->get('DateTimeTz');
return [
new LesserFilterDirective($this->field, $type),
new GreaterFilterDirective($this->field, $type),
];
}
}
@@ -1,28 +0,0 @@
<?php
declare(strict_types=1);
namespace App\GraphQL\Definition\Filters;
use App\GraphQL\Definition\Filters\Directives\FilterDirective;
use App\GraphQL\Definition\Filters\Directives\InFilterDirective;
use App\GraphQL\Definition\Filters\Directives\NotInFilterDirective;
/**
* Class EnumFilter.
*/
class EnumFilter extends Filter
{
/**
* The directives available for this filter.
*
* @return array<int, FilterDirective>
*/
protected function directives(): array
{
return [
new InFilterDirective($this->field, $this->field->getType()),
new NotInFilterDirective($this->field, $this->field->getType()),
];
}
}
-41
View File
@@ -1,41 +0,0 @@
<?php
declare(strict_types=1);
namespace App\GraphQL\Definition\Filters;
use App\GraphQL\Definition\Fields\Field;
use App\GraphQL\Definition\Filters\Directives\FilterDirective;
/**
* Class Filter.
*/
abstract class Filter
{
/**
* @param Field $field
*/
public function __construct(
protected Field $field,
) {
}
/**
* Get the arguments available for a field.
*
* @return array
*/
public function toArray(): array
{
return collect($this->directives())
->map(fn (FilterDirective $directive) => $directive->toString())
->toArray();
}
/**
* The directives available for this filter.
*
* @return array<int, FilterDirective>
*/
abstract protected function directives(): array;
}
@@ -1,33 +0,0 @@
<?php
declare(strict_types=1);
namespace App\GraphQL\Definition\Filters;
use App\GraphQL\Definition\Filters\Directives\FilterDirective;
use App\GraphQL\Definition\Filters\Directives\GreaterFilterDirective;
use App\GraphQL\Definition\Filters\Directives\InFilterDirective;
use App\GraphQL\Definition\Filters\Directives\LesserFilterDirective;
use App\GraphQL\Definition\Filters\Directives\NotInFilterDirective;
use GraphQL\Type\Definition\Type;
/**
* Class FloatFilter.
*/
class FloatFilter extends Filter
{
/**
* The directives available for this filter.
*
* @return array<int, FilterDirective>
*/
protected function directives(): array
{
return [
new InFilterDirective($this->field, Type::float()),
new NotInFilterDirective($this->field, Type::float()),
new LesserFilterDirective($this->field, Type::float()),
new GreaterFilterDirective($this->field, Type::float()),
];
}
}
@@ -1,33 +0,0 @@
<?php
declare(strict_types=1);
namespace App\GraphQL\Definition\Filters;
use App\GraphQL\Definition\Filters\Directives\FilterDirective;
use App\GraphQL\Definition\Filters\Directives\GreaterFilterDirective;
use App\GraphQL\Definition\Filters\Directives\InFilterDirective;
use App\GraphQL\Definition\Filters\Directives\LesserFilterDirective;
use App\GraphQL\Definition\Filters\Directives\NotInFilterDirective;
use GraphQL\Type\Definition\Type;
/**
* Class IntFilter.
*/
class IntFilter extends Filter
{
/**
* The directives available for this filter.
*
* @return array<int, FilterDirective>
*/
protected function directives(): array
{
return [
new InFilterDirective($this->field, Type::int()),
new NotInFilterDirective($this->field, Type::int()),
new LesserFilterDirective($this->field, Type::int()),
new GreaterFilterDirective($this->field, Type::int()),
];
}
}
@@ -1,29 +0,0 @@
<?php
declare(strict_types=1);
namespace App\GraphQL\Definition\Filters;
use App\GraphQL\Definition\Filters\Directives\FilterDirective;
use App\GraphQL\Definition\Filters\Directives\EqFilterDirective;
use App\GraphQL\Definition\Filters\Directives\LikeFilterDirective;
use GraphQL\Type\Definition\Type;
/**
* Class StringFilter.
*/
class StringFilter extends Filter
{
/**
* The directives available for this filter.
*
* @return array<int, FilterDirective>
*/
protected function directives(): array
{
return [
new EqFilterDirective($this->field, Type::string()),
new LikeFilterDirective($this->field, Type::string()),
];
}
}
+11 -1
View File
@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace App\GraphQL\Definition\Relations;
use App\Concerns\GraphQL\ResolvesDirectives;
use App\Enums\GraphQL\RelationType;
use GraphQL\Type\Definition\Type;
use Illuminate\Support\Str;
@@ -13,6 +14,8 @@ use Illuminate\Support\Str;
*/
abstract class Relation
{
use ResolvesDirectives;
/**
* @param Type $type
* @param string $relationName
@@ -36,11 +39,18 @@ abstract class Relation
*/
public function toString(): string
{
$directives = $this->resolveDirectives(
$this->relation()->getDirective([
'relation' => $this->relationName,
'edgeType' => $this->edgeType,
])
);
return Str::of($this->field ?? $this->relationName)
->append(': ')
->append($this->type()->toString())
->append(' ')
->append($this->relation()->getDirective(['relation' => $this->relationName, 'edgeType' => $this->edgeType]))
->append($directives)
->toString();
}
+4 -4
View File
@@ -48,17 +48,17 @@ abstract class BaseType extends ObjectType
return Str::of('"')
->append($field->description())
->append('"')
->append("\n ")
->append("\n")
->append($field->toString());
})
->implode("\n ");
->implode("\n");
$relations = collect($this->relations())
->map(fn (Relation $relation) => $relation->toString())
->implode("\n ");
->implode("\n");
$allFields = Str::of($fields)
->append("\n ")
->append("\n")
->append($relations)
->toString();
@@ -0,0 +1,48 @@
<?php
declare(strict_types=1);
namespace App\GraphQL\Directives;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Str;
use Laravel\Pennant\Middleware\EnsureFeaturesAreActive;
use Nuwave\Lighthouse\Schema\Directives\BaseDirective;
use Nuwave\Lighthouse\Schema\Values\FieldValue;
use Nuwave\Lighthouse\Support\Contracts\FieldMiddleware;
/**
* Class FeatureEnabledDirective.
*/
class FeatureEnabledDirective extends BaseDirective implements FieldMiddleware
{
/**
* Define the directive.
*
* @return string
*/
public static function definition(): string
{
return /** @lang GraphQL */ <<<'GRAPHQL'
directive @featureEnabled(class: String!) on FIELD_DEFINITION
GRAPHQL;
}
/**
* Wrap around the final field resolver.
*
* @param FieldValue $fieldValue
* @return void
*/
public function handleField(FieldValue $fieldValue): void
{
$class = $this->directiveArgValue('class');
$isExternalProfileManagementAllowed = Str::of(EnsureFeaturesAreActive::class)
->append(':')
->append($class)
->__toString();
App::make($isExternalProfileManagementAllowed)->handle(request(), fn () => null);
}
}
+17 -2
View File
@@ -6,13 +6,15 @@ namespace App\GraphQL\Directives;
use Illuminate\Support\Facades\App;
use Nuwave\Lighthouse\Schema\Directives\BaseDirective;
use Nuwave\Lighthouse\Schema\Values\FieldValue;
use Nuwave\Lighthouse\Schema\Values\TypeValue;
use Nuwave\Lighthouse\Support\Contracts\FieldMiddleware;
use Nuwave\Lighthouse\Support\Contracts\TypeMiddleware;
/**
* Class MiddlewareDirective.
*/
class MiddlewareDirective extends BaseDirective implements TypeMiddleware
class MiddlewareDirective extends BaseDirective implements FieldMiddleware, TypeMiddleware
{
/**
* Define the directive.
@@ -22,10 +24,23 @@ class MiddlewareDirective extends BaseDirective implements TypeMiddleware
public static function definition(): string
{
return /** @lang GraphQL */ <<<'GRAPHQL'
directive @middleware(class: String!) on OBJECT | ARGUMENT_DEFINITION
directive @middleware(class: String!) on OBJECT | FIELD_DEFINITION
GRAPHQL;
}
/**
* Wrap around the final field resolver.
*
* @param FieldValue $fieldValue
* @return void
*/
public function handleField(FieldValue $fieldValue): void
{
$class = $this->directiveArgValue('class');
App::make($class)->handle(request(), fn () => null);
}
/**
* Handle a type AST as it is converted to an executable type.
*
@@ -22,7 +22,7 @@ extend type Mutation @guard {
id: Int! @bind(class: "App\\Models\\List\\ExternalProfile", column: "profile_id")
): MessageResponse!
@middleware(class: "App\\Http\\Middleware\\Api\\EnabledOnlyOnLocalhost")
@feature(name: "App\\Features\\AllowExternalProfileManagement")
@featureEnabled(name: "App\\Features\\AllowExternalProfileManagement")
@canModel(ability: "update", injectArgs: true, model: "App\\Models\\List\\ExternalProfile")
@field(resolver: "App\\GraphQL\\Mutations\\List\\ExternalProfileMutator@sync")
}