Skip to content

Commit f1f4db1

Browse files
authored
Merge pull request #117 from TomHAnderson/feature/between-filter-input-type
Cleared InvalidArguments
2 parents 91b103d + 18cc49c commit f1f4db1

6 files changed

Lines changed: 106 additions & 84 deletions

File tree

src/Criteria/CriteriaFactory.php

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
namespace ApiSkeletons\Doctrine\GraphQL\Criteria;
66

77
use ApiSkeletons\Doctrine\GraphQL\Config;
8-
use ApiSkeletons\Doctrine\GraphQL\Criteria\Type\FiltersInputType;
8+
use ApiSkeletons\Doctrine\GraphQL\Criteria\Type\FiltersInputObjectType;
99
use ApiSkeletons\Doctrine\GraphQL\Type\Entity;
1010
use ApiSkeletons\Doctrine\GraphQL\Type\TypeManager;
1111
use Doctrine\ORM\EntityManager;
@@ -16,7 +16,6 @@
1616

1717
use function array_filter;
1818
use function array_keys;
19-
use function assert;
2019
use function count;
2120
use function in_array;
2221

@@ -72,12 +71,9 @@ public function get(
7271
continue;
7372
}
7473

75-
$fieldMetadata = $classMetadata->getFieldMapping($fieldName);
76-
$graphQLType = $this->typeManager
74+
$graphQLType = $this->typeManager
7775
->get($entityMetadata['fields'][$fieldName]['type']);
7876

79-
assert($graphQLType, 'GraphQL type not found for ' . $fieldMetadata['type']);
80-
8177
if ($classMetadata->isIdentifier($fieldName)) {
8278
$graphQLType = Type::id();
8379
}
@@ -98,7 +94,7 @@ static function ($value) use ($fieldExcludeCriteria) {
9894

9995
$fields[$fieldName] = [
10096
'name' => $fieldName,
101-
'type' => new FiltersInputType($typeName, $fieldName, $graphQLType, $allowedFilters),
97+
'type' => new FiltersInputObjectType($typeName, $fieldName, $graphQLType, $allowedFilters),
10298
'description' => 'Filters for ' . $fieldName,
10399
];
104100
}
@@ -120,7 +116,7 @@ static function ($value) use ($fieldExcludeCriteria) {
120116
if (in_array(Filters::EQ, $allowedFilters)) {
121117
$fields[$associationName] = [
122118
'name' => $associationName,
123-
'type' => new FiltersInputType($typeName, $associationName, $graphQLType, ['eq']),
119+
'type' => new FiltersInputObjectType($typeName, $associationName, $graphQLType, ['eq']),
124120
'description' => 'Filters for ' . $associationName,
125121
];
126122
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace ApiSkeletons\Doctrine\GraphQL\Criteria\Type;
6+
7+
use ApiSkeletons\Doctrine\GraphQL\Criteria\Filters as FiltersDef;
8+
use GraphQL\Type\Definition\InputObjectField;
9+
use GraphQL\Type\Definition\InputObjectType;
10+
use GraphQL\Type\Definition\ListOfType;
11+
use GraphQL\Type\Definition\ScalarType;
12+
13+
class BetweenInputObjectType extends InputObjectType
14+
{
15+
public function __construct(string $typeName, string $fieldName, ScalarType|ListOfType $type)
16+
{
17+
$fields = [
18+
'from' => new InputObjectField([
19+
'name' => 'from',
20+
'type' => $type,
21+
'description' => 'Low value of between',
22+
]),
23+
'to' => new InputObjectField([
24+
'name' => 'to',
25+
'type' => $type,
26+
'description' => 'High value of between',
27+
]),
28+
];
29+
30+
parent::__construct([
31+
'name' => $typeName
32+
. '_' . $fieldName
33+
. '_filters_'
34+
. FiltersDef::BETWEEN
35+
. '_fields',
36+
'description' => 'Between `from` and `to`',
37+
'fields' => static fn () => $fields,
38+
]);
39+
}
40+
}

src/Criteria/Type/FiltersInputType.php renamed to src/Criteria/Type/FiltersInputObjectType.php

Lines changed: 5 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,14 @@
66

77
use ApiSkeletons\Doctrine\GraphQL\Criteria\Filters as FiltersDef;
88
use GraphQL\Type\Definition\InputObjectType;
9+
use GraphQL\Type\Definition\ListOfType;
10+
use GraphQL\Type\Definition\ScalarType;
911
use GraphQL\Type\Definition\Type;
1012

11-
class FiltersInputType extends InputObjectType
13+
class FiltersInputObjectType extends InputObjectType
1214
{
1315
/** @param string[] $allowedFilters */
14-
public function __construct(string $typeName, string $fieldName, Type $type, array $allowedFilters)
16+
public function __construct(string $typeName, string $fieldName, ScalarType|ListOfType $type, array $allowedFilters)
1517
{
1618
$fields = [];
1719
$descriptions = FiltersDef::getDescriptions();
@@ -27,7 +29,7 @@ public function __construct(string $typeName, string $fieldName, Type $type, arr
2729
$filterType = Type::boolean();
2830
break;
2931
case FiltersDef::BETWEEN:
30-
$filterType = $this->buildBetweenInputObject(
32+
$filterType = new BetweenInputObjectType(
3133
$typeName,
3234
$fieldName,
3335
$type,
@@ -54,39 +56,10 @@ public function __construct(string $typeName, string $fieldName, Type $type, arr
5456
];
5557
}
5658

57-
/** @psalm-suppress InvalidArgument */
5859
parent::__construct([
5960
'name' => $typeName . '_' . $fieldName . '_filters',
6061
'description' => 'Field filters',
6162
'fields' => static fn () => $fields,
6263
]);
6364
}
64-
65-
private function buildBetweenInputObject(
66-
string $typeName,
67-
string $fieldName,
68-
Type $type,
69-
): InputObjectType {
70-
/** @psalm-suppress InvalidArgument */
71-
return new InputObjectType([
72-
'name' => $typeName
73-
. '_' . $fieldName
74-
. '_filters_'
75-
. FiltersDef::BETWEEN
76-
. '_fields',
77-
'fields' => [
78-
'from' => [
79-
'name' => 'from',
80-
'type' => $type,
81-
'description' => 'Low value of between',
82-
],
83-
'to' => [
84-
'name' => 'to',
85-
'type' => $type,
86-
'description' => 'High value of between',
87-
],
88-
],
89-
'description' => 'Between `from` and `to`',
90-
]);
91-
}
9265
}

src/Input/InputFactory.php

Lines changed: 50 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
use Doctrine\ORM\EntityManager;
1212
use Exception;
1313
use GraphQL\Error\Error;
14+
use GraphQL\Type\Definition\InputObjectField;
1415
use GraphQL\Type\Definition\InputObjectType;
1516
use GraphQL\Type\Definition\Type;
1617

@@ -36,58 +37,63 @@ public function __construct(
3637
*/
3738
public function get(string $id, array $requiredFields = [], array $optionalFields = []): InputObjectType
3839
{
40+
$fields = [];
3941
$targetEntity = $this->metadata->get($id);
4042

41-
/** @psalm-suppress InvalidArgument */
42-
return new InputObjectType([
43-
'name' => $targetEntity->getTypeName() . '_Input',
44-
'description' => $targetEntity->getDescription(),
45-
'fields' => function () use ($id, $targetEntity, $requiredFields, $optionalFields): array {
46-
$fields = [];
47-
48-
foreach ($this->entityManager->getClassMetadata($id)->getFieldNames() as $fieldName) {
49-
/**
50-
* Do not include identifiers as input. In the majority of cases there will be
51-
* no reason to set or update an identifier. For the case where an identifier
52-
* should be set or updated, this facotry is not the correct solution.
53-
*/
54-
if (! empty($optionalFields) || ! empty($requiredFields)) {
55-
// Include field as optional
56-
if (in_array($fieldName, $optionalFields) || $optionalFields === ['*']) {
57-
if ($optionalFields === ['*'] && $this->entityManager->getClassMetadata($id)->isIdentifier($fieldName)) {
58-
continue;
59-
}
60-
61-
$fields[$fieldName]['description'] = $targetEntity->getMetadataConfig()['fields'][$fieldName]['description'];
62-
$fields[$fieldName]['type'] = $this->typeManager->get($targetEntity->getMetadataConfig()['fields'][$fieldName]['type']);
63-
}
64-
65-
// Include field as required
66-
if (in_array($fieldName, $requiredFields) || $requiredFields === ['*']) {
67-
if ($requiredFields === ['*'] && $this->entityManager->getClassMetadata($id)->isIdentifier($fieldName)) {
68-
continue;
69-
}
43+
foreach ($this->entityManager->getClassMetadata($id)->getFieldNames() as $fieldName) {
44+
/**
45+
* Do not include identifiers as input. In the majority of cases there will be
46+
* no reason to set or update an identifier. For the case where an identifier
47+
* should be set or updated, this facotry is not the correct solution.
48+
*/
49+
if (! empty($optionalFields) || ! empty($requiredFields)) {
50+
// Include field as optional
51+
if (in_array($fieldName, $optionalFields) || $optionalFields === ['*']) {
52+
if ($optionalFields === ['*'] && $this->entityManager->getClassMetadata($id)->isIdentifier($fieldName)) {
53+
continue;
54+
}
7055

71-
if ($this->entityManager->getClassMetadata($id)->isIdentifier($fieldName)) {
72-
throw new Exception('Identifier ' . $fieldName . ' is an invalid input.');
73-
}
56+
$fields[$fieldName] = new InputObjectField([
57+
'name' => $fieldName,
58+
'description' => (string) $targetEntity->getMetadataConfig()['fields'][$fieldName]['description'],
59+
'type' => $this->typeManager->get($targetEntity->getMetadataConfig()['fields'][$fieldName]['type']),
60+
]);
61+
}
7462

75-
$fields[$fieldName]['description'] = $targetEntity->getMetadataConfig()['fields'][$fieldName]['description'];
76-
$fields[$fieldName]['type'] = Type::nonNull($this->typeManager->get($targetEntity->getMetadataConfig()['fields'][$fieldName]['type']));
77-
}
78-
} else {
79-
// All fields are required
80-
if ($this->entityManager->getClassMetadata($id)->isIdentifier($fieldName)) {
81-
continue;
82-
}
63+
// Include field as required
64+
if (in_array($fieldName, $requiredFields) || $requiredFields === ['*']) {
65+
if ($requiredFields === ['*'] && $this->entityManager->getClassMetadata($id)->isIdentifier($fieldName)) {
66+
continue;
67+
}
8368

84-
$fields[$fieldName]['description'] = $targetEntity->getMetadataConfig()['fields'][$fieldName]['description'];
85-
$fields[$fieldName]['type'] = Type::nonNull($this->typeManager->get($targetEntity->getMetadataConfig()['fields'][$fieldName]['type']));
69+
if ($this->entityManager->getClassMetadata($id)->isIdentifier($fieldName)) {
70+
throw new Exception('Identifier ' . $fieldName . ' is an invalid input.');
8671
}
72+
73+
$fields[$fieldName] = new InputObjectField([
74+
'name' => $fieldName,
75+
'description' => (string) $targetEntity->getMetadataConfig()['fields'][$fieldName]['description'],
76+
'type' => Type::nonNull($this->typeManager->get($targetEntity->getMetadataConfig()['fields'][$fieldName]['type'])),
77+
]);
8778
}
79+
} else {
80+
// All fields are required
81+
if ($this->entityManager->getClassMetadata($id)->isIdentifier($fieldName)) {
82+
continue;
83+
}
84+
85+
$fields[$fieldName] = new InputObjectField([
86+
'name' => $fieldName,
87+
'description' => (string) $targetEntity->getMetadataConfig()['fields'][$fieldName]['description'],
88+
'type' => Type::nonNull($this->typeManager->get($targetEntity->getMetadataConfig()['fields'][$fieldName]['type'])),
89+
]);
90+
}
91+
}
8892

89-
return $fields;
90-
},
93+
return new InputObjectType([
94+
'name' => $targetEntity->getTypeName() . '_Input',
95+
'description' => $targetEntity->getDescription(),
96+
'fields' => static fn () => $fields,
9197
]);
9298
}
9399
}

src/Resolve/ResolveCollectionFactory.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ public function __construct(
3232

3333
public function parseValue(ClassMetadata $metadata, string $field, mixed $value): mixed
3434
{
35+
/** @psalm-suppress UndefinedDocblockClass */
3536
$fieldMapping = $metadata->getFieldMapping($field);
3637
$graphQLType = $this->typeManager->get($fieldMapping['type']);
3738

src/Type/TypeManager.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,12 @@ public function __construct()
4343
->set('pagination', static fn () => new Pagination());
4444
}
4545

46+
/** Use Type as return type */
47+
public function get(string $id): mixed
48+
{
49+
return parent::get($id);
50+
}
51+
4652
/**
4753
* @param mixed[] $params
4854
*

0 commit comments

Comments
 (0)