Passed
Pull Request — main (#39)
by Tom
02:19
created

InputFactory::addRequiredFields()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 24
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 10
nc 4
nop 3
dl 0
loc 24
rs 9.9332
c 0
b 0
f 0
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
            if ($this->entityManager->getClassMetadata($targetEntity->getEntityClass())->isIdentifier($fieldName)) {
81
                throw new Exception('Identifier ' . $fieldName . ' is an invalid input.');
82
            }
83
84
            $fields[$fieldName] = new InputObjectField([
85
                'name' => $fieldName,
86
                'description' => (string) $targetEntity->getMetadata()['fields'][$fieldName]['description'],
87
                'type' => $this->typeManager->get($targetEntity->getMetadata()['fields'][$fieldName]['type']),
88
            ]);
89
        }
90
    }
91
92
    /**
93
     * @param string[]                     $requiredFields
94
     * @param array<int, InputObjectField> $fields
95
     */
96
    protected function addRequiredFields(
97
        mixed $targetEntity,
98
        array $requiredFields,
99
        array &$fields,
100
    ): void {
101
        foreach ($this->entityManager->getClassMetadata($targetEntity->getEntityClass())->getFieldNames() as $fieldName) {
102
            if (! in_array($fieldName, $requiredFields)) {
103
                continue;
104
            }
105
106
            /**
107
             * Do not include identifiers as input.  In the majority of cases there will be
108
             * no reason to set or update an identifier.  For the case where an identifier
109
             * should be set or updated, this factory is not the correct solution.
110
             */
111
            if ($this->entityManager->getClassMetadata($targetEntity->getEntityClass())->isIdentifier($fieldName)) {
112
                throw new Exception('Identifier ' . $fieldName . ' is an invalid input.');
113
            }
114
115
            $fields[$fieldName] = new InputObjectField([
116
                'name' => $fieldName,
117
                'description' => (string) $targetEntity->getMetadata()['fields'][$fieldName]['description'],
118
                'type' => Type::nonNull($this->typeManager->get(
119
                    $targetEntity->getMetadata()['fields'][$fieldName]['type'],
120
                )),
121
            ]);
122
        }
123
    }
124
125
    /** @param array<int, InputObjectField> $fields */
126
    protected function addAllFieldsAsRequired(mixed $targetEntity, array &$fields): void
127
    {
128
        foreach ($this->entityManager->getClassMetadata($targetEntity->getEntityClass())->getFieldNames() as $fieldName) {
129
            /**
130
             * Do not include identifiers as input.  In the majority of cases there will be
131
             * no reason to set or update an identifier.  For the case where an identifier
132
             * should be set or updated, this factory is not the correct solution.
133
             */
134
            if ($this->entityManager->getClassMetadata($targetEntity->getEntityClass())->isIdentifier($fieldName)) {
135
                continue;
136
            }
137
138
            $fields[$fieldName] = new InputObjectField([
139
                'name' => $fieldName,
140
                'description' => (string) $targetEntity->getMetadata()['fields'][$fieldName]['description'],
141
                'type' => Type::nonNull($this->typeManager->get($targetEntity->getMetadata()['fields'][$fieldName]['type'])),
142
            ]);
143
        }
144
    }
145
}
146