diff --git a/packages/core/src/Utils/Cast.php b/packages/core/src/Utils/Cast.php
index 3d70f4787..66a5fbf22 100644
--- a/packages/core/src/Utils/Cast.php
+++ b/packages/core/src/Utils/Cast.php
@@ -19,36 +19,72 @@ public static function toInt(mixed $value): int {
return $value;
}
+ public static function toIntNullable(mixed $value): ?int {
+ assert($value === null || is_int($value));
+
+ return $value;
+ }
+
public static function toFloat(mixed $value): float {
assert(is_float($value));
return $value;
}
+ public static function toFloatNullable(mixed $value): ?float {
+ assert($value === null || is_float($value));
+
+ return $value;
+ }
+
public static function toString(mixed $value): string {
assert(is_string($value));
return $value;
}
+ public static function toStringNullable(mixed $value): ?string {
+ assert($value === null || is_string($value));
+
+ return $value;
+ }
+
public static function toScalar(mixed $value): int|float|string|bool {
assert(is_scalar($value));
return $value;
}
+ public static function toScalarNullable(mixed $value): int|float|string|bool|null {
+ assert($value === null || is_scalar($value));
+
+ return $value;
+ }
+
public static function toNumber(mixed $value): int|float {
assert(is_int($value) || is_float($value));
return $value;
}
+ public static function toNumberNullable(mixed $value): int|float|null {
+ assert($value === null || is_int($value) || is_float($value));
+
+ return $value;
+ }
+
public static function toBool(mixed $value): bool {
assert(is_bool($value));
return $value;
}
+ public static function toBoolNullable(mixed $value): ?bool {
+ assert($value === null || is_bool($value));
+
+ return $value;
+ }
+
public static function toStringable(mixed $value): Stringable|string {
assert(is_string($value) || $value instanceof Stringable);
diff --git a/packages/graphql/UPGRADE.md b/packages/graphql/UPGRADE.md
index 8bb326a49..73a34b90f 100644
--- a/packages/graphql/UPGRADE.md
+++ b/packages/graphql/UPGRADE.md
@@ -51,6 +51,8 @@ Please also see [changelog](https://github.com/LastDragon-ru/lara-asp/releases)
* [ ] `enum SearchByTypeFlag { yes }` => `enum SearchByTypeFlag { Yes }`. 🤝
+* [ ] `@searchByOperators` => `@searchByExtendOperators`. 🤝
+
* [ ] `@searchByOperatorRelation` => `@searchByOperatorRelationship` (and class too; generated types will be named as `SearchByRelationship*` instead of `SearchByComplex*`).
* [ ] `LastDragon_ru\LaraASP\GraphQL\SearchBy\Operators::Condition` => `LastDragon_ru\LaraASP\GraphQL\SearchBy\Operators::Object`.
@@ -139,6 +141,8 @@ Please also see [changelog](https://github.com/LastDragon-ru/lara-asp/releases)
This section is actual only if you are extending the package. Please review and update (listed the most significant changes only):
+* [ ] `LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\Operator` must explicitly implement concrete `LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\Scope` (used to filter available directive-operators, previously was required implicitly).
+
* [ ] `LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\Handler`
* [ ] `LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\Operator`
@@ -151,6 +155,8 @@ This section is actual only if you are extending the package. Please review and
* [ ] `LastDragon_ru\LaraASP\GraphQL\Builder\Exceptions\Client\ConditionTooManyProperties` => `LastDragon_ru\LaraASP\GraphQL\Builder\Exceptions\Client\ConditionTooManyFields`
+* [ ] `LastDragon_ru\LaraASP\GraphQL\Builder\Exceptions\TypeUnknown` removed
+
* [ ] `LastDragon_ru\LaraASP\GraphQL\Builder\Property` => `LastDragon_ru\LaraASP\GraphQL\Builder\Field`
* [ ] `LastDragon_ru\LaraASP\GraphQL\Builder\Manipulator`
@@ -163,10 +169,18 @@ This section is actual only if you are extending the package. Please review and
* [ ] `getPlaceholderTypeDefinitionNode()` removed => `LastDragon_ru\LaraASP\GraphQL\Utils\AstManipulator::getOriginType()`
+ * [ ] `getTypeOperators()`/`getOperator()` removed. To get operators the `LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\Context` should be used instead
+
+ ```php
+ $context->get(LastDragon_ru\LaraASP\GraphQL\Builder\Context\HandlerContextOperators::class)?->value
+ ```
+
* [ ] `LastDragon_ru\LaraASP\GraphQL\Builder\Directives\HandlerDirective`
* [ ] `LastDragon_ru\LaraASP\GraphQL\Builder\Directives\PropertyDirective` removed
+* [ ] `LastDragon_ru\LaraASP\GraphQL\Builder\Operators`
+
* [ ] `LastDragon_ru\LaraASP\GraphQL\Builder\Sources\*`
* [ ] `LastDragon_ru\LaraASP\GraphQL\Builder\Traits\PropertyOperator` => `LastDragon_ru\LaraASP\GraphQL\Builder\Traits\HandlerOperator`
diff --git a/packages/graphql/docs/Directives/@searchBy.md b/packages/graphql/docs/Directives/@searchBy.md
index bd590390a..044dbec39 100644
--- a/packages/graphql/docs/Directives/@searchBy.md
+++ b/packages/graphql/docs/Directives/@searchBy.md
@@ -218,12 +218,32 @@ The package also defines a few own types in addition to the standard GraphQL typ
```graphql
scalar MyScalar
-@searchByOperators(type: "MyScalar") # Re-use operators for `MyScalar` from config
-@searchByOperators(type: "Int") # Re-use operators from `Int` from schema
-@searchByOperatorEqual # Package operator
-@myOperator # Custom operator
+@searchByExtendOperators # Re-use operators for `MyScalar` from config
+@searchByExtendOperators(type: "MyScalar") # same
+@searchByExtendOperators(type: "Int") # Re-use operators from `Int` from schema
+@searchByOperatorEqual # Add package operator
+@myOperator # Add custom operator
```
+[include:exec]: <../../../../dev/artisan dev:directive @searchByExtendOperators>
+[//]: # (start: fb9508c1688c78899393b1119463a14ebcc2c0872316ca676b2945a296312230)
+[//]: # (warning: Generated automatically. Do not edit.)
+
+```graphql
+"""
+Extends the list of operators by the operators from the specified
+`type` or from the config if `null`.
+"""
+directive @searchByExtendOperators(
+ type: String
+)
+on
+ | ENUM
+ | SCALAR
+```
+
+[//]: # (end: fb9508c1688c78899393b1119463a14ebcc2c0872316ca676b2945a296312230)
+
### Schema
```php
diff --git a/packages/graphql/src/Builder/Context/HandlerContextOperators.php b/packages/graphql/src/Builder/Context/HandlerContextOperators.php
new file mode 100644
index 000000000..f855f08b3
--- /dev/null
+++ b/packages/graphql/src/Builder/Context/HandlerContextOperators.php
@@ -0,0 +1,13 @@
+
+ */
+ protected static function getDirectiveLocations(): array {
+ return [
+ DirectiveLocation::SCALAR,
+ DirectiveLocation::ENUM,
+ ];
+ }
+
+ public function getType(): ?string {
+ return Cast::toStringNullable($this->directiveArgValue('type'));
+ }
+}
diff --git a/packages/graphql/src/Builder/Directives/HandlerDirective.php b/packages/graphql/src/Builder/Directives/HandlerDirective.php
index 2af537291..70a98c0f7 100644
--- a/packages/graphql/src/Builder/Directives/HandlerDirective.php
+++ b/packages/graphql/src/Builder/Directives/HandlerDirective.php
@@ -19,11 +19,11 @@
use LastDragon_ru\LaraASP\GraphQL\Builder\Context;
use LastDragon_ru\LaraASP\GraphQL\Builder\Context\HandlerContextBuilderInfo;
use LastDragon_ru\LaraASP\GraphQL\Builder\Context\HandlerContextImplicit;
+use LastDragon_ru\LaraASP\GraphQL\Builder\Context\HandlerContextOperators;
use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\Context as ContextContract;
use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\Enhancer;
use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\Handler;
use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\Operator;
-use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\Scope;
use LastDragon_ru\LaraASP\GraphQL\Builder\Exceptions\Client\ConditionEmpty;
use LastDragon_ru\LaraASP\GraphQL\Builder\Exceptions\Client\ConditionTooManyFields;
use LastDragon_ru\LaraASP\GraphQL\Builder\Exceptions\Client\ConditionTooManyOperators;
@@ -55,23 +55,11 @@ abstract class HandlerDirective extends BaseDirective implements Handler, Enhanc
use WithSource;
public function __construct(
- private ArgumentFactory $factory,
+ protected readonly ArgumentFactory $argumentFactory,
) {
// empty
}
- //
- // =========================================================================
- /**
- * @return class-string
- */
- abstract public static function getScope(): string;
-
- protected function getFactory(): ArgumentFactory {
- return $this->factory;
- }
- //
-
//
// =========================================================================
/**
@@ -104,7 +92,7 @@ protected function handleAnyBuilder(
): object {
if ($value !== null && $this->definitionNode instanceof InputValueDefinitionNode) {
$argument = !($value instanceof Argument)
- ? $this->getFactory()->getArgument($this->definitionNode, $value)
+ ? $this->argumentFactory->getArgument($this->definitionNode, $value)
: $value;
$builder = $this->enhance($builder, $argument, $field, $context);
}
@@ -287,7 +275,8 @@ protected function getArgumentTypeDefinitionNode(
string $operator,
): ListTypeNode|NamedTypeNode|NonNullTypeNode|null {
// Supported?
- $operator = $manipulator->getOperator($operator, static::getScope(), $argument, $context);
+ $provider = $context->get(HandlerContextOperators::class)?->value;
+ $operator = $provider?->getOperator($manipulator, $operator, $argument, $context);
if (!$operator) {
return null;
diff --git a/packages/graphql/src/Builder/Directives/OperatorsDirective.php b/packages/graphql/src/Builder/Directives/OperatorsDirective.php
index 8a45007cf..8675c1405 100644
--- a/packages/graphql/src/Builder/Directives/OperatorsDirective.php
+++ b/packages/graphql/src/Builder/Directives/OperatorsDirective.php
@@ -2,9 +2,7 @@
namespace LastDragon_ru\LaraASP\GraphQL\Builder\Directives;
-use GraphQL\Language\DirectiveLocation;
use Nuwave\Lighthouse\Schema\DirectiveLocator;
-use Nuwave\Lighthouse\Schema\Directives\BaseDirective;
use Override;
use function array_unique;
@@ -12,11 +10,10 @@
use function implode;
use function is_string;
-abstract class OperatorsDirective extends BaseDirective {
- public function __construct() {
- // empty
- }
-
+/**
+ * @deprecated 5.6.0 Use {@see ExtendOperatorsDirective} instead.
+ */
+abstract class OperatorsDirective extends ExtendOperatorsDirective {
#[Override]
public static function definition(): string {
$name = DirectiveLocator::directiveName(static::class);
@@ -25,21 +22,14 @@ public static function definition(): string {
return <<
- */
- protected static function getDirectiveLocations(): array {
- return [
- DirectiveLocation::SCALAR,
- DirectiveLocation::ENUM,
- ];
- }
-
+ #[Override]
public function getType(): string {
$type = $this->directiveArgValue('type');
diff --git a/packages/graphql/src/Builder/Exceptions/TypeUnknown.php b/packages/graphql/src/Builder/Exceptions/TypeUnknown.php
deleted file mode 100644
index a7ebc440c..000000000
--- a/packages/graphql/src/Builder/Exceptions/TypeUnknown.php
+++ /dev/null
@@ -1,32 +0,0 @@
- $scope
- */
- public function __construct(
- protected string $scope,
- protected string $name,
- Throwable $previous = null,
- ) {
- parent::__construct(
- sprintf(
- 'Type `%s` in `%s` scope is not defined.',
- $this->name,
- $this->scope,
- ),
- $previous,
- );
- }
-
- public function getName(): string {
- return $this->name;
- }
-}
diff --git a/packages/graphql/src/Builder/Manipulator.php b/packages/graphql/src/Builder/Manipulator.php
index 1d85cd319..40ba1da75 100644
--- a/packages/graphql/src/Builder/Manipulator.php
+++ b/packages/graphql/src/Builder/Manipulator.php
@@ -5,24 +5,26 @@
use GraphQL\Language\AST\DirectiveNode;
use GraphQL\Language\AST\InputObjectTypeDefinitionNode;
use GraphQL\Language\AST\InterfaceTypeDefinitionNode;
+use GraphQL\Language\AST\Node;
use GraphQL\Language\AST\ObjectTypeDefinitionNode;
use GraphQL\Language\AST\TypeDefinitionNode;
use GraphQL\Language\AST\TypeNode;
use GraphQL\Language\BlockString;
use GraphQL\Language\Parser;
use GraphQL\Language\Printer;
+use GraphQL\Type\Definition\Argument;
+use GraphQL\Type\Definition\FieldDefinition;
+use GraphQL\Type\Definition\InputObjectField;
use GraphQL\Type\Definition\InputObjectType;
use GraphQL\Type\Definition\InterfaceType;
use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\Type;
use Illuminate\Container\Container;
+use LastDragon_ru\LaraASP\GraphQL\Builder\Context\HandlerContextOperators;
use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\Context;
-use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\Ignored;
use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\Operator;
-use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\Scope;
use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\TypeProvider;
use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\TypeSource;
-use LastDragon_ru\LaraASP\GraphQL\Builder\Directives\OperatorsDirective;
use LastDragon_ru\LaraASP\GraphQL\Builder\Exceptions\FakeTypeDefinitionIsNotFake;
use LastDragon_ru\LaraASP\GraphQL\Builder\Exceptions\FakeTypeDefinitionUnknown;
use LastDragon_ru\LaraASP\GraphQL\Builder\Exceptions\OperatorImpossibleToCreateField;
@@ -34,21 +36,15 @@
use LastDragon_ru\LaraASP\GraphQL\Builder\Sources\Source;
use LastDragon_ru\LaraASP\GraphQL\Utils\AstManipulator;
use Nuwave\Lighthouse\Schema\DirectiveLocator;
+use Nuwave\Lighthouse\Support\Contracts\Directive;
use Override;
use function array_map;
-use function array_push;
use function array_unshift;
-use function array_values;
use function count;
use function implode;
class Manipulator extends AstManipulator implements TypeProvider {
- /**
- * @var array, Operators>
- */
- private array $operators = [];
-
//
// =========================================================================
#[Override]
@@ -103,89 +99,41 @@ public function getTypeSource(TypeDefinitionNode|TypeNode|Type $type): TypeSourc
//
// =========================================================================
- public function addOperators(Operators $operators): static {
- $this->operators[$operators->getScope()] = $operators;
-
- return $this;
- }
-
/**
* @template T of Operator
*
- * @param class-string $operator
- * @param class-string $scope
+ * @param Node|(TypeDefinitionNode&Node)|Type|InputObjectField|FieldDefinition|Argument $node
+ * @param class-string $operator
*
- * @return T|null
+ * @return (T&Directive)|null
*/
- public function getOperator(string $operator, string $scope, TypeSource $source, Context $context): ?Operator {
- // Provider?
- $provider = $this->operators[$scope] ?? null;
-
- if (!$provider) {
- return null;
- }
-
- // Available?
- $operator = $provider->getOperator($operator);
-
- if (!$operator->isAvailable($this, $source, $context)) {
- return null;
- }
-
- // Return
- return $operator;
- }
-
- /**
- * @param class-string $scope
- *
- * @return list
- */
- public function getTypeOperators(
- string $type,
- string $scope,
+ public function getOperatorDirective(
+ Node|TypeDefinitionNode|Type|InputObjectField|FieldDefinition|Argument $node,
+ string $operator,
TypeSource $source,
Context $context,
- string ...$extras,
- ): array {
- // Provider?
- $provider = $this->operators[$scope] ?? null;
+ ): ?Operator {
+ // Operators?
+ $provider = $context->get(HandlerContextOperators::class)?->value;
if (!$provider) {
- return [];
- }
-
- // Operators
- $operators = $this->getOperators($provider, $scope, $type);
-
- if (!$operators) {
- return [];
- }
-
- // Extra
- foreach ($extras as $extra) {
- array_push($operators, ...$this->getOperators($provider, $scope, $extra));
+ return null;
}
- // Unique
- $unique = [];
+ // Search
+ $instance = null;
+ $directives = $this->getDirectives($node, $operator);
- foreach ($operators as $operator) {
- if (isset($unique[$operator::class])) {
- continue;
- }
+ foreach ($directives as $directive) {
+ $directive = $provider->getOperator($this, $directive, $source, $context);
- if (!$operator->isAvailable($this, $source, $context)) {
- continue;
+ if ($directive) {
+ $instance = $directive;
+ break;
}
-
- $unique[$operator::class] = $operator;
}
- $unique = array_values($unique);
-
- // Return
- return $unique;
+ return $instance;
}
/**
@@ -244,15 +192,15 @@ public function getOperatorField(
* @param list $operators
*/
public function getOperatorsFields(array $operators, TypeSource $source, Context $context): string {
- return implode(
- "\n",
- array_map(
- function (Operator $operator) use ($source, $context): string {
- return $this->getOperatorField($operator, $source, $context, null);
- },
- $operators,
- ),
- );
+ $fields = [];
+
+ foreach ($operators as $operator) {
+ if (!isset($fields[$operator::class])) {
+ $fields[$operator::class] = $this->getOperatorField($operator, $source, $context, null);
+ }
+ }
+
+ return implode("\n", $fields);
}
//
@@ -288,50 +236,5 @@ protected function removeFakeTypeDefinition(string $name): void {
// Remove
$this->removeTypeDefinition($name);
}
-
- /**
- * @param class-string $scope
- *
- * @return array
- */
- private function getOperators(Operators $provider, string $scope, string $type): array {
- $ignored = false;
- $operators = [];
-
- if ($this->isTypeDefinitionExists($type)) {
- $node = $this->getTypeDefinition($type);
- $directives = $this->getDirectives($node);
-
- foreach ($directives as $directive) {
- if (!($directive instanceof $scope)) {
- continue;
- }
-
- if ($directive instanceof OperatorsDirective) {
- $directiveType = $directive->getType();
-
- if ($type !== $directiveType) {
- array_push($operators, ...$this->getOperators($provider, $scope, $directiveType));
- } else {
- array_push($operators, ...$provider->getOperators($type));
- }
- } elseif ($directive instanceof Operator) {
- $operators[] = $directive;
- } elseif ($directive instanceof Ignored) {
- $ignored = true;
- $operators = [];
- break;
- } else {
- // empty
- }
- }
- }
-
- if (!$operators && !$ignored && $provider->hasOperators($type)) {
- array_push($operators, ...$provider->getOperators($type));
- }
-
- return $operators;
- }
//
}
diff --git a/packages/graphql/src/Builder/ManipulatorTest.php b/packages/graphql/src/Builder/ManipulatorTest.php
deleted file mode 100644
index 01ecc03a2..000000000
--- a/packages/graphql/src/Builder/ManipulatorTest.php
+++ /dev/null
@@ -1,357 +0,0 @@
-
- // =========================================================================
- public function testGetTypeOperators(): void {
- // Operators
- $scope = new class() implements Scope {
- // empty;
- };
- $builder = new stdClass();
- $aOperator = ManipulatorTest_OperatorA::class;
- $bOperator = ManipulatorTest_OperatorB::class;
- $cOperator = ManipulatorTest_OperatorC::class;
-
- // Types
- $types = Container::getInstance()->make(TypeRegistry::class);
-
- $types->register(
- new CustomScalarType([
- 'name' => 'TestScalar',
- ]),
- );
- $types->register(
- new CustomScalarType([
- 'name' => 'TestOperators',
- ]),
- );
- $types->register(
- new CustomScalarType([
- 'name' => 'TestBuiltinOperators',
- ]),
- );
-
- // Directives
- $directives = Container::getInstance()->make(DirectiveLocator::class);
-
- $directives->setResolved('ignored', ManipulatorTest_Ignored::class);
- $directives->setResolved('operators', ManipulatorTest_Operators::class);
- $directives->setResolved('aOperator', $aOperator);
- $directives->setResolved('bOperator', $bOperator);
- $directives->setResolved('cOperator', $cOperator);
-
- // Schema
- $this->useGraphQLSchema(
- <<<'GRAPHQL'
- scalar TestScalar
- @aOperator
- @bOperator
- @cOperator
-
- scalar TestIgnored
- @aOperator
- @ignored
-
- scalar TestOperators
- @operators(type: "TestScalar")
-
- scalar TestBuiltinOperators
- @operators(type: "TestBuiltinOperators")
- @aOperator
-
- type Query {
- test: Int @all
- }
- GRAPHQL,
- );
-
- // Operators
- $config = [];
- $default = [
- Operators::ID => [
- $aOperator,
- $bOperator,
- ],
- Operators::Int => [
- $bOperator,
- $cOperator,
- ],
- 'TestBuiltinOperators' => [
- $cOperator,
- ],
- ];
- $operators = new class($config, $default) extends Operators {
- /**
- * @param array|string>> $operators
- * @param array|string>> $default
- */
- public function __construct(array $operators = [], array $default = []) {
- parent::__construct($operators);
-
- $this->default = $default;
- }
-
- #[Override]
- public function getScope(): string {
- return Scope::class;
- }
- };
-
- // Manipulator
- $source = Mockery::mock(TypeSource::class);
- $context = (new Context())->override([
- HandlerContextBuilderInfo::class => new HandlerContextBuilderInfo(
- new BuilderInfo($builder::class, $builder::class),
- ),
- ]);
- $document = Container::getInstance()->make(ASTBuilder::class)->documentAST();
- $manipulator = Container::getInstance()->make(Manipulator::class, [
- 'document' => $document,
- ]);
-
- $manipulator->addOperators($operators);
-
- // Test
- $map = static function (Operator $operator): string {
- return $operator::class;
- };
-
- self::assertEquals(
- [
- $aOperator,
- ],
- array_map($map, $manipulator->getTypeOperators(Operators::ID, $operators->getScope(), $source, $context)),
- );
- self::assertEquals(
- [
- $aOperator,
- $cOperator,
- ],
- array_map(
- $map,
- $manipulator->getTypeOperators(
- Operators::ID,
- $operators->getScope(),
- $source,
- $context,
- Operators::Int,
- ),
- ),
- );
- self::assertEquals(
- [
- // empty (another scope)
- ],
- array_map($map, $manipulator->getTypeOperators(Operators::ID, $scope::class, $source, $context)),
- );
- self::assertEquals(
- [
- $aOperator,
- ],
- array_map($map, $manipulator->getTypeOperators('TestScalar', $operators->getScope(), $source, $context)),
- );
- self::assertEquals(
- [
- $aOperator,
- ],
- array_map($map, $manipulator->getTypeOperators('TestOperators', $operators->getScope(), $source, $context)),
- );
- self::assertEquals(
- [
- $cOperator,
- $aOperator,
- ],
- array_map(
- $map,
- $manipulator->getTypeOperators('TestBuiltinOperators', $operators->getScope(), $source, $context),
- ),
- );
- self::assertEquals(
- [
- // empty
- ],
- array_map(
- $map,
- $manipulator->getTypeOperators('Unknown', $operators->getScope(), $source, $context, Operators::ID),
- ),
- );
- self::assertEquals(
- [
- // empty
- ],
- array_map(
- $map,
- $manipulator->getTypeOperators('TestIgnored', $operators->getScope(), $source, $context),
- ),
- );
- }
- //
-}
-
-// @phpcs:disable PSR1.Classes.ClassDeclaration.MultipleClasses
-// @phpcs:disable Squiz.Classes.ValidClassName.NotCamelCaps
-
-/**
- * @internal
- * @noinspection PhpMultipleClassesDeclarationsInOneFile
- */
-class ManipulatorTest_Operators extends OperatorsDirective implements Scope {
- // empty
-}
-
-/**
- * @internal
- * @noinspection PhpMultipleClassesDeclarationsInOneFile
- */
-class ManipulatorTest_OperatorA extends OperatorDirective implements Operator, Scope {
- #[Override]
- public static function getName(): string {
- return 'a';
- }
-
- #[Override]
- public function getFieldType(TypeProvider $provider, TypeSource $source, ContextContract $context): ?string {
- return $source->getTypeName();
- }
-
- #[Override]
- public function getFieldDescription(): ?string {
- return '';
- }
-
- #[Override]
- protected function isBuilderSupported(string $builder): bool {
- return is_a($builder, stdClass::class, true);
- }
-
- #[Override]
- public function call(
- Handler $handler,
- object $builder,
- Field $field,
- Argument $argument,
- ContextContract $context,
- ): object {
- return $builder;
- }
-}
-
-/**
- * @internal
- * @noinspection PhpMultipleClassesDeclarationsInOneFile
- */
-class ManipulatorTest_OperatorB extends OperatorDirective implements Operator {
- #[Override]
- public static function getName(): string {
- return 'b';
- }
-
- #[Override]
- public function getFieldType(TypeProvider $provider, TypeSource $source, ContextContract $context): ?string {
- return $source->getTypeName();
- }
-
- #[Override]
- public function getFieldDescription(): ?string {
- return '';
- }
-
- #[Override]
- protected function isBuilderSupported(string $builder): bool {
- return false;
- }
-
- #[Override]
- public function call(
- Handler $handler,
- object $builder,
- Field $field,
- Argument $argument,
- ContextContract $context,
- ): object {
- return $builder;
- }
-}
-
-/**
- * @internal
- * @noinspection PhpMultipleClassesDeclarationsInOneFile
- */
-class ManipulatorTest_OperatorC extends OperatorDirective implements Operator {
- #[Override]
- public static function getName(): string {
- return 'c';
- }
-
- #[Override]
- public function getFieldType(TypeProvider $provider, TypeSource $source, ContextContract $context): ?string {
- return $source->getTypeName();
- }
-
- #[Override]
- public function getFieldDescription(): ?string {
- return '';
- }
-
- #[Override]
- protected function isBuilderSupported(string $builder): bool {
- return is_a($builder, stdClass::class, true);
- }
-
- #[Override]
- public function call(
- Handler $handler,
- object $builder,
- Field $field,
- Argument $argument,
- ContextContract $context,
- ): object {
- return $builder;
- }
-}
-
-/**
- * @internal
- * @noinspection PhpMultipleClassesDeclarationsInOneFile
- */
-class ManipulatorTest_Ignored extends BaseDirective implements Ignored, Scope {
- #[Override]
- public static function definition(): string {
- return <<<'GRAPHQL'
- directive @ignored on SCALAR
- GRAPHQL;
- }
-}
diff --git a/packages/graphql/src/Builder/Operators.php b/packages/graphql/src/Builder/Operators.php
index 016e32b6f..cd9ffae7d 100644
--- a/packages/graphql/src/Builder/Operators.php
+++ b/packages/graphql/src/Builder/Operators.php
@@ -4,18 +4,18 @@
use GraphQL\Type\Definition\Type;
use Illuminate\Container\Container;
+use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\Context;
+use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\Ignored;
use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\Operator;
use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\Scope;
-use LastDragon_ru\LaraASP\GraphQL\Builder\Exceptions\TypeUnknown;
+use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\TypeSource;
+use LastDragon_ru\LaraASP\GraphQL\Builder\Directives\ExtendOperatorsDirective;
+use LastDragon_ru\LaraASP\GraphQL\Utils\AstManipulator;
-use function array_key_exists;
-use function array_map;
use function array_merge;
-use function array_push;
-use function array_shift;
-use function array_unique;
use function array_values;
use function is_a;
+use function is_string;
abstract class Operators {
public const ID = Type::ID;
@@ -42,82 +42,172 @@ abstract class Operators {
* @param array|string>> $operators
*/
public function __construct(array $operators = []) {
- foreach ($operators as $key => $value) {
- $this->setOperators($key, $value);
+ foreach ($operators as $type => $value) {
+ $this->operators[$type] = $value;
}
}
/**
* @return class-string
*/
- abstract public function getScope(): string;
+ abstract protected function getScope(): string;
/**
* @template T of Operator
*
- * @param class-string $operator
+ * @param T|class-string $operator
*
- * @return T
+ * @return T|null
*/
- public function getOperator(string $operator): Operator {
- return Container::getInstance()->make($operator);
- }
+ public function getOperator(
+ Manipulator $manipulator,
+ Operator|string $operator,
+ TypeSource $source,
+ Context $context,
+ ): ?Operator {
+ if (!is_a($operator, $this->getScope(), true)) {
+ return null;
+ }
+
+ if (is_string($operator)) {
+ $operator = Container::getInstance()->make($operator);
+ }
+
+ if (!$operator->isAvailable($manipulator, $source, $context)) {
+ return null;
+ }
- public function hasOperators(string $type): bool {
- return array_key_exists($type, $this->operators)
- || array_key_exists($type, $this->default);
+ return $operator;
}
/**
- * @param list|string> $operators
+ * @return list
*/
- public function setOperators(string $type, array $operators): void {
- $this->operators[$type] = $operators;
+ public function getOperators(Manipulator $manipulator, string $type, TypeSource $source, Context $context): array {
+ // Operators
+ $unique = [];
+ $operators = $this->findOperators($manipulator, $type);
+
+ foreach ($operators as $operator) {
+ $operator = $this->getOperator($manipulator, $operator, $source, $context);
+
+ if ($operator && !isset($unique[$operator::class])) {
+ $unique[$operator::class] = $operator;
+ }
+ }
+
+ // Return
+ return array_values($unique);
}
/**
- * @return list
+ * @param array $processed
+ *
+ * @return array|Operator>
*/
- public function getOperators(string $type): array {
- // Is known?
- if (!$this->hasOperators($type)) {
- throw new TypeUnknown($this->getScope(), $type);
+ private function findOperators(
+ AstManipulator $manipulator,
+ string $type,
+ int $level = 0,
+ array &$processed = [],
+ ): array {
+ // We have several levels where operators can be defined - AST, config,
+ // and built-in defaults. We are always starting at the highest level
+ // and go deeper if there are no operators or if the type with the same
+ // name is found.
+
+ // Processed?
+ if (isset($processed[$type])) {
+ return [];
}
- // Operators
- $operators = $this->findOperators($type);
- $operators = array_map($this->getOperator(...), $operators);
+ // Search for operators
+ $list = match ($level) {
+ 0 => $this->findAstOperators($manipulator, $type),
+ 1 => $this->findConfigOperators($type),
+ 2 => $this->findDefaultOperators($type),
+ default => null,
+ };
+
+ if ($list === null) {
+ return [];
+ }
+
+ // Merge
+ $operators = [];
+
+ foreach ($list as $operator) {
+ if (is_a($operator, Operator::class, true)) {
+ $operators[] = $operator;
+ } elseif ($type !== $operator) {
+ $processed[$type] = true;
+ $operators = array_merge(
+ $operators,
+ $this->findOperators($manipulator, $operator, 0, $processed),
+ );
+ } else {
+ $operators = array_merge(
+ $operators,
+ $this->findOperators($manipulator, $operator, $level + 1, $processed),
+ );
+ }
+ }
+
+ // Empty?
+ if (!$operators) {
+ $operators = $this->findOperators($manipulator, $type, $level + 1, $processed);
+ }
+
+ // Mark
+ $processed[$type] = true;
// Return
return $operators;
}
/**
- * @return list>
+ * @return array|Operator|string>|null
*/
- private function findOperators(string $type): array {
- $extends = $this->operators[$type] ?? $this->default[$type] ?? [];
- $operators = [];
- $processed = [];
+ private function findAstOperators(AstManipulator $manipulator, string $type): ?array {
+ if (!$manipulator->isTypeDefinitionExists($type)) {
+ return [];
+ }
- do {
- $operator = array_shift($extends);
+ $scope = $this->getScope();
+ $operators = [];
+ $directives = $manipulator->getDirectives($manipulator->getTypeDefinition($type));
- if ($operator === null || isset($processed[$operator])) {
+ foreach ($directives as $directive) {
+ if (!($directive instanceof $scope)) {
continue;
}
- if (is_a($operator, Operator::class, true)) {
- $operators[] = $operator;
- } elseif ($type === $operator) {
- array_push($extends, ...($this->default[$operator] ?? []));
+ if ($directive instanceof ExtendOperatorsDirective) {
+ $operators[] = $directive->getType() ?? $type;
+ } elseif ($directive instanceof Operator) {
+ $operators[] = $directive;
+ } elseif ($directive instanceof Ignored) {
+ $operators = null;
+ break;
} else {
- $operators = array_merge($operators, $this->findOperators($operator));
+ // empty
}
+ }
+
+ return $operators;
+ }
- $processed[$operator] = true;
- } while ($extends);
+ /**
+ * @return array|string>
+ */
+ private function findConfigOperators(string $type): array {
+ return $this->operators[$type] ?? [];
+ }
- return array_values(array_unique($operators));
+ /**
+ * @return array|string>
+ */
+ private function findDefaultOperators(string $type): array {
+ return $this->default[$type] ?? [];
}
}
diff --git a/packages/graphql/src/Builder/OperatorsTest.php b/packages/graphql/src/Builder/OperatorsTest.php
index 886f9fd2b..4e5325c1b 100644
--- a/packages/graphql/src/Builder/OperatorsTest.php
+++ b/packages/graphql/src/Builder/OperatorsTest.php
@@ -3,15 +3,22 @@
namespace LastDragon_ru\LaraASP\GraphQL\Builder;
use Exception;
+use Illuminate\Container\Container;
use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\Context;
use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\Handler;
+use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\Ignored;
use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\Operator;
use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\Scope;
use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\TypeProvider;
use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\TypeSource;
-use LastDragon_ru\LaraASP\GraphQL\Builder\Exceptions\TypeUnknown;
+use LastDragon_ru\LaraASP\GraphQL\Builder\Directives\ExtendOperatorsDirective;
+use LastDragon_ru\LaraASP\GraphQL\Builder\Directives\OperatorsDirective;
use LastDragon_ru\LaraASP\GraphQL\Testing\Package\TestCase;
+use Mockery;
use Nuwave\Lighthouse\Execution\Arguments\Argument;
+use Nuwave\Lighthouse\Schema\AST\ASTBuilder;
+use Nuwave\Lighthouse\Schema\DirectiveLocator;
+use Nuwave\Lighthouse\Schema\Directives\BaseDirective;
use Override;
use PHPUnit\Framework\Attributes\CoversClass;
@@ -22,86 +29,124 @@
final class OperatorsTest extends TestCase {
//
// =========================================================================
- public function testHasOperators(): void {
- $operators = new class() extends Operators {
- /**
- * @inheritDoc
- */
- protected array $default = [
- Operators::Int => [
- OperatorsTest__OperatorA::class,
- ],
- ];
-
- #[Override]
- public function getScope(): string {
- return Scope::class;
- }
- };
+ public function testGetOperators(): void {
+ // Directives
+ $directives = Container::getInstance()->make(DirectiveLocator::class);
- self::assertTrue($operators->hasOperators(Operators::Int));
- self::assertFalse($operators->hasOperators('unknown'));
- }
+ $directives->setResolved('ignored', OperatorsTest__Ignored::class);
+ $directives->setResolved('operators', OperatorsTest__OperatorsDirective::class);
+ $directives->setResolved('extendOperators', OperatorsTest__ExtendOperatorsDirective::class);
+ $directives->setResolved('aOperator', OperatorsTest__OperatorA::class);
+ $directives->setResolved('bOperator', OperatorsTest__OperatorB::class);
+ $directives->setResolved('cOperator', OperatorsTest__OperatorC::class);
+ $directives->setResolved('externalOperator', OperatorsTest__OperatorExternal::class);
- public function testGetOperators(): void {
- $config = [
- 'alias' => [
- 'type-a',
+ // Schema
+ $this->useGraphQLSchema(
+ <<<'GRAPHQL'
+ scalar SchemaTypeA
+ @aOperator
+ @bOperator
+ @cOperator
+ @externalOperator
+
+ scalar SchemaTypeIgnored
+ @aOperator
+ @ignored
+
+ scalar SchemaTypeD
+ @operators(type: "TypeD")
+ @externalOperator
+
+ scalar TypeD
+ @extendOperators
+ @externalOperator
+
+ scalar SchemaTypeInfiniteLoop
+ @operators(type: "InfiniteLoop")
+ @aOperator
+
+ type Query {
+ test: Int @all
+ }
+ GRAPHQL,
+ );
+
+ // Config
+ $config = [
+ 'Alias' => [
+ 'TypeA',
],
- 'type-a' => [
+ 'TypeA' => [
+ OperatorsTest__OperatorA::class,
OperatorsTest__OperatorA::class,
+ OperatorsTest__OperatorExternal::class,
+ OperatorsTest__OperatorNotAvailable::class,
+ ],
+ 'TypeB' => [
+ OperatorsTest__OperatorNotAvailable::class,
+ OperatorsTest__OperatorExternal::class,
OperatorsTest__OperatorA::class,
+ 'TypeB',
+ ],
+ 'TypeD' => [
+ 'TypeD',
],
- 'type-b' => [
+ 'InfiniteLoop' => [
+ OperatorsTest__OperatorNotAvailable::class,
+ OperatorsTest__OperatorExternal::class,
OperatorsTest__OperatorA::class,
- 'type-b',
+ 'SchemaTypeInfiniteLoop',
],
];
- $default = [
- 'type-a' => [
+ $default = [
+ 'TypeA' => [
OperatorsTest__OperatorA::class,
OperatorsTest__OperatorB::class,
OperatorsTest__OperatorC::class,
+ OperatorsTest__OperatorExternal::class,
+ OperatorsTest__OperatorNotAvailable::class,
],
- 'type-b' => [
+ 'TypeB' => [
+ OperatorsTest__OperatorNotAvailable::class,
+ OperatorsTest__OperatorExternal::class,
OperatorsTest__OperatorB::class,
- 'type-b',
+ 'TypeB',
],
- 'type-c' => [
+ 'TypeC' => [
+ OperatorsTest__OperatorNotAvailable::class,
+ OperatorsTest__OperatorExternal::class,
OperatorsTest__OperatorC::class,
- 'type-b',
- 'type-a',
+ 'TypeB',
+ 'TypeA',
+ ],
+ 'TypeD' => [
+ OperatorsTest__OperatorNotAvailable::class,
+ OperatorsTest__OperatorExternal::class,
+ OperatorsTest__OperatorA::class,
],
];
- $operators = new class($config, $default) extends Operators {
- /**
- * @param array|string>> $operators
- * @param array|string>> $default
- */
- public function __construct(array $operators = [], array $default = []) {
- parent::__construct($operators);
-
- $this->default = $default;
- }
-
- #[Override]
- public function getScope(): string {
- return Scope::class;
- }
- };
+ $source = Mockery::mock(TypeSource::class);
+ $context = Mockery::mock(Context::class);
+ $operators = new OperatorsTest__Operators($config, $default);
+ $document = Container::getInstance()->make(ASTBuilder::class)->documentAST();
+ $manipulator = Container::getInstance()->make(Manipulator::class, [
+ 'document' => $document,
+ ]);
+ // Tests
self::assertEquals(
[
OperatorsTest__OperatorA::class,
],
- $this->toClassNames($operators->getOperators('type-a')),
+ $this->toClassNames($operators->getOperators($manipulator, 'TypeA', $source, $context)),
);
self::assertEquals(
[
OperatorsTest__OperatorA::class,
OperatorsTest__OperatorB::class,
],
- $this->toClassNames($operators->getOperators('type-b')),
+ $this->toClassNames($operators->getOperators($manipulator, 'TypeB', $source, $context)),
);
self::assertEquals(
[
@@ -109,25 +154,44 @@ public function getScope(): string {
OperatorsTest__OperatorA::class,
OperatorsTest__OperatorB::class,
],
- $this->toClassNames($operators->getOperators('type-c')),
+ $this->toClassNames($operators->getOperators($manipulator, 'TypeC', $source, $context)),
);
self::assertEquals(
- $operators->getOperators('type-a'),
- $operators->getOperators('alias'),
+ $operators->getOperators($manipulator, 'TypeA', $source, $context),
+ $operators->getOperators($manipulator, 'Alias', $source, $context),
+ );
+ self::assertEquals(
+ [
+ OperatorsTest__OperatorA::class,
+ OperatorsTest__OperatorB::class,
+ OperatorsTest__OperatorC::class,
+ ],
+ $this->toClassNames($operators->getOperators($manipulator, 'SchemaTypeA', $source, $context)),
+ );
+ self::assertEquals(
+ [
+ OperatorsTest__OperatorA::class,
+ ],
+ $this->toClassNames($operators->getOperators($manipulator, 'TypeD', $source, $context)),
+ );
+ self::assertEquals(
+ [
+ OperatorsTest__OperatorA::class,
+ ],
+ $this->toClassNames($operators->getOperators($manipulator, 'SchemaTypeD', $source, $context)),
+ );
+ self::assertEquals(
+ [
+ // empty
+ ],
+ $this->toClassNames($operators->getOperators($manipulator, 'SchemaTypeIgnored', $source, $context)),
+ );
+ self::assertEquals(
+ [
+ OperatorsTest__OperatorA::class,
+ ],
+ $this->toClassNames($operators->getOperators($manipulator, 'SchemaTypeInfiniteLoop', $source, $context)),
);
- }
-
- public function testGetOperatorsUnknownType(): void {
- $operators = new class() extends Operators {
- #[Override]
- public function getScope(): string {
- return Scope::class;
- }
- };
-
- self::expectExceptionObject(new TypeUnknown($operators->getScope(), 'unknown'));
-
- $operators->getOperators('unknown');
}
//
@@ -153,6 +217,53 @@ protected function toClassNames(array $objects): array {
// @phpcs:disable PSR1.Classes.ClassDeclaration.MultipleClasses
// @phpcs:disable Squiz.Classes.ValidClassName.NotCamelCaps
+/**
+ * @internal
+ * @noinspection PhpMultipleClassesDeclarationsInOneFile
+ */
+interface OperatorsTest__Scope extends Scope {
+ // empty
+}
+
+/**
+ * @deprecated 5.6.0
+ * @internal
+ * @noinspection PhpMultipleClassesDeclarationsInOneFile
+ *
+ */
+class OperatorsTest__OperatorsDirective extends OperatorsDirective implements OperatorsTest__Scope {
+ // empty
+}
+
+/**
+ * @internal
+ * @noinspection PhpMultipleClassesDeclarationsInOneFile
+ */
+class OperatorsTest__ExtendOperatorsDirective extends ExtendOperatorsDirective implements OperatorsTest__Scope {
+ // empty
+}
+
+/**
+ * @internal
+ * @noinspection PhpMultipleClassesDeclarationsInOneFile
+ */
+class OperatorsTest__Operators extends Operators {
+ /**
+ * @param array|string>> $operators
+ * @param array|string>> $default
+ */
+ public function __construct(array $operators = [], array $default = []) {
+ parent::__construct($operators);
+
+ $this->default = $default;
+ }
+
+ #[Override]
+ public function getScope(): string {
+ return OperatorsTest__Scope::class;
+ }
+}
+
/**
* @internal
* @noinspection PhpMultipleClassesDeclarationsInOneFile
@@ -170,7 +281,7 @@ public static function getName(): string {
#[Override]
public function isAvailable(TypeProvider $provider, TypeSource $source, Context $context): bool {
- throw new Exception('Should not be called');
+ return true;
}
#[Override]
@@ -199,7 +310,7 @@ public function call(
* @internal
* @noinspection PhpMultipleClassesDeclarationsInOneFile
*/
-class OperatorsTest__OperatorA extends OperatorsTest__Operator {
+class OperatorsTest__OperatorA extends OperatorsTest__Operator implements OperatorsTest__Scope {
// empty
}
@@ -207,7 +318,7 @@ class OperatorsTest__OperatorA extends OperatorsTest__Operator {
* @internal
* @noinspection PhpMultipleClassesDeclarationsInOneFile
*/
-class OperatorsTest__OperatorB extends OperatorsTest__Operator {
+class OperatorsTest__OperatorB extends OperatorsTest__Operator implements OperatorsTest__Scope {
// empty
}
@@ -215,7 +326,7 @@ class OperatorsTest__OperatorB extends OperatorsTest__Operator {
* @internal
* @noinspection PhpMultipleClassesDeclarationsInOneFile
*/
-class OperatorsTest__OperatorC extends OperatorsTest__Operator {
+class OperatorsTest__OperatorC extends OperatorsTest__Operator implements OperatorsTest__Scope {
// empty
}
@@ -223,6 +334,30 @@ class OperatorsTest__OperatorC extends OperatorsTest__Operator {
* @internal
* @noinspection PhpMultipleClassesDeclarationsInOneFile
*/
-class OperatorsTest__OperatorD extends OperatorsTest__Operator {
+class OperatorsTest__OperatorNotAvailable extends OperatorsTest__Operator implements OperatorsTest__Scope {
+ #[Override]
+ public function isAvailable(TypeProvider $provider, TypeSource $source, Context $context): bool {
+ return false;
+ }
+}
+
+/**
+ * @internal
+ * @noinspection PhpMultipleClassesDeclarationsInOneFile
+ */
+class OperatorsTest__OperatorExternal extends OperatorsTest__Operator {
// empty
}
+
+/**
+ * @internal
+ * @noinspection PhpMultipleClassesDeclarationsInOneFile
+ */
+class OperatorsTest__Ignored extends BaseDirective implements Ignored, OperatorsTest__Scope {
+ #[Override]
+ public static function definition(): string {
+ return <<<'GRAPHQL'
+ directive @ignored on SCALAR
+ GRAPHQL;
+ }
+}
diff --git a/packages/graphql/src/Builder/Types/InputObject.php b/packages/graphql/src/Builder/Types/InputObject.php
index 858d0065a..4f4db6be7 100644
--- a/packages/graphql/src/Builder/Types/InputObject.php
+++ b/packages/graphql/src/Builder/Types/InputObject.php
@@ -13,10 +13,10 @@
use GraphQL\Type\Definition\InputObjectField;
use GraphQL\Type\Definition\Type;
use LastDragon_ru\LaraASP\GraphQL\Builder\Context\HandlerContextImplicit;
+use LastDragon_ru\LaraASP\GraphQL\Builder\Context\HandlerContextOperators;
use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\Context;
use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\Ignored;
use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\Operator;
-use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\Scope;
use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\TypeDefinition;
use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\TypeSource;
use LastDragon_ru\LaraASP\GraphQL\Builder\Exceptions\TypeDefinitionFieldAlreadyDefined;
@@ -46,11 +46,6 @@ public function __construct() {
// empty
}
- /**
- * @return class-string
- */
- abstract protected function getScope(): string;
-
abstract protected function getDescription(
Manipulator $manipulator,
InputSource|ObjectSource|InterfaceSource $source,
@@ -141,8 +136,9 @@ protected function getOperators(
Context $context,
): array {
$type = $this->getTypeForOperators();
- $operators = $type
- ? $manipulator->getTypeOperators($type, $this->getScope(), $source, $context)
+ $provider = $context->get(HandlerContextOperators::class)?->value;
+ $operators = $type && $provider
+ ? $provider->getOperators($manipulator, $type, $source, $context)
: [];
return $operators;
@@ -313,7 +309,7 @@ protected function getFieldDefinition(
// Operator?
$operator = $this->getFieldOperator($manipulator, $field, $context);
- if ($operator === null || !$operator->isAvailable($manipulator, $field, $context)) {
+ if (!$operator) {
return null;
}
@@ -341,10 +337,11 @@ protected function getFieldOperator(
$operator = $this->getFieldOperatorDirective($manipulator, $field, $context, $this->getFieldMarkerOperator());
if (!$operator) {
- $type = $this->getTypeForFieldOperator();
+ $type = $this->getTypeForFieldOperator();
+ $provider = $context->get(HandlerContextOperators::class)?->value;
- if ($type) {
- $operators = $manipulator->getTypeOperators($type, $this->getScope(), $field, $context);
+ if ($type && $provider) {
+ $operators = $provider->getOperators($manipulator, $type, $field, $context);
$operator = reset($operators) ?: null;
}
}
@@ -370,13 +367,7 @@ protected function getFieldOperatorDirective(
$nodes = [$field->getField(), $field->getTypeDefinition()];
foreach ($nodes as $node) {
- $operator = $manipulator->getDirective(
- $node,
- $directive,
- static function (Operator $operator) use ($manipulator, $field, $context): bool {
- return $operator->isAvailable($manipulator, $field, $context);
- },
- );
+ $operator = $manipulator->getOperatorDirective($node, $directive, $field, $context);
if ($operator) {
break;
diff --git a/packages/graphql/src/Builder/Types/InputObjectTest.php b/packages/graphql/src/Builder/Types/InputObjectTest.php
index f76da5594..8b95274db 100644
--- a/packages/graphql/src/Builder/Types/InputObjectTest.php
+++ b/packages/graphql/src/Builder/Types/InputObjectTest.php
@@ -158,11 +158,6 @@ public static function definition(): string {
* @noinspection PhpMultipleClassesDeclarationsInOneFile
*/
class InputObjectTest__InputObject extends InputObject {
- #[Override]
- protected function getScope(): string {
- throw new Exception('Should not be called');
- }
-
#[Override]
protected function getDescription(
Manipulator $manipulator,
diff --git a/packages/graphql/src/Provider.php b/packages/graphql/src/Provider.php
index 7c261ced6..6c63d4961 100644
--- a/packages/graphql/src/Provider.php
+++ b/packages/graphql/src/Provider.php
@@ -8,13 +8,10 @@
use LastDragon_ru\LaraASP\Core\Provider\WithConfig;
use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\BuilderFieldResolver as BuilderFieldResolverContract;
use LastDragon_ru\LaraASP\GraphQL\Builder\Defaults\BuilderFieldResolver;
-use LastDragon_ru\LaraASP\GraphQL\Builder\Manipulator;
use LastDragon_ru\LaraASP\GraphQL\Printer\DirectiveResolver;
use LastDragon_ru\LaraASP\GraphQL\SearchBy\Definitions\SearchByDirective;
-use LastDragon_ru\LaraASP\GraphQL\SearchBy\Operators as SearchByOperators;
use LastDragon_ru\LaraASP\GraphQL\SortBy\Contracts\SorterFactory as SorterFactoryContract;
use LastDragon_ru\LaraASP\GraphQL\SortBy\Definitions\SortByDirective;
-use LastDragon_ru\LaraASP\GraphQL\SortBy\Operators as SortByOperators;
use LastDragon_ru\LaraASP\GraphQL\SortBy\SorterFactory;
use LastDragon_ru\LaraASP\GraphQL\Stream\Contracts\StreamFactory as StreamFactoryContract;
use LastDragon_ru\LaraASP\GraphQL\Stream\Definitions\StreamDirective;
@@ -46,7 +43,6 @@ public function register(): void {
parent::register();
$this->registerBindings();
- $this->registerOperators();
$this->registerSchemaPrinter();
}
@@ -70,16 +66,6 @@ protected function registerBindings(): void {
$this->app->scopedIf(BuilderFieldResolverContract::class, BuilderFieldResolver::class);
}
- protected function registerOperators(): void {
- $this->callAfterResolving(
- Manipulator::class,
- static function (Manipulator $manipulator): void {
- $manipulator->addOperators(new SearchByOperators());
- $manipulator->addOperators(new SortByOperators());
- },
- );
- }
-
protected function registerSchemaPrinter(): void {
$this->app->bindIf(SettingsContract::class, DefaultSettings::class);
$this->app->bindIf(DirectiveResolverContract::class, DirectiveResolver::class);
diff --git a/packages/graphql/src/SearchBy/Definitions/SearchByExtendOperatorsDirective.php b/packages/graphql/src/SearchBy/Definitions/SearchByExtendOperatorsDirective.php
new file mode 100644
index 000000000..307d87d0a
--- /dev/null
+++ b/packages/graphql/src/SearchBy/Definitions/SearchByExtendOperatorsDirective.php
@@ -0,0 +1,11 @@
+
- // =========================================================================
- #[Override]
- public static function getScope(): string {
- return Scope::class;
- }
- //
-
//
// =========================================================================
#[Override]
@@ -62,7 +55,8 @@ protected function getArgDefinitionType(
ObjectFieldArgumentSource|InterfaceFieldArgumentSource $argument,
Context $context,
): ListTypeNode|NamedTypeNode|NonNullTypeNode {
- $type = $this->getArgumentTypeDefinitionNode($manipulator, $document, $argument, $context, Root::class);
+ $context = $context->override([HandlerContextOperators::class => new HandlerContextOperators(new Operators())]);
+ $type = $this->getArgumentTypeDefinitionNode($manipulator, $document, $argument, $context, Root::class);
if (!$type) {
throw new FailedToCreateSearchCondition($argument);
diff --git a/packages/graphql/src/SearchBy/Directives/DirectiveTest/ScalarOperators.expected.graphql b/packages/graphql/src/SearchBy/Directives/DirectiveTest/ScalarOperators.expected.graphql
index ac41db64c..f0549f6d3 100644
--- a/packages/graphql/src/SearchBy/Directives/DirectiveTest/ScalarOperators.expected.graphql
+++ b/packages/graphql/src/SearchBy/Directives/DirectiveTest/ScalarOperators.expected.graphql
@@ -5,6 +5,17 @@ directive @searchBy
on
| ARGUMENT_DEFINITION
+"""
+Extends the list of operators by the operators from the specified
+`type` or from the config if `null`.
+"""
+directive @searchByExtendOperators(
+ type: String
+)
+on
+ | ENUM
+ | SCALAR
+
directive @searchByOperatorAllOf
on
| ENUM
@@ -65,16 +76,6 @@ on
| INPUT_FIELD_DEFINITION
| SCALAR
-"""
-Extends the list of operators by the operators from the specified `type`.
-"""
-directive @searchByOperators(
- type: String!
-)
-on
- | ENUM
- | SCALAR
-
enum EnumA
@searchByOperatorIn
{
@@ -215,7 +216,7 @@ scalar Date
@scalar(
class: "Nuwave\\Lighthouse\\Schema\\Types\\Scalars\\Date"
)
-@searchByOperators(
+@searchByExtendOperators(
type: "Boolean"
)
@searchByOperatorLessThan
diff --git a/packages/graphql/src/SearchBy/Directives/DirectiveTest/ScalarOperators.schema.graphql b/packages/graphql/src/SearchBy/Directives/DirectiveTest/ScalarOperators.schema.graphql
index d9fcb93ae..ad15482ff 100644
--- a/packages/graphql/src/SearchBy/Directives/DirectiveTest/ScalarOperators.schema.graphql
+++ b/packages/graphql/src/SearchBy/Directives/DirectiveTest/ScalarOperators.schema.graphql
@@ -10,7 +10,7 @@ input A {
scalar Date
@scalar(class: "Nuwave\\Lighthouse\\Schema\\Types\\Scalars\\Date")
-@searchByOperators(type: "Boolean")
+@searchByExtendOperators(type: "Boolean")
@searchByOperatorLessThan
@searchByOperatorLessThanOrEqual
diff --git a/packages/graphql/src/SearchBy/Directives/DirectiveTest/Scout.expected.graphql b/packages/graphql/src/SearchBy/Directives/DirectiveTest/Scout.expected.graphql
index 374023e01..05912fab8 100644
--- a/packages/graphql/src/SearchBy/Directives/DirectiveTest/Scout.expected.graphql
+++ b/packages/graphql/src/SearchBy/Directives/DirectiveTest/Scout.expected.graphql
@@ -5,6 +5,17 @@ directive @searchBy
on
| ARGUMENT_DEFINITION
+"""
+Extends the list of operators by the operators from the specified
+`type` or from the config if `null`.
+"""
+directive @searchByExtendOperators(
+ type: String
+)
+on
+ | ENUM
+ | SCALAR
+
"""
Marks that field/definition should be excluded from search.
"""
@@ -65,16 +76,6 @@ on
| INPUT_FIELD_DEFINITION
| SCALAR
-"""
-Extends the list of operators by the operators from the specified `type`.
-"""
-directive @searchByOperators(
- type: String!
-)
-on
- | ENUM
- | SCALAR
-
enum EnumA {
One
Two
@@ -1014,7 +1015,7 @@ scalar ScalarCustom
@scalar(
class: "Nuwave\\Lighthouse\\Schema\\Types\\Scalars\\Date"
)
-@searchByOperators(
+@searchByExtendOperators(
type: "Boolean"
)
@searchByOperatorLessThan
diff --git a/packages/graphql/src/SearchBy/Directives/DirectiveTest/Scout.expected.v10.3.0.graphql b/packages/graphql/src/SearchBy/Directives/DirectiveTest/Scout.expected.v10.3.0.graphql
index 466f9c562..883122ca3 100644
--- a/packages/graphql/src/SearchBy/Directives/DirectiveTest/Scout.expected.v10.3.0.graphql
+++ b/packages/graphql/src/SearchBy/Directives/DirectiveTest/Scout.expected.v10.3.0.graphql
@@ -5,6 +5,17 @@ directive @searchBy
on
| ARGUMENT_DEFINITION
+"""
+Extends the list of operators by the operators from the specified
+`type` or from the config if `null`.
+"""
+directive @searchByExtendOperators(
+ type: String
+)
+on
+ | ENUM
+ | SCALAR
+
"""
Marks that field/definition should be excluded from search.
"""
@@ -71,16 +82,6 @@ on
| INPUT_FIELD_DEFINITION
| SCALAR
-"""
-Extends the list of operators by the operators from the specified `type`.
-"""
-directive @searchByOperators(
- type: String!
-)
-on
- | ENUM
- | SCALAR
-
enum EnumA {
One
Two
@@ -1080,7 +1081,7 @@ scalar ScalarCustom
@scalar(
class: "Nuwave\\Lighthouse\\Schema\\Types\\Scalars\\Date"
)
-@searchByOperators(
+@searchByExtendOperators(
type: "Boolean"
)
@searchByOperatorLessThan
diff --git a/packages/graphql/src/SearchBy/Directives/DirectiveTest/Scout.schema.graphql b/packages/graphql/src/SearchBy/Directives/DirectiveTest/Scout.schema.graphql
index bd88c2cf7..d0d60b56c 100644
--- a/packages/graphql/src/SearchBy/Directives/DirectiveTest/Scout.schema.graphql
+++ b/packages/graphql/src/SearchBy/Directives/DirectiveTest/Scout.schema.graphql
@@ -98,7 +98,7 @@ scalar DateIgnored
scalar ScalarCustom
@scalar(class: "Nuwave\\Lighthouse\\Schema\\Types\\Scalars\\Date")
-@searchByOperators(type: "Boolean")
+@searchByExtendOperators(type: "Boolean")
@searchByOperatorLessThan
@searchByOperatorLessThanOrEqual
diff --git a/packages/graphql/src/SearchBy/Directives/DirectiveTest/V5CompatScout.expected.graphql b/packages/graphql/src/SearchBy/Directives/DirectiveTest/V5CompatScout.expected.graphql
index 83724db8f..ce2aa4e4b 100644
--- a/packages/graphql/src/SearchBy/Directives/DirectiveTest/V5CompatScout.expected.graphql
+++ b/packages/graphql/src/SearchBy/Directives/DirectiveTest/V5CompatScout.expected.graphql
@@ -61,6 +61,8 @@ on
"""
Extends the list of operators by the operators from the specified `type`.
+
+The directive is deprecated!
"""
directive @searchByOperators(
type: String!
diff --git a/packages/graphql/src/SearchBy/Directives/ExtendOperators.php b/packages/graphql/src/SearchBy/Directives/ExtendOperators.php
new file mode 100644
index 000000000..32ef46b8f
--- /dev/null
+++ b/packages/graphql/src/SearchBy/Directives/ExtendOperators.php
@@ -0,0 +1,10 @@
+isNullable() && ($source->isScalar() || $source->isEnum()));
+ }
+
#[Override]
public function getFieldDescription(): ?string {
return 'Is NOT NULL?';
diff --git a/packages/graphql/src/SearchBy/Operators/Comparison/IsNull.php b/packages/graphql/src/SearchBy/Operators/Comparison/IsNull.php
index 383da73ea..02d985e97 100644
--- a/packages/graphql/src/SearchBy/Operators/Comparison/IsNull.php
+++ b/packages/graphql/src/SearchBy/Operators/Comparison/IsNull.php
@@ -24,6 +24,12 @@ public static function getName(): string {
return 'isNull';
}
+ #[Override]
+ public function isAvailable(TypeProvider $provider, TypeSource $source, Context $context): bool {
+ return parent::isAvailable($provider, $source, $context)
+ && ($source->isNullable() && ($source->isScalar() || $source->isEnum()));
+ }
+
#[Override]
public function getFieldDescription(): ?string {
return 'Is NULL?';
diff --git a/packages/graphql/src/SearchBy/OperatorsTest.php b/packages/graphql/src/SearchBy/OperatorsTest.php
index 957958595..b1446287e 100644
--- a/packages/graphql/src/SearchBy/OperatorsTest.php
+++ b/packages/graphql/src/SearchBy/OperatorsTest.php
@@ -2,11 +2,21 @@
namespace LastDragon_ru\LaraASP\GraphQL\SearchBy;
+use Exception;
use Illuminate\Container\Container;
+use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\Context;
+use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\Handler;
+use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\TypeProvider;
+use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\TypeSource;
+use LastDragon_ru\LaraASP\GraphQL\Builder\Field;
+use LastDragon_ru\LaraASP\GraphQL\Builder\Manipulator;
use LastDragon_ru\LaraASP\GraphQL\Package;
-use LastDragon_ru\LaraASP\GraphQL\SearchBy\Definitions\SearchByOperatorEqualDirective;
-use LastDragon_ru\LaraASP\GraphQL\SearchBy\Definitions\SearchByOperatorNotEqualDirective;
+use LastDragon_ru\LaraASP\GraphQL\SearchBy\Operators\Operator;
use LastDragon_ru\LaraASP\GraphQL\Testing\Package\TestCase;
+use Mockery;
+use Nuwave\Lighthouse\Execution\Arguments\Argument;
+use Nuwave\Lighthouse\Schema\AST\DocumentAST;
+use Override;
use PHPUnit\Framework\Attributes\CoversClass;
use function config;
@@ -22,25 +32,28 @@ public function testConstructor(): void {
config([
Package::Name.'.search_by.operators' => [
Operators::ID => [
- SearchByOperatorEqualDirective::class,
+ OperatorsTest__Operator::class,
],
Operators::Int => [
- SearchByOperatorNotEqualDirective::class,
+ OperatorsTest__Operator::class,
],
],
]);
- $operators = Container::getInstance()->make(Operators::class);
+ $source = Mockery::mock(TypeSource::class);
+ $context = Mockery::mock(Context::class);
+ $operators = Container::getInstance()->make(Operators::class);
+ $manipulator = Container::getInstance()->make(Manipulator::class, [
+ 'document' => Mockery::mock(DocumentAST::class),
+ ]);
- self::assertTrue($operators->hasOperators(Operators::ID));
- self::assertTrue($operators->hasOperators(Operators::Int));
- self::assertFalse($operators->hasOperators('unknown'));
+ self::assertEquals([], $operators->getOperators($manipulator, 'unknown', $source, $context));
self::assertEquals(
[
- SearchByOperatorEqualDirective::class,
+ OperatorsTest__Operator::class,
],
$this->toClassNames(
- $operators->getOperators(Operators::ID),
+ $operators->getOperators($manipulator, Operators::ID, $source, $context),
),
);
}
@@ -64,3 +77,43 @@ protected function toClassNames(array $objects): array {
}
//
}
+
+// @phpcs:disable PSR1.Classes.ClassDeclaration.MultipleClasses
+// @phpcs:disable Squiz.Classes.ValidClassName.NotCamelCaps
+
+/**
+ * @internal
+ * @noinspection PhpMultipleClassesDeclarationsInOneFile
+ */
+class OperatorsTest__Operator extends Operator {
+ #[Override]
+ public static function getName(): string {
+ throw new Exception('Should not be called');
+ }
+
+ #[Override]
+ public function isAvailable(TypeProvider $provider, TypeSource $source, Context $context): bool {
+ return true;
+ }
+
+ #[Override]
+ public function getFieldType(TypeProvider $provider, TypeSource $source, Context $context): ?string {
+ throw new Exception('Should not be called');
+ }
+
+ #[Override]
+ public function getFieldDescription(): ?string {
+ throw new Exception('Should not be called');
+ }
+
+ #[Override]
+ public function call(
+ Handler $handler,
+ object $builder,
+ Field $field,
+ Argument $argument,
+ Context $context,
+ ): object {
+ throw new Exception('Should not be called');
+ }
+}
diff --git a/packages/graphql/src/SearchBy/Types/Condition/Type.php b/packages/graphql/src/SearchBy/Types/Condition/Type.php
index ba8d7372f..3392c63ca 100644
--- a/packages/graphql/src/SearchBy/Types/Condition/Type.php
+++ b/packages/graphql/src/SearchBy/Types/Condition/Type.php
@@ -3,6 +3,7 @@
namespace LastDragon_ru\LaraASP\GraphQL\SearchBy\Types\Condition;
use LastDragon_ru\LaraASP\GraphQL\Builder\Context\HandlerContextBuilderInfo;
+use LastDragon_ru\LaraASP\GraphQL\Builder\Context\HandlerContextOperators;
use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\Context;
use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\Operator as OperatorContract;
use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\TypeSource;
@@ -34,11 +35,6 @@ public function getTypeName(TypeSource $source, Context $context): string {
return "{$directiveName}{$builderName}{$name}{$typeName}";
}
- #[Override]
- protected function getScope(): string {
- return Directive::getScope();
- }
-
#[Override]
protected function getDescription(
Manipulator $manipulator,
@@ -74,13 +70,11 @@ protected function getFieldOperator(
InputFieldSource|ObjectFieldSource|InterfaceFieldSource $field,
Context $context,
): ?OperatorContract {
+ $provider = $context->get(HandlerContextOperators::class)?->value;
+ $operator = SearchByOperatorConditionDirective::class;
+
return match (true) {
- $field->isScalar(), $field->isEnum() => $manipulator->getOperator(
- SearchByOperatorConditionDirective::class,
- $this->getScope(),
- $field,
- $context,
- ),
+ $field->isScalar(), $field->isEnum() => $provider?->getOperator($manipulator, $operator, $field, $context),
$field->isObject() => parent::getFieldOperator($manipulator, $field, $context),
default => throw new NotImplemented($field),
};
diff --git a/packages/graphql/src/SearchBy/Types/Enumeration.php b/packages/graphql/src/SearchBy/Types/Enumeration.php
index 177417ca4..0be834119 100644
--- a/packages/graphql/src/SearchBy/Types/Enumeration.php
+++ b/packages/graphql/src/SearchBy/Types/Enumeration.php
@@ -6,6 +6,7 @@
use GraphQL\Language\Parser;
use GraphQL\Type\Definition\Type;
use LastDragon_ru\LaraASP\GraphQL\Builder\Context\HandlerContextBuilderInfo;
+use LastDragon_ru\LaraASP\GraphQL\Builder\Context\HandlerContextOperators;
use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\Context;
use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\TypeDefinition;
use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\TypeSource;
@@ -14,6 +15,8 @@
use LastDragon_ru\LaraASP\GraphQL\SearchBy\Operators;
use Override;
+use function array_merge;
+
class Enumeration implements TypeDefinition {
public function __construct() {
// empty
@@ -44,17 +47,30 @@ public function getTypeDefinition(
return null;
}
+ // Operators?
+ $provider = $context->get(HandlerContextOperators::class)?->value;
+
+ if (!$provider) {
+ return null;
+ }
+
// Operators
$type = $manipulator->getTypeSource($source->getType());
- $scope = Directive::getScope();
- $extras = $type->isNullable() ? [Operators::Null] : [];
- $operators = $manipulator->getTypeOperators($type->getTypeName(), $scope, $type, $context, ...$extras)
- ?: $manipulator->getTypeOperators(Operators::Enum, $scope, $type, $context, ...$extras);
+ $operators = $provider->getOperators($manipulator, $type->getTypeName(), $type, $context)
+ ?: $provider->getOperators($manipulator, Operators::Enum, $type, $context);
if (!$operators) {
return null;
}
+ // Nullable?
+ if ($type->isNullable()) {
+ $operators = array_merge(
+ $operators,
+ $provider->getOperators($manipulator, Operators::Null, $type, $context),
+ );
+ }
+
// Definition
$content = $manipulator->getOperatorsFields($operators, $type, $context);
$definition = Parser::inputObjectTypeDefinition(
diff --git a/packages/graphql/src/SearchBy/Types/Scalar.php b/packages/graphql/src/SearchBy/Types/Scalar.php
index 3620172b9..05740ebd9 100644
--- a/packages/graphql/src/SearchBy/Types/Scalar.php
+++ b/packages/graphql/src/SearchBy/Types/Scalar.php
@@ -6,6 +6,7 @@
use GraphQL\Language\Parser;
use GraphQL\Type\Definition\Type;
use LastDragon_ru\LaraASP\GraphQL\Builder\Context\HandlerContextBuilderInfo;
+use LastDragon_ru\LaraASP\GraphQL\Builder\Context\HandlerContextOperators;
use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\Context;
use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\TypeDefinition;
use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\TypeSource;
@@ -14,6 +15,8 @@
use LastDragon_ru\LaraASP\GraphQL\SearchBy\Operators;
use Override;
+use function array_merge;
+
class Scalar implements TypeDefinition {
public function __construct() {
// empty
@@ -44,16 +47,29 @@ public function getTypeDefinition(
return null;
}
+ // Operators?
+ $provider = $context->get(HandlerContextOperators::class)?->value;
+
+ if (!$provider) {
+ return null;
+ }
+
// Operators
$type = $manipulator->getTypeSource($source->getType());
- $scope = Directive::getScope();
- $extras = $type->isNullable() ? [Operators::Null] : [];
- $operators = $manipulator->getTypeOperators($type->getTypeName(), $scope, $type, $context, ...$extras);
+ $operators = $provider->getOperators($manipulator, $type->getTypeName(), $type, $context);
if (!$operators) {
return null;
}
+ // Nullable?
+ if ($type->isNullable()) {
+ $operators = array_merge(
+ $operators,
+ $provider->getOperators($manipulator, Operators::Null, $type, $context),
+ );
+ }
+
// Definition
$content = $manipulator->getOperatorsFields($operators, $type, $context);
$definition = Parser::inputObjectTypeDefinition(
diff --git a/packages/graphql/src/SortBy/Directives/Directive.php b/packages/graphql/src/SortBy/Directives/Directive.php
index dba921d79..4d5c2c9e5 100644
--- a/packages/graphql/src/SortBy/Directives/Directive.php
+++ b/packages/graphql/src/SortBy/Directives/Directive.php
@@ -5,13 +5,14 @@
use GraphQL\Language\AST\ListTypeNode;
use GraphQL\Language\AST\NamedTypeNode;
use GraphQL\Language\AST\NonNullTypeNode;
+use LastDragon_ru\LaraASP\GraphQL\Builder\Context\HandlerContextOperators;
use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\Context;
use LastDragon_ru\LaraASP\GraphQL\Builder\Directives\HandlerDirective;
use LastDragon_ru\LaraASP\GraphQL\Builder\Manipulator;
use LastDragon_ru\LaraASP\GraphQL\Builder\Sources\InterfaceFieldArgumentSource;
use LastDragon_ru\LaraASP\GraphQL\Builder\Sources\ObjectFieldArgumentSource;
-use LastDragon_ru\LaraASP\GraphQL\SortBy\Contracts\Scope;
use LastDragon_ru\LaraASP\GraphQL\SortBy\Exceptions\FailedToCreateSortClause;
+use LastDragon_ru\LaraASP\GraphQL\SortBy\Operators;
use LastDragon_ru\LaraASP\GraphQL\SortBy\Operators\Root;
use Nuwave\Lighthouse\Schema\AST\DocumentAST;
use Nuwave\Lighthouse\Schema\DirectiveLocator;
@@ -37,14 +38,6 @@ public static function definition(): string {
GRAPHQL;
}
- //
- // =========================================================================
- #[Override]
- public static function getScope(): string {
- return Scope::class;
- }
- //
-
//
// =========================================================================
#[Override]
@@ -59,7 +52,8 @@ protected function getArgDefinitionType(
ObjectFieldArgumentSource|InterfaceFieldArgumentSource $argument,
Context $context,
): ListTypeNode|NamedTypeNode|NonNullTypeNode {
- $type = $this->getArgumentTypeDefinitionNode($manipulator, $document, $argument, $context, Root::class);
+ $context = $context->override([HandlerContextOperators::class => new HandlerContextOperators(new Operators())]);
+ $type = $this->getArgumentTypeDefinitionNode($manipulator, $document, $argument, $context, Root::class);
if (!$type) {
throw new FailedToCreateSortClause($argument);
diff --git a/packages/graphql/src/SortBy/Operators.php b/packages/graphql/src/SortBy/Operators.php
index 285fbc416..2ec52c70c 100644
--- a/packages/graphql/src/SortBy/Operators.php
+++ b/packages/graphql/src/SortBy/Operators.php
@@ -5,6 +5,7 @@
use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\Operator as BuilderOperator;
use LastDragon_ru\LaraASP\GraphQL\Builder\Operators as BuilderOperators;
use LastDragon_ru\LaraASP\GraphQL\Package;
+use LastDragon_ru\LaraASP\GraphQL\SortBy\Contracts\Scope;
use LastDragon_ru\LaraASP\GraphQL\SortBy\Definitions\SortByOperatorChildDirective;
use LastDragon_ru\LaraASP\GraphQL\SortBy\Definitions\SortByOperatorFieldDirective;
use LastDragon_ru\LaraASP\GraphQL\SortBy\Definitions\SortByOperatorNullsFirstDirective;
@@ -41,6 +42,6 @@ public function __construct() {
#[Override]
public function getScope(): string {
- return Directive::getScope();
+ return Scope::class;
}
}
diff --git a/packages/graphql/src/SortBy/OperatorsTest.php b/packages/graphql/src/SortBy/OperatorsTest.php
index eca4a7d20..7c4dc44d5 100644
--- a/packages/graphql/src/SortBy/OperatorsTest.php
+++ b/packages/graphql/src/SortBy/OperatorsTest.php
@@ -2,10 +2,21 @@
namespace LastDragon_ru\LaraASP\GraphQL\SortBy;
+use Exception;
use Illuminate\Container\Container;
+use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\Context;
+use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\Handler;
+use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\TypeProvider;
+use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\TypeSource;
+use LastDragon_ru\LaraASP\GraphQL\Builder\Field;
+use LastDragon_ru\LaraASP\GraphQL\Builder\Manipulator;
use LastDragon_ru\LaraASP\GraphQL\Package;
-use LastDragon_ru\LaraASP\GraphQL\SortBy\Definitions\SortByOperatorRandomDirective;
+use LastDragon_ru\LaraASP\GraphQL\SortBy\Operators\Operator;
use LastDragon_ru\LaraASP\GraphQL\Testing\Package\TestCase;
+use Mockery;
+use Nuwave\Lighthouse\Execution\Arguments\Argument;
+use Nuwave\Lighthouse\Schema\AST\DocumentAST;
+use Override;
use PHPUnit\Framework\Attributes\CoversClass;
use function config;
@@ -21,21 +32,25 @@ public function testConstructor(): void {
config([
Package::Name.'.sort_by.operators' => [
Operators::Extra => [
- SortByOperatorRandomDirective::class,
+ OperatorsTest__Operator::class,
],
],
]);
- $operators = Container::getInstance()->make(Operators::class);
+ $source = Mockery::mock(TypeSource::class);
+ $context = Mockery::mock(Context::class);
+ $operators = Container::getInstance()->make(Operators::class);
+ $manipulator = Container::getInstance()->make(Manipulator::class, [
+ 'document' => Mockery::mock(DocumentAST::class),
+ ]);
- self::assertTrue($operators->hasOperators(Operators::Extra));
- self::assertFalse($operators->hasOperators('unknown'));
+ self::assertEquals([], $operators->getOperators($manipulator, 'unknown', $source, $context));
self::assertEquals(
[
- SortByOperatorRandomDirective::class,
+ OperatorsTest__Operator::class,
],
$this->toClassNames(
- $operators->getOperators(Operators::Extra),
+ $operators->getOperators($manipulator, Operators::Extra, $source, $context),
),
);
}
@@ -59,3 +74,43 @@ protected function toClassNames(array $objects): array {
}
//
}
+
+// @phpcs:disable PSR1.Classes.ClassDeclaration.MultipleClasses
+// @phpcs:disable Squiz.Classes.ValidClassName.NotCamelCaps
+
+/**
+ * @internal
+ * @noinspection PhpMultipleClassesDeclarationsInOneFile
+ */
+class OperatorsTest__Operator extends Operator {
+ #[Override]
+ public static function getName(): string {
+ throw new Exception('Should not be called');
+ }
+
+ #[Override]
+ public function isAvailable(TypeProvider $provider, TypeSource $source, Context $context): bool {
+ return true;
+ }
+
+ #[Override]
+ public function getFieldType(TypeProvider $provider, TypeSource $source, Context $context): ?string {
+ throw new Exception('Should not be called');
+ }
+
+ #[Override]
+ public function getFieldDescription(): ?string {
+ throw new Exception('Should not be called');
+ }
+
+ #[Override]
+ public function call(
+ Handler $handler,
+ object $builder,
+ Field $field,
+ Argument $argument,
+ Context $context,
+ ): object {
+ throw new Exception('Should not be called');
+ }
+}
diff --git a/packages/graphql/src/SortBy/Types/Clause/Type.php b/packages/graphql/src/SortBy/Types/Clause/Type.php
index c7e5a4a23..a105f7038 100644
--- a/packages/graphql/src/SortBy/Types/Clause/Type.php
+++ b/packages/graphql/src/SortBy/Types/Clause/Type.php
@@ -3,6 +3,7 @@
namespace LastDragon_ru\LaraASP\GraphQL\SortBy\Types\Clause;
use LastDragon_ru\LaraASP\GraphQL\Builder\Context\HandlerContextBuilderInfo;
+use LastDragon_ru\LaraASP\GraphQL\Builder\Context\HandlerContextOperators;
use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\Context;
use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\Operator as OperatorContract;
use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\TypeSource;
@@ -34,11 +35,6 @@ public function getTypeName(TypeSource $source, Context $context): string {
return "{$directiveName}{$builderName}{$name}{$typeName}";
}
- #[Override]
- protected function getScope(): string {
- return Directive::getScope();
- }
-
#[Override]
protected function getDescription(
Manipulator $manipulator,
@@ -94,13 +90,11 @@ protected function getFieldOperator(
InputFieldSource|ObjectFieldSource|InterfaceFieldSource $field,
Context $context,
): ?OperatorContract {
+ $provider = $context->get(HandlerContextOperators::class)?->value;
+ $operator = SortByOperatorSortDirective::class;
+
return match (true) {
- $field->isScalar(), $field->isEnum() => $manipulator->getOperator(
- SortByOperatorSortDirective::class,
- $this->getScope(),
- $field,
- $context,
- ),
+ $field->isScalar(), $field->isEnum() => $provider?->getOperator($manipulator, $operator, $field, $context),
$field->isObject() => parent::getFieldOperator($manipulator, $field, $context),
default => throw new NotImplemented($field),
};
diff --git a/packages/testing/src/Comparators/DatabaseQueryComparatorTest.php b/packages/testing/src/Comparators/DatabaseQueryComparatorTest.php
index 2af6a60ae..985d43b2a 100644
--- a/packages/testing/src/Comparators/DatabaseQueryComparatorTest.php
+++ b/packages/testing/src/Comparators/DatabaseQueryComparatorTest.php
@@ -55,7 +55,7 @@ public function normalize(Query $query): Query {
INNER JOIN b laravel_reserved_10 ON laravel_reserved_10.b = laravel_reserved_2.c
INNER JOIN c laravel_reserved_2 ON laravel_reserved_2.c = laravel_reserved_1.a
WHERE laravel_reserved_1.a IS NOT NULL
- and laravel_reserved_10.b IS NULL
+ AND laravel_reserved_10.b IS NULL
AND laravel_reserved_2.c > 10
SQL,
);
@@ -70,7 +70,7 @@ public function normalize(Query $query): Query {
INNER JOIN c laravel_reserved_1 ON laravel_reserved_1.c = laravel_reserved_0.a
WHERE
laravel_reserved_0.a IS NOT NULL
- and laravel_reserved_2.b IS NULL
+ AND laravel_reserved_2.b IS NULL
AND laravel_reserved_1.c > 10
SQL;