Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Builder Context #116

Merged
merged 8 commits into from
Jan 10, 2024
28 changes: 25 additions & 3 deletions packages/graphql/UPGRADE.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,30 @@ Please also see [changelog](https://github.com/LastDragon-ru/lara-asp/releases)

* [ ] `enum SortByTypeDirection { asc, desc }` => `enum SortByTypeDirection { Asc, Desc }`. 🤝

* [ ] `LastDragon_ru\LaraASP\GraphQL\SortBy\Builders\*` => `LastDragon_ru\LaraASP\GraphQL\SortBy\Sorters\*`.
* [ ] If you are testing generated queries, you need to update `sort_by_*` alias to `lara_asp_graphql__sort_by__*`.

* [ ] Update `LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\TypeDefinition` implementations.
## API

* [ ] If you are testing generated queries, you need to update `sort_by_*` alias to `lara_asp_graphql__sort_by__*`.
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\Handler`

* [ ] `LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\Operator`

* [ ] `LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\TypeDefinition`.

* [ ] `LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\TypeProvider`

* [ ] `LastDragon_ru\LaraASP\GraphQL\Builder\Manipulator` (removed `BuilderInfo`)

* [ ] `LastDragon_ru\LaraASP\GraphQL\Builder\Directives\HandlerDirective`

* [ ] `LastDragon_ru\LaraASP\GraphQL\Builder\Directives\PropertyDirective`

* [ ] `LastDragon_ru\LaraASP\GraphQL\SortBy\Builders\*` => `LastDragon_ru\LaraASP\GraphQL\SortBy\Sorters\*`

* [ ] To get `BuilderInfo` instance within Operator the `LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\Context` should be used instead of `LastDragon_ru\LaraASP\GraphQL\Builder\Manipulator`:

```php
$context->get(\LastDragon_ru\LaraASP\GraphQL\Builder\Contexts\AstManipulation\AstManipulation::class)?->builderInfo
```
46 changes: 46 additions & 0 deletions packages/graphql/src/Builder/Context.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?php declare(strict_types = 1);

namespace LastDragon_ru\LaraASP\GraphQL\Builder;

use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\Context as ContextContract;
use Override;

/**
* @internal
*/
class Context implements ContextContract {
/**
* @var array<class-string, object|null>
*/
private array $context = [];

public function __construct() {
// empty
}

#[Override]
public function has(string $key): bool {
return isset($this->context[$key]) && $this->context[$key] instanceof $key;
}

#[Override]
public function get(string $key): mixed {
return isset($this->context[$key]) && $this->context[$key] instanceof $key
? $this->context[$key]
: null;
}

/**
* @inheritDoc
*/
#[Override]
public function override(array $context): static {
$overridden = clone $this;

foreach ($context as $key => $value) {
$overridden->context[$key] = $value;
}

return $overridden;
}
}
13 changes: 13 additions & 0 deletions packages/graphql/src/Builder/Contexts/AstManipulation.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php declare(strict_types = 1);

namespace LastDragon_ru\LaraASP\GraphQL\Builder\Contexts;

use LastDragon_ru\LaraASP\GraphQL\Builder\BuilderInfo;

class AstManipulation {
public function __construct(
public readonly BuilderInfo $builderInfo,
) {
// empty
}
}
26 changes: 26 additions & 0 deletions packages/graphql/src/Builder/Contracts/Context.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php declare(strict_types = 1);

namespace LastDragon_ru\LaraASP\GraphQL\Builder\Contracts;

interface Context {
/**
* @param class-string $key
*/
public function has(string $key): bool;

/**
* @template T of object
*
* @param class-string<T> $key
*
* @return T|null
*/
public function get(string $key): mixed;

/**
* @template T of object
*
* @param array<class-string<T>, T|null> $context
*/
public function override(array $context): static;
}
2 changes: 1 addition & 1 deletion packages/graphql/src/Builder/Contracts/Handler.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,5 @@ interface Handler {
*
* @return TBuilder
*/
public function handle(object $builder, Property $property, ArgumentSet $conditions): object;
public function handle(object $builder, Property $property, ArgumentSet $conditions, Context $context): object;
}
10 changes: 8 additions & 2 deletions packages/graphql/src/Builder/Contracts/Operator.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ interface Operator extends Directive {
*/
public static function getName(): string;

public function getFieldType(TypeProvider $provider, TypeSource $source): string;
public function getFieldType(TypeProvider $provider, TypeSource $source, Context $context): string;

public function getFieldDescription(): string;

Expand All @@ -31,5 +31,11 @@ public function isBuilderSupported(string $builder): bool;
*
* @return TBuilder
*/
public function call(Handler $handler, object $builder, Property $property, Argument $argument): object;
public function call(
Handler $handler,
object $builder,
Property $property,
Argument $argument,
Context $context,
): object;
}
3 changes: 2 additions & 1 deletion packages/graphql/src/Builder/Contracts/TypeDefinition.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ interface TypeDefinition {
/**
* Returns the type name for given Builder and Source.
*/
public function getTypeName(Manipulator $manipulator, TypeSource $source): string;
public function getTypeName(TypeSource $source, Context $context): string;

/**
* Returns the type definition for given Source if possible. The name must be equal to `$name`.
Expand All @@ -22,6 +22,7 @@ public function getTypeName(Manipulator $manipulator, TypeSource $source): strin
public function getTypeDefinition(
Manipulator $manipulator,
TypeSource $source,
Context $context,
string $name,
): TypeDefinitionNode|Type|null;
}
2 changes: 1 addition & 1 deletion packages/graphql/src/Builder/Contracts/TypeProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ interface TypeProvider {
/**
* @param class-string<TypeDefinition> $definition
*/
public function getType(string $definition, TypeSource $source): string;
public function getType(string $definition, TypeSource $source, Context $context): string;

/**
* @param (TypeDefinitionNode&Node)|NamedTypeNode|ListTypeNode|NonNullTypeNode|Type $type
Expand Down
43 changes: 33 additions & 10 deletions packages/graphql/src/Builder/Directives/HandlerDirective.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
use Illuminate\Database\Query\Builder as QueryBuilder;
use Laravel\Scout\Builder as ScoutBuilder;
use LastDragon_ru\LaraASP\GraphQL\Builder\BuilderInfoDetector;
use LastDragon_ru\LaraASP\GraphQL\Builder\Context;
use LastDragon_ru\LaraASP\GraphQL\Builder\Contexts\AstManipulation;
use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\Context as ContextContract;
use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\Handler;
use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\Operator;
use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\Scope;
Expand All @@ -41,6 +44,9 @@
use function is_array;
use function reset;

/**
* @see AstManipulation
*/
abstract class HandlerDirective extends BaseDirective implements Handler {
use WithManipulator;
use WithSource;
Expand Down Expand Up @@ -87,7 +93,7 @@ public function handleScoutBuilder(ScoutBuilder $builder, mixed $value): ScoutBu
*
* @return T
*/
protected function handleAnyBuilder(object $builder, mixed $value): object {
protected function handleAnyBuilder(object $builder, mixed $value, ContextContract $context = null): object {
if ($value !== null && $this->definitionNode instanceof InputValueDefinitionNode) {
$argument = !($value instanceof Argument)
? $this->getFactory()->getArgument($this->definitionNode, $value)
Expand All @@ -99,7 +105,7 @@ protected function handleAnyBuilder(object $builder, mixed $value): object {

foreach ($conditions as $condition) {
if ($condition instanceof ArgumentSet) {
$builder = $this->handle($builder, new Property(), $condition);
$builder = $this->handle($builder, new Property(), $condition, $context ?? new Context());
} else {
throw new HandlerInvalidConditions($this);
}
Expand All @@ -117,7 +123,12 @@ protected function handleAnyBuilder(object $builder, mixed $value): object {
* @return T
*/
#[Override]
public function handle(object $builder, Property $property, ArgumentSet $conditions): object {
public function handle(
object $builder,
Property $property,
ArgumentSet $conditions,
ContextContract $context,
): object {
// Empty?
if (count($conditions->arguments) === 0) {
return $builder;
Expand All @@ -131,7 +142,7 @@ public function handle(object $builder, Property $property, ArgumentSet $conditi
}

// Call
return $this->call($builder, $property, $conditions);
return $this->call($builder, $property, $conditions, $context);
}

/**
Expand All @@ -141,7 +152,12 @@ public function handle(object $builder, Property $property, ArgumentSet $conditi
*
* @return T
*/
protected function call(object $builder, Property $property, ArgumentSet $operator): object {
protected function call(
object $builder,
Property $property,
ArgumentSet $operator,
ContextContract $context,
): object {
// Arguments?
if (count($operator->arguments) > 1) {
throw new ConditionTooManyOperators(
Expand Down Expand Up @@ -184,7 +200,7 @@ static function (Operator $operator): string {
}

// Return
return $op->call($this, $builder, $property, $value);
return $op->call($this, $builder, $property, $value, $context);
}
// </editor-fold>

Expand All @@ -199,15 +215,20 @@ public function manipulateArgDefinition(
// Converted?
$detector = Container::getInstance()->make(BuilderInfoDetector::class);
$builder = $detector->getFieldArgumentBuilderInfo($documentAST, $parentType, $parentField, $argDefinition);
$manipulator = $this->getManipulator($documentAST, $builder);
$manipulator = $this->getAstManipulator($documentAST);

if ($this->isTypeName($manipulator->getTypeName($argDefinition))) {
return;
}

// Argument
$source = $this->getFieldArgumentSource($manipulator, $parentType, $parentField, $argDefinition);
$type = $this->getArgDefinitionType($manipulator, $documentAST, $source);
$context = (new Context())->override([
AstManipulation::class => new AstManipulation(
builderInfo: $builder,
),
]);
$source = $this->getFieldArgumentSource($manipulator, $parentType, $parentField, $argDefinition);
$type = $this->getArgDefinitionType($manipulator, $documentAST, $source, $context);

$manipulator->setArgumentType(
$parentType,
Expand All @@ -226,6 +247,7 @@ abstract protected function getArgDefinitionType(
Manipulator $manipulator,
DocumentAST $document,
ObjectFieldArgumentSource|InterfaceFieldArgumentSource $argument,
ContextContract $context,
): ListTypeNode|NamedTypeNode|NonNullTypeNode;

/**
Expand All @@ -236,6 +258,7 @@ protected function getArgumentTypeDefinitionNode(
DocumentAST $document,
ObjectFieldArgumentSource|InterfaceFieldArgumentSource $argument,
string $operator,
ContextContract $context,
): ListTypeNode|NamedTypeNode|NonNullTypeNode|null {
$type = null;
$definition = $manipulator->isPlaceholder($argument->getArgument())
Expand All @@ -245,7 +268,7 @@ protected function getArgumentTypeDefinitionNode(
if ($definition) {
$operator = $manipulator->getOperator(static::getScope(), $operator);
$node = $manipulator->getTypeSource($definition);
$type = $operator->getFieldType($manipulator, $node);
$type = $operator->getFieldType($manipulator, $node, $context);
$type = Parser::typeReference($type);
}

Expand Down
13 changes: 10 additions & 3 deletions packages/graphql/src/Builder/Directives/PropertyDirective.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace LastDragon_ru\LaraASP\GraphQL\Builder\Directives;

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;
Expand All @@ -23,7 +24,7 @@ public static function getName(): string {
}

#[Override]
public function getFieldType(TypeProvider $provider, TypeSource $source): string {
public function getFieldType(TypeProvider $provider, TypeSource $source, Context $context): string {
return $source->getTypeName();
}

Expand All @@ -33,7 +34,13 @@ public function isBuilderSupported(string $builder): bool {
}

#[Override]
public function call(Handler $handler, object $builder, Property $property, Argument $argument): object {
public function call(
Handler $handler,
object $builder,
Property $property,
Argument $argument,
Context $context,
): object {
if (!($argument->value instanceof ArgumentSet)) {
throw new HandlerInvalidConditions($handler);
}
Expand All @@ -51,6 +58,6 @@ public function call(Handler $handler, object $builder, Property $property, Argu
}

// Apply
return $handler->handle($builder, $property, $argument->value);
return $handler->handle($builder, $property, $argument->value, $context);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace LastDragon_ru\LaraASP\GraphQL\Builder\Exceptions;

use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\Context;
use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\TypeDefinition;
use Stringable;
use Throwable;
Expand All @@ -14,14 +15,15 @@ class TypeDefinitionImpossibleToCreateType extends BuilderException {
*/
public function __construct(
protected string $definition,
protected Stringable|string|null $source,
protected Stringable|string $source,
protected Context $context,
Throwable $previous = null,
) {
parent::__construct(
sprintf(
'Definition `%s`: Impossible to create type for `%s`.',
$this->definition,
$this->source ?: 'null',
$this->source,
),
$previous,
);
Expand All @@ -31,7 +33,11 @@ public function getDefinition(): string {
return $this->definition;
}

public function getSource(): Stringable|string|null {
public function getSource(): Stringable|string {
return $this->source;
}

public function getContext(): Context {
return $this->context;
}
}
Loading
Loading