Passed
Branch main (0cd36c)
by Tom
03:14 queued 39s
created

InputFactory   A

Complexity

Total Complexity 15

Size/Duplication

Total Lines 119
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 37
dl 0
loc 119
rs 10
c 0
b 0
f 0
wmc 15

5 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A get() 0 16 3
A addOptionalFields() 0 25 4
A addAllFieldsAsRequired() 0 16 3
A addRequiredFields() 0 24 4
1
<?php
2
3
declare(strict_types=1);
4
5
namespace ApiSkeletons\Doctrine\ORM\GraphQL\Input;
6
7
use ApiSkeletons\Doctrine\ORM\GraphQL\AbstractContainer;
8
use ApiSkeletons\Doctrine\ORM\GraphQL\Config;
9
use ApiSkeletons\Doctrine\ORM\GraphQL\Type\Entity;
10
use ApiSkeletons\Doctrine\ORM\GraphQL\Type\TypeManager;
11
use Doctrine\ORM\EntityManager;
12
use Exception;
13
use GraphQL\Error\Error;
14
use GraphQL\Type\Definition\InputObjectField;
15
use GraphQL\Type\Definition\InputObjectType;
16
use GraphQL\Type\Definition\Type;
17
18
use function count;
19
use function in_array;
20
use function uniqid;
21
22
/**
23
 * Create an input object type for a mutation
24
 */
25
class InputFactory extends AbstractContainer
26
{
27
    public function __construct(
28
        protected Config $config,
29
        protected EntityManager $entityManager,
30
        protected TypeManager $typeManager,
31
    ) {
32
    }
33
34
    /**
35
     * @param string[] $requiredFields An optional list of just the required fields you want for the mutation.
36
     *                                 This allows specific fields per mutation.
37
     * @param string[] $optionalFields An optional list of optional fields you want for the mutation.
38
     *                                 This allows specific fields per mutation.
39
     *
40
     * @throws Error
41
     */
42
    public function get(string $id, array $requiredFields = [], array $optionalFields = []): InputObjectType
43
    {
44
        $fields       = [];
45
        $targetEntity = $this->typeManager->build(Entity::class, $id);
46
47
        if (! count($requiredFields) && ! count($optionalFields)) {
48
            $this->addAllFieldsAsRequired($targetEntity, $fields);
49
        } else {
50
            $this->addRequiredFields($targetEntity, $requiredFields, $fields);
51
            $this->addOptionalFields($targetEntity, $optionalFields, $fields);
52
        }
53
54
        return new InputObjectType([
55
            'name' => $targetEntity->getTypeName() . '_Input_' . uniqid(),
56
            'description' => $targetEntity->getDescription(),
57
            'fields' => static fn () => $fields,
58
        ]);
59
    }
60
61
    /**
62
     * @param string[]                     $optionalFields
63
     * @param array<int, InputObjectField> $fields
64
     */
65
    protected function addOptionalFields(
66
        mixed $targetEntity,
67
        array $optionalFields,
68
        array &$fields,
69
    ): void {
70
        foreach ($this->entityManager->getClassMetadata($targetEntity->getEntityClass())->getFieldNames() as $fieldName) {
71
            if (! in_array($fieldName, $optionalFields)) {
72
                continue;
73
            }
74
75
            /**
76
             * Do not include identifiers as input.  In the majority of cases there will be
77
             * no reason to set or update an identifier.  For the case where an identifier
78
             * should be set or updated, this factory is not the correct solution.
79
             *
80
             * @phpcs-disable
81
             */
82
            if ($this->entityManager->getClassMetadata($targetEntity->getEntityClass())->isIdentifier($fieldName)) {
83
                throw new Exception('Identifier ' . $fieldName . ' is an invalid input.');
84
            }
85
86
            $fields[$fieldName] = new InputObjectField([
87
                'name' => $fieldName,
88
                'description' => (string) $targetEntity->getMetadata()['fields'][$fieldName]['description'],
89
                'type' => $this->typeManager->get($targetEntity->getMetadata()['fields'][$fieldName]['type']),
90
            ]);
91
        }
92
    }
93
94
    /**
95
     * @param string[]                     $requiredFields
96
     * @param array<int, InputObjectField> $fields
97
     */
98
    protected function addRequiredFields(
99
        mixed $targetEntity,
100
        array $requiredFields,
101
        array &$fields,
102
    ): void {
103
        foreach ($this->entityManager->getClassMetadata($targetEntity->getEntityClass())->getFieldNames() as $fieldName) {
104
            if (! in_array($fieldName, $requiredFields)) {
105
                continue;
106
            }
107
108
            /**
109
             * Do not include identifiers as input.  In the majority of cases there will be
110
             * no reason to set or update an identifier.  For the case where an identifier
111
             * should be set or updated, this factory is not the correct solution.
112
             */
113
            if ($this->entityManager->getClassMetadata($targetEntity->getEntityClass())->isIdentifier($fieldName)) {
114
                throw new Exception('Identifier ' . $fieldName . ' is an invalid input.');
115
            }
116
117
            $fields[$fieldName] = new InputObjectField([
118
                'name' => $fieldName,
119
                'description' => (string) $targetEntity->getMetadata()['fields'][$fieldName]['description'],
120
                'type' => Type::nonNull($this->typeManager->get(
121
                    $targetEntity->getMetadata()['fields'][$fieldName]['type'],
122
                )),
123
            ]);
124
        }
125
    }
126
127
    /** @param array<int, InputObjectField> $fields */
128
    protected function addAllFieldsAsRequired(mixed $targetEntity, array &$fields): void
129
    {
130
        foreach ($this->entityManager->getClassMetadata($targetEntity->getEntityClass())->getFieldNames() as $fieldName) {
131
            /**
132
             * Do not include identifiers as input.  In the majority of cases there will be
133
             * no reason to set or update an identifier.  For the case where an identifier
134
             * should be set or updated, this factory is not the correct solution.
135
             */
136
            if ($this->entityManager->getClassMetadata($targetEntity->getEntityClass())->isIdentifier($fieldName)) {
137
                continue;
138
            }
139
140
            $fields[$fieldName] = new InputObjectField([
141
                'name' => $fieldName,
142
                'description' => (string) $targetEntity->getMetadata()['fields'][$fieldName]['description'],
143
                'type' => Type::nonNull($this->typeManager->get($targetEntity->getMetadata()['fields'][$fieldName]['type'])),
144
            ]);
145
        }
146
    }
147
}
148