This example serves as a simple tutorial on how to declare and use constraints on fields, arguments and objects.
This example depends on optional package
infinityloop-dev/graphpinator-constraint-directives
.
In this example, we define a simple schema with a few constraints - custom directives that apply value constraints to fields, arguments and types. You should be familiar with our previous HelloWorld example to understand the basics.
Here we define one Query
object type with some leaf constraints and one AOrB
object type with object constraint.
In this specific example, we assign
fieldConstraint
field, which specifies that returned string is at least 5 characters long,arg
argument on argumentConstraint
field, which specifies that inputed int must be >= 10,AOrB
type, which specifies that either fieldA or fieldB contains a value.<?php
declare(strict_types = 1);
namespace Example;
final class Query extends \Graphpinator\Typesystem\Type
{
protected const NAME = 'Query';
protected const DESCRIPTION = 'Graphpinator Constraints: Query type';
public function __construct(
private \Graphpinator\ConstraintDirectives\StringConstraintDirective $stringConstraint,
private \Graphpinator\ConstraintDirectives\IntConstraintDirective $intConstraint,
)
{
parent::__construct();
}
public function validateNonNullValue($rawValue) : bool
{
return true;
}
protected function getFieldDefinition() : \Graphpinator\Typesystem\Field\ResolvableFieldSet
{
return new \Graphpinator\Typesystem\Field\ResolvableFieldSet([
\Graphpinator\Typesystem\Field\ResolvableField::create(
'fieldConstraint',
\Graphpinator\Typesystem\Container::String()->notNull(),
function ($parent) : string {
return 'Hello world';
},
)->addDirective($this->stringConstraint, ['minLength' => 5]),
\Graphpinator\Typesystem\Field\ResolvableField::create(
'argumentConstraint',
\Graphpinator\Typesystem\Container::Int()->notNull(),
function ($parent, int $arg) : int {
return $arg;
},
)->setArguments(
new \Graphpinator\Typesystem\Argument\ArgumentSet([
\Graphpinator\Typesystem\Argument\Argument::create(
'arg',
\Graphpinator\Typesystem\Container::Int()->notNull(),
)->addDirective($this->intConstraint, ['min' => 10]),
])
),
]);
}
}
final class AOrB extends \Graphpinator\Typesystem\Type
{
protected const NAME = 'AOrB';
protected const DESCRIPTION = 'Graphpinator Constraints: AOrB type';
public function __construct(
private \Graphpinator\ConstraintDirectives\ObjectConstraintDirective $objectConstraint,
)
{
parent::__construct();
$this->addDirective($objectConstraint, ['exactlyOne' => ['fieldA', 'fieldB']]);
}
public function validateNonNullValue($rawValue) : bool
{
return \is_int($rawValue) && \in_array($rawValue, [0, 1], true);
}
protected function getFieldDefinition() : \Graphpinator\Typesystem\Field\ResolvableFieldSet
{
return new \Graphpinator\Typesystem\Field\ResolvableFieldSet([
\Graphpinator\Typesystem\Field\ResolvableField::create(
'fieldA',
\Graphpinator\Typesystem\Container::Int(),
function (?int $parent) : ?int {
return $parent === 1 ? 1 : null;
},
),
\Graphpinator\Typesystem\Field\ResolvableField::create(
'fieldB',
\Graphpinator\Typesystem\Container::Int(),
function (int $parent) : ?int {
return $parent === 0 ? 1 : null;
},
),
]);
}
}
Make sure to select Constraint corresponding to its usage e.g.
Visualize our GraphQL schema in type language.
Declaration of
Container
,Schema
andGraphpinator
classes is skipped in this example. Visit our HelloWorld example for more information.
Printing the schema using infinityloop-dev/graphpinator-printer
produces following schema.
schema {
query: Query
mutation: null
subscription: null
}
"""
Graphpinator Constraints: AOrB type
"""
type AOrB @objectConstraint(exactlyOne: ["fieldA", "fieldB"]) {
fieldA: Int
fieldB: Int
}
"""
Graphpinator Constraints: Query type
"""
type Query {
fieldConstraint: String! @stringConstraint(minLength: 5)
argumentConstraint(
arg: Int! @intConstraint(min: 10)
): Int!
}
This is the end of the Constraints example, thank you for reading this far.