Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion docs/computed-fields.rst
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,11 @@ GraphQL arguments are named, so the argument is ``year``, not a position.
* A variadic parameter, or one passed by reference, cannot be an argument.

These are checked when the metadata is built, and throw
``ApiSkeletons\Doctrine\ORM\GraphQL\Exception\Metadata``.
``ApiSkeletons\Doctrine\ORM\GraphQL\Exception\Metadata``. When the type
is built, an argument's type must be one which can be input, and a default
must be a value of it: ``'2020-01-01'`` is not a value of ``date_immutable``,
whose values are dates, so a parameter given that type in ``args`` may not
have it as its default. These throw the same exception.

Each set of arguments has its own value, so aliases of a field with
different arguments, such as ``a: totalRecordings(year: 2002)
Expand Down
35 changes: 35 additions & 0 deletions src/Type/Entity/Entity.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,14 @@
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Mapping\MappingException;
use GraphQL\Type\Definition\InputType;
use GraphQL\Type\Definition\LeafType;
use GraphQL\Type\Definition\NullableType;
use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\Type;
use Laminas\Hydrator\HydratorInterface;
use League\Event\EventDispatcher;
use ReflectionClass;
use Throwable;

use function array_keys;
use function array_merge;
Expand All @@ -44,6 +46,7 @@
use function str_starts_with;
use function substr;
use function ucwords;
use function var_export;

/**
* This class is used to build an ObjectType for an entity
Expand Down Expand Up @@ -492,12 +495,44 @@ private function computedFieldArgs(string $fieldName, ComputedFieldMetadata $com
continue;
}

// A default is a value of the argument's type, which introspection
// serializes; the method receives it as a value of the type
if (! self::isValueOf($type, $argument->default)) {
throw new MetadataException(
'Argument ' . $name . ' of computed field ' . $fieldName . ' of entity ' . $this->getEntityClass()
. ' has the default value ' . var_export($argument->default, true) . ', which is not a value of its '
. 'type ' . $type->toString() . '. Remove the default, or give the parameter a type of which '
. 'it is a value.',
);
}

$args[$name]['defaultValue'] = $argument->default;
}

return ['args' => $args];
}

/**
* Whether a scalar is a value of a type: the type is a scalar or an enum,
* not a list or an input object, and serializes the value
*/
private static function isValueOf(Type $type, int|float|string|bool $value): bool
{
$type = Type::getNullableType($type);

if (! $type instanceof LeafType) {
return false;
}

try {
$type->serialize($value);
} catch (Throwable) {
return false;
}

return true;
}

/**
* The type of a computed field: its type, or a list of it
*/
Expand Down
16 changes: 16 additions & 0 deletions test/Entity/ComputedArgsInvalid.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
#[GraphQL\Entity(group: 'ComputedArgsObjectDefault')]
#[GraphQL\Entity(group: 'ComputedArgsUnknownArg')]
#[GraphQL\Entity(group: 'ComputedArgsNotInput')]
#[GraphQL\Entity(group: 'ComputedArgsTypedDefault')]
#[GraphQL\Entity(group: 'ComputedArgsListDefault')]
#[ORM\Entity]
class ComputedArgsInvalid
{
Expand Down Expand Up @@ -75,4 +77,18 @@ public function getNotInput(mixed $page): int
{
return 0;
}

/** A default which is not a value of the type its args give it */
#[GraphQL\ComputedField(type: 'string', group: 'ComputedArgsTypedDefault', args: ['since' => 'date_immutable'])]
public function getTypedDefault(mixed $since = '2020-01-01'): string
{
return (string) $since;
}

/** A scalar default for a list */
#[GraphQL\ComputedField(type: 'int', group: 'ComputedArgsListDefault', args: ['tags' => 'simple_array'])]
public function getListDefault(mixed $tags = 'live'): int
{
return 0;
}
}
11 changes: 11 additions & 0 deletions test/Feature/ComputedField/ComputedFieldArgumentsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,17 @@ public static function invalidProvider(): array
'The args of the ComputedField attribute of computed field method getUnknownArg of entity '
. ComputedArgsInvalid::class . ' give the type of other, which is not a parameter of the method.',
],
'default not a value of the given type' => [
'ComputedArgsTypedDefault',
'Argument since of computed field typedDefault of entity ' . ComputedArgsInvalid::class
. " has the default value '2020-01-01', which is not a value of its type DateImmutable. Remove "
. 'the default, or give the parameter a type of which it is a value.',
],
'scalar default for a list' => [
'ComputedArgsListDefault',
'Argument tags of computed field listDefault of entity ' . ComputedArgsInvalid::class
. " has the default value 'live', which is not a value of its type [String].",
],
'not an input type' => [
'ComputedArgsNotInput',
'Argument page of computed field notInput of entity ' . ComputedArgsInvalid::class
Expand Down
Loading