Completed
Pull Request — master (#7)
by Yonel Ceruto
10:45 queued 04:14
created

CRUDAnnotationParser::createListOperation()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 12
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 12
ccs 0
cts 9
cp 0
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 8
nc 2
nop 4
crap 6
1
<?php
2
/*******************************************************************************
3
 *  This file is part of the GraphQL Bundle package.
4
 *
5
 *  (c) YnloUltratech <[email protected]>
6
 *
7
 *  For the full copyright and license information, please view the LICENSE
8
 *  file that was distributed with this source code.
9
 ******************************************************************************/
10
11
namespace Ynlo\GraphQLBundle\Definition\Loader\Annotation;
12
13
use Doctrine\Common\Util\Inflector;
14
use Ynlo\GraphQLBundle\Annotation;
15
use Ynlo\GraphQLBundle\Definition\DefinitionInterface;
16
use Ynlo\GraphQLBundle\Definition\ObjectDefinitionInterface;
17
use Ynlo\GraphQLBundle\Definition\Registry\Endpoint;
18
use Ynlo\GraphQLBundle\Form\Node\NodeDeleteInput;
19
use Ynlo\GraphQLBundle\Model\AddNodePayload;
20
use Ynlo\GraphQLBundle\Model\DeleteNodePayload;
21
use Ynlo\GraphQLBundle\Model\NodeInterface;
22
use Ynlo\GraphQLBundle\Model\UpdateNodePayload;
23
use Ynlo\GraphQLBundle\Mutation\AddNode;
24
use Ynlo\GraphQLBundle\Mutation\DeleteNode;
25
use Ynlo\GraphQLBundle\Mutation\UpdateNode;
26
use Ynlo\GraphQLBundle\Query\Node\AllNodesWithPagination;
27
use Ynlo\GraphQLBundle\Util\ClassUtils;
28
29
/**
30
 * CRUDAnnotationParser
31
 *
32
 * @deprecated in favor of QueryList, MutationAdd, MutationUpdate, MutationDelete parsers
33
 */
34
class CRUDAnnotationParser implements AnnotationParserInterface
35
{
36
    use AnnotationReaderAwareTrait;
37
38
    /**
39
     * @var QueryAnnotationParser
40
     */
41
    protected $queryParser;
42
43
    /**
44
     * @var MutationAnnotationParser
45
     */
46
    protected $mutationParser;
47
48
    /**
49
     * CRUDAnnotationParser constructor.
50
     *
51
     * @param QueryAnnotationParser    $queryParser
52
     * @param MutationAnnotationParser $mutationParser
53
     */
54 22
    public function __construct(QueryAnnotationParser $queryParser, MutationAnnotationParser $mutationParser)
55
    {
56 22
        $this->queryParser = $queryParser;
57 22
        $this->mutationParser = $mutationParser;
58 22
    }
59
60
    /**
61
     * {@inheritdoc}
62
     */
63 22
    public function supports($annotation): bool
64
    {
65 22
        return $annotation instanceof Annotation\CRUDOperations;
66
    }
67
68
    /**
69
     * {@inheritDoc}
70
     */
71 22
    public function parse($annotation, \ReflectionClass $refClass, Endpoint $endpoint)
72
    {
73 22
        if (!$endpoint->hasTypeForClass($refClass->getName())) {
74
            throw new \RuntimeException(sprintf('Can`t apply CRUD operations to "%s", CRUD operations can only be applied to valid GraphQL object types.', $refClass->getName()));
75
        }
76
77 22
        if (!$refClass->implementsInterface(NodeInterface::class)) {
78
            throw new \RuntimeException(
79
                sprintf(
80
                    'Can`t apply CRUD operations to "%s", CRUD operations can only be applied to nodes.
81
             You are implementing NodeInterface in this class?',
82
                    $refClass->getName()
83
                )
84
            );
85
        }
86
87
        /** @var Annotation\CRUDOperations $annotation */
88 22
        if ($annotation->exclude) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $annotation->exclude of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
89
            $annotation->include = array_diff($annotation->include, $annotation->exclude);
90
        }
91
92 22
        $definition = $endpoint->getType($endpoint->getTypeForClass($refClass->getName()));
93
94 22
        $bundleNamespace = ClassUtils::relatedBundleNamespace($refClass->getName());
95
96
        //All query
97 22
        if (\in_array('list', $annotation->include, true)) {
98
            if ($annotation->list) {
99
                $query = $annotation->list;
100
            } else {
101
                $query = new Annotation\Query();
102
            }
103
            $this->createListOperation($definition, $query, $endpoint, $bundleNamespace);
104
        }
105
106
        //Add mutation
107 22
        if (\in_array('add', $annotation->include, true)) {
108
            if ($annotation->add) {
109
                $mutation = $annotation->add;
110
            } else {
111
                $mutation = new Annotation\Mutation();
112
            }
113
            $this->createAddOperation($definition, $mutation, $endpoint, $bundleNamespace);
114
        }
115
116
        //Update mutation
117 22
        if (\in_array('update', $annotation->include, true)) {
118
            if ($annotation->update) {
119
                $mutation = $annotation->update;
120
            } else {
121
                $mutation = new Annotation\Mutation();
122
            }
123
            $this->createUpdateOperation($definition, $mutation, $endpoint, $bundleNamespace);
124
        }
125
126
        //Delete mutation
127 22
        if (\in_array('delete', $annotation->include, true)) {
128 22
            if ($annotation->delete) {
129
                $mutation = $annotation->delete;
130
            } else {
131 22
                $mutation = new Annotation\Mutation();
132
            }
133 22
            $this->createDeleteOperation($definition, $mutation, $endpoint, $bundleNamespace);
134
        }
135 22
    }
136
137
    /**
138
     * @param ObjectDefinitionInterface $definition
139
     * @param Annotation\Query          $query
140
     * @param Endpoint                  $endpoint
141
     * @param string                    $bundleNamespace
142
     */
143
    protected function createListOperation(ObjectDefinitionInterface $definition, Annotation\Query $query, Endpoint $endpoint, $bundleNamespace)
144
    {
145
        $query->name = $query->name ?? 'all'.Inflector::pluralize(ucfirst($definition->getName()));
146
        $query->type = $query->type ?? $definition->getName();
147
        $query->options = array_merge(['pagination' => true], $query->options);
148
        $resolver = ClassUtils::applyNamingConvention($bundleNamespace, 'Query', $definition->getName(), $query->name);
149
        if (class_exists($resolver)) {
150
            $query->resolver = $resolver;
151
        }
152
153
        $resolverReflection = new \ReflectionClass(AllNodesWithPagination::class);
154
        $this->queryParser->parse($query, $resolverReflection, $endpoint);
155
    }
156
157
    /**
158
     * @param ObjectDefinitionInterface $definition
159
     * @param Annotation\Mutation       $mutation
160
     * @param Endpoint                  $endpoint
161
     * @param string                    $bundleNamespace
162
     */
163
    protected function createAddOperation(ObjectDefinitionInterface $definition, Annotation\Mutation $mutation, Endpoint $endpoint, $bundleNamespace)
164
    {
165
        $mutation->name = $mutation->name ?? 'add'.ucfirst($definition->getName());
166
        $mutation->payload = $mutation->payload ?? null;
167
        if (!$mutation->payload) {
168
            //deep cloning
169
            /** @var ObjectDefinitionInterface $payload */
170
            $payload = unserialize(serialize($endpoint->getType(AddNodePayload::class)), [DefinitionInterface::class]);
171
            $payload->setName(ucfirst($mutation->name.'Payload'));
172
173
            if (!$endpoint->hasType($payload->getName())) {
174
                $payload->getField('node')->setType($definition->getName());
175
                $endpoint->add($payload);
176
            }
177
178
            $mutation->payload = $payload->getName();
179
        }
180
        $mutation->node = $mutation->node ?? $definition->getName();
181
182
        if ($endpoint->hasTypeForClass($mutation->node)) {
183
            $mutation->node = $endpoint->getTypeForClass($mutation->node);
184
        }
185
186
        $formType = true;
187
        $options = [];
188
        $generalForm = ClassUtils::applyNamingConvention($bundleNamespace, 'Form\Input', $mutation->node, $mutation->node, 'Input');
189
        $specificForm = ClassUtils::applyNamingConvention($bundleNamespace, 'Form\Input', $mutation->node, $mutation->name, 'Input');
190
        if (class_exists($specificForm)) {
191
            $formType = $specificForm;
192
        } elseif (class_exists($generalForm)) {
193
            $formType = $generalForm;
194
            $options['operation'] = $mutation->name;
195
        }
196
197
        $mutation->options = array_merge(['form' => ['type' => $formType, 'options' => $options]], $mutation->options);
198
        $resolverReflection = new \ReflectionClass(AddNode::class);
199
200
        $resolver = ClassUtils::applyNamingConvention($bundleNamespace, 'Mutation', $definition->getName(), $mutation->name);
201
        if (class_exists($resolver)) {
202
            $mutation->resolver = $resolver;
203
        }
204
205
        $this->mutationParser->parse($mutation, $resolverReflection, $endpoint);
206
    }
207
208
    /**
209
     * @param ObjectDefinitionInterface $definition
210
     * @param Annotation\Mutation       $mutation
211
     * @param Endpoint                  $endpoint
212
     * @param string                    $bundleNamespace
213
     */
214
    protected function createUpdateOperation(ObjectDefinitionInterface $definition, Annotation\Mutation $mutation, Endpoint $endpoint, $bundleNamespace)
215
    {
216
        $mutation->name = $mutation->name ?? 'update'.ucfirst($definition->getName());
217
        $mutation->payload = $mutation->payload ?? null;
218
        if (!$mutation->payload) {
219
            //deep cloning
220
            /** @var ObjectDefinitionInterface $payload */
221
            $payload = unserialize(serialize($endpoint->getType(UpdateNodePayload::class)), [DefinitionInterface::class]);
222
            $payload->setName(ucfirst($mutation->name.'Payload'));
223
224
            if (!$endpoint->hasType($payload->getName())) {
225
                $payload->getField('node')->setType($definition->getName());
226
                $endpoint->add($payload);
227
            }
228
229
            $mutation->payload = $payload->getName();
230
        }
231
        $mutation->node = $mutation->node ?? $definition->getName();
232
233
        if ($endpoint->hasTypeForClass($mutation->node)) {
234
            $mutation->node = $endpoint->getTypeForClass($mutation->node);
235
        }
236
237
        $formType = true;
238
        $options = [];
239
        $generalForm = ClassUtils::applyNamingConvention($bundleNamespace, 'Form\Input', $mutation->node, $mutation->node, 'Input');
240
        $specificForm = ClassUtils::applyNamingConvention($bundleNamespace, 'Form\Input', $mutation->node, $mutation->name, 'Input');
241
        if (class_exists($specificForm)) {
242
            $formType = $specificForm;
243
        } elseif (class_exists($generalForm)) {
244
            $formType = $generalForm;
245
            $options['operation'] = $mutation->name;
246
        }
247
248
        $mutation->options = array_merge(['form' => ['type' => $formType, 'options' => $options]], $mutation->options);
249
        $resolverReflection = new \ReflectionClass(UpdateNode::class);
250
251
        $resolver = ClassUtils::applyNamingConvention($bundleNamespace, 'Mutation', $definition->getName(), $mutation->name);
252
        if (class_exists($resolver)) {
253
            $mutation->resolver = $resolver;
254
        }
255
256
        $this->mutationParser->parse($mutation, $resolverReflection, $endpoint);
257
    }
258
259
    /**
260
     * @param ObjectDefinitionInterface $definition
261
     * @param Annotation\Mutation       $mutation
262
     * @param Endpoint                  $endpoint
263
     * @param string                    $bundleNamespace
264
     */
265 22
    protected function createDeleteOperation(ObjectDefinitionInterface $definition, Annotation\Mutation $mutation, Endpoint $endpoint, $bundleNamespace)
266
    {
267 22
        $mutation->name = $mutation->name ?? 'delete'.ucfirst($definition->getName());
268 22
        $mutation->payload = $mutation->payload ?? null;
269 22
        if (!$mutation->payload) {
270 22
            $mutation->payload = DeleteNodePayload::class;
271
        }
272 22
        $mutation->node = $mutation->node ?? $definition->getName();
273 22
        $mutation->options = array_merge(['form' => ['type' => NodeDeleteInput::class]], $mutation->options);
274 22
        $resolverReflection = new \ReflectionClass(DeleteNode::class);
275
276 22
        $resolver = ClassUtils::applyNamingConvention($bundleNamespace, 'Mutation', $definition->getName(), $mutation->name);
277 22
        if (class_exists($resolver)) {
278
            $mutation->resolver = $resolver;
279
        }
280
281 22
        $this->mutationParser->parse($mutation, $resolverReflection, $endpoint);
282 22
    }
283
}
284