Skip to content

Commit

Permalink
refactor(graphql)!: Scalars renamed to Operators and moved into `…
Browse files Browse the repository at this point in the history
…Builder`.
  • Loading branch information
LastDragon-ru committed Aug 27, 2022
2 parents 43b8046 + 3b2106f commit e1987fb
Show file tree
Hide file tree
Showing 25 changed files with 714 additions and 550 deletions.
28 changes: 14 additions & 14 deletions packages/graphql/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,16 +129,16 @@ query {
```


## Scalars
## Config

In addition to standard GraphQL scalars the package defines few own:
In addition to standard GraphQL types the package defines few own:

* `LastDragon_ru\\LaraASP\\GraphQL\\SearchBy\\Directives\\Directive::ScalarNumber` - any operator for this scalar will be available for `Int` and `Float`;
* `LastDragon_ru\\LaraASP\\GraphQL\\SearchBy\\Directives\\Directive::ScalarNull` - additional operators available for nullable scalars;
* `LastDragon_ru\\LaraASP\\GraphQL\\SearchBy\\Directives\\Directive::ScalarLogic` - list of logical operators, please see below;
* `LastDragon_ru\\LaraASP\\GraphQL\\SearchBy\\Directives\\Directive::ScalarEnum` - default operators for enums;
* `LastDragon_ru\LaraASP\GraphQL\SearchBy\Operators::Number` - any operator for this type will be available for `Int` and `Float`;
* `LastDragon_ru\LaraASP\GraphQL\SearchBy\Operators::Null` - additional operators available for nullable types;
* `LastDragon_ru\LaraASP\GraphQL\SearchBy\Operators::Logical` - list of logical operators, please see below;
* `LastDragon_ru\LaraASP\GraphQL\SearchBy\Operators::Enum` - default operators for enums;

To work with custom scalars you need to configure supported operators for each of them. First, you need to publish package config:
To work with custom types you need to configure supported operators for each of them. First, you need to publish package config:

```shell
php artisan vendor:publish --provider=LastDragon_ru\\LaraASP\\GraphQL\\Provider --tag=config
Expand All @@ -165,15 +165,15 @@ return [
*/
'search_by' => [
/**
* Scalars
* Operators
* ---------------------------------------------------------------------
*
* You can (re)define scalars and supported operators here.
* You can (re)define types and supported operators here.
*
* @var array<string, array<class-string<\LastDragon_ru\LaraASP\GraphQL\SearchBy\Contracts\Operator>>>
* @var array<string, array<class-string<Operator>>>
*/
'scalars' => [
// You can define a list of operators for each Scalar
'operators' => [
// You can define a list of operators for each type
'Date' => [
Equal::class,
Between::class,
Expand All @@ -195,8 +195,8 @@ return [

There are three types of operators:

* Comparison - used to compare column with value(s), eg `{equal: "value"}`, `{lt: 2}`, etc. To add your own you just need to implement [`Operator`](./src/SearchBy/Contracts/Operator.php) and add it to scalar(s);
* Logical - used to group comparisons into groups, eg `anyOf([{equal: "a"}, {equal: "b"}])`. Adding your own is the same: implement [`Operator`](./src/SearchBy/Contracts/Operator.php) and add it to `Directive::ScalarLogic` scalar;
* Comparison - used to compare column with value(s), eg `{equal: "value"}`, `{lt: 2}`, etc. To add your own you just need to implement [`Operator`](./src/SearchBy/Contracts/Operator.php) and add it to type(s);
* Logical - used to group comparisons into groups, eg `anyOf([{equal: "a"}, {equal: "b"}])`. Adding your own is the same: implement [`Operator`](./src/SearchBy/Contracts/Operator.php) and add it to `Operators::Logical` type;
* Complex - used to create conditions for nested Input types and allow implement any logic eg `whereHas`, `whereDoesntHave`, etc. These operators must implement [`ComplexOperator`](./src/SearchBy/Contracts/ComplexOperator.php) (by default the [`Relation`](./src/SearchBy/Operators/Complex/Relation.php) operator will be used, you can use it as example):

```graphql
Expand Down
9 changes: 5 additions & 4 deletions packages/graphql/config/config.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<?php declare(strict_types = 1);

use LastDragon_ru\LaraASP\Core\Utils\ConfigMerger;
use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\Operator;

/**
* -----------------------------------------------------------------------------
Expand All @@ -13,14 +14,14 @@
*/
'search_by' => [
/**
* Scalars
* Operators
* ---------------------------------------------------------------------
*
* You can (re)define scalars and supported operators here.
* You can (re)define types and supported operators here.
*
* @var array<string, array<class-string<\LastDragon_ru\LaraASP\GraphQL\SearchBy\Contracts\Operator>>>
* @var array<string, array<class-string<Operator>>>
*/
'scalars' => [
'operators' => [
// This value has no effect inside the published config.
ConfigMerger::Replace => true,
],
Expand Down
5 changes: 3 additions & 2 deletions packages/graphql/src/Builder/Contracts/TypeDefinition.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,18 @@

namespace LastDragon_ru\LaraASP\GraphQL\Builder\Contracts;

use GraphQL\Language\AST\Node;
use GraphQL\Language\AST\TypeDefinitionNode;

interface TypeDefinition {
public static function getName(): string;

/**
* @return (TypeDefinitionNode&\GraphQL\Language\AST\Node)|null
* @return (TypeDefinitionNode&Node)|null
*/
public function getTypeDefinitionNode(
string $name,
string $scalar = null,
string $type = null,
bool $nullable = null,
): ?TypeDefinitionNode;
}
4 changes: 2 additions & 2 deletions packages/graphql/src/Builder/Contracts/TypeProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

interface TypeProvider {
/**
* @param class-string<TypeDefinition> $type
* @param class-string<TypeDefinition> $definition
*/
public function getType(string $type, string $scalar = null, bool $nullable = null): string;
public function getType(string $definition, string $type = null, bool $nullable = null): string;
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,15 @@ class TypeDefinitionImpossibleToCreateType extends BuilderException {
*/
public function __construct(
protected string $definition,
protected ?string $scalar,
protected ?string $type,
protected ?bool $nullable,
Throwable $previous = null,
) {
parent::__construct(
sprintf(
'Definition `%s`: Impossible to create type for scalar `%s`.',
'Definition `%s`: Impossible to create type for type `%s`.',
$this->definition,
($this->scalar ?: 'null').($this->nullable ? '' : '!'),
($this->type ?: 'null').($this->nullable ? '' : '!'),
),
$previous,
);
Expand All @@ -31,8 +31,8 @@ public function getDefinition(): string {
return $this->definition;
}

public function getScalar(): ?string {
return $this->scalar;
public function getType(): ?string {
return $this->type;
}

public function isNullable(): ?bool {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
<?php declare(strict_types = 1);

namespace LastDragon_ru\LaraASP\GraphQL\SearchBy\Exceptions;
namespace LastDragon_ru\LaraASP\GraphQL\Builder\Exceptions;

use Throwable;

use function sprintf;

class ScalarNoOperators extends SearchByException {
class TypeNoOperators extends BuilderException {
public function __construct(
protected string $name,
Throwable $previous = null,
) {
parent::__construct(sprintf(
'List of operators for scalar `%s` cannot be empty.',
'List of operators for type `%s` cannot be empty.',
$this->name,
), $previous);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
<?php declare(strict_types = 1);

namespace LastDragon_ru\LaraASP\GraphQL\SearchBy\Exceptions;
namespace LastDragon_ru\LaraASP\GraphQL\Builder\Exceptions;

use Throwable;

use function sprintf;

class ScalarUnknown extends SearchByException {
class TypeUnknown extends BuilderException {
public function __construct(
protected string $name,
Throwable $previous = null,
) {
parent::__construct(sprintf(
'Scalar `%s` is not defined.',
'Type `%s` is not defined.',
$this->name,
), $previous);
}
Expand Down
20 changes: 10 additions & 10 deletions packages/graphql/src/Builder/Manipulator.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,34 +42,34 @@ protected function getContainer(): Container {

// <editor-fold desc="TypeProvider">
// =========================================================================
public function getType(string $type, string $scalar = null, bool $nullable = null): string {
public function getType(string $definition, string $type = null, bool $nullable = null): string {
// Exists?
$name = $this->getTypeName($type::getName(), $scalar, $nullable);
$name = $this->getTypeName($definition::getName(), $type, $nullable);

if ($this->isTypeDefinitionExists($name)) {
return $name;
}

// Create new
$instance = $this->getContainer()->make($type);
$definition = $instance->getTypeDefinitionNode($name, $scalar, $nullable);
$instance = $this->getContainer()->make($definition);
$node = $instance->getTypeDefinitionNode($name, $type, $nullable);

if (!$definition) {
throw new TypeDefinitionImpossibleToCreateType($type, $scalar, $nullable);
if (!$node) {
throw new TypeDefinitionImpossibleToCreateType($definition, $type, $nullable);
}

if ($name !== $this->getNodeName($definition)) {
throw new TypeDefinitionInvalidTypeName($type, $name, $this->getNodeName($definition));
if ($name !== $this->getNodeName($node)) {
throw new TypeDefinitionInvalidTypeName($definition, $name, $this->getNodeName($node));
}

// Save
$this->addTypeDefinition($definition);
$this->addTypeDefinition($node);

// Return
return $name;
}

abstract protected function getTypeName(string $name, string $scalar = null, bool $nullable = null): string;
abstract protected function getTypeName(string $name, string $type = null, bool $nullable = null): string;
// </editor-fold>

// <editor-fold desc="Operators">
Expand Down
117 changes: 117 additions & 0 deletions packages/graphql/src/Builder/Operators.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
<?php declare(strict_types = 1);

namespace LastDragon_ru\LaraASP\GraphQL\Builder;

use GraphQL\Type\Definition\Type;
use Illuminate\Container\Container;
use LastDragon_ru\LaraASP\GraphQL\Builder\Contracts\Operator;
use LastDragon_ru\LaraASP\GraphQL\Builder\Exceptions\TypeNoOperators;
use LastDragon_ru\LaraASP\GraphQL\Builder\Exceptions\TypeUnknown;

use function array_map;
use function array_merge;
use function array_push;
use function array_unique;
use function array_values;
use function is_array;
use function is_string;

use const SORT_REGULAR;

abstract class Operators {
public const ID = Type::ID;
public const Int = Type::INT;
public const Float = Type::FLOAT;
public const String = Type::STRING;
public const Boolean = Type::BOOLEAN;
public const Null = 'Null';

/**
* Determines default operators available for each type.
*
* @var array<string, array<class-string<Operator>>|string>
*/
protected array $operators = [];

/**
* Determines additional operators available for type.
*
* @var array<string, string>
*/
protected array $extends = [];

public function __construct(
private Container $container,
) {
// empty
}

protected function getContainer(): Container {
return $this->container;
}

public function hasOperators(string $type): bool {
return isset($this->operators[$type]);
}

/**
* @param array<class-string<Operator>>|string $operators
*/
public function addOperators(string $type, array|string $operators): void {
if (is_string($operators) && !$this->hasOperators($operators)) {
throw new TypeUnknown($operators);
}

if (is_array($operators) && !$operators) {
throw new TypeNoOperators($type);
}

$this->operators[$type] = $operators;
}

/**
* @return array<Operator>
*/
public function getOperators(string $type, bool $nullable): array {
// Is known?
if (!$this->hasOperators($type)) {
throw new TypeUnknown($type);
}

// Base
$base = $type;
$operators = $type;

do {
$operators = $this->operators[$operators] ?? [];
$isAlias = !is_array($operators);

if ($isAlias) {
$base = $operators;
}
} while ($isAlias);

// Create Instances
$container = $this->getContainer();
$operators = array_map(static function (string $operator) use ($container): Operator {
return $container->make($operator);
}, array_unique($operators));

// Extends
if (isset($this->extends[$base])) {
$extends = $this->getOperators($this->extends[$base], $nullable);
$operators = array_merge($operators, $extends);
}

// Add `null` for nullable
if ($nullable) {
array_push($operators, ...$this->getOperators(static::Null, false));
}

// Cleanup
$operators = array_values(array_unique($operators, SORT_REGULAR));

// Return
return $operators;
}
}
Loading

0 comments on commit e1987fb

Please sign in to comment.