Completed
Push — master ( 9e9a0d...740d0f )
by Rafael
04:13
created

CRUDAnnotationParser::createUpdateOperation()   C

Complexity

Conditions 7
Paths 36

Size

Total Lines 43
Code Lines 27

Duplication

Lines 43
Ratio 100 %

Code Coverage

Tests 23
CRAP Score 7.2791

Importance

Changes 0
Metric Value
cc 7
eloc 27
nc 36
nop 4
dl 43
loc 43
ccs 23
cts 28
cp 0.8214
crap 7.2791
rs 6.7272
c 0
b 0
f 0
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\Query\Node\Node;
28
use Ynlo\GraphQLBundle\Query\Node\Nodes;
29
use Ynlo\GraphQLBundle\Util\ClassUtils;
30
31
/**
32
 * CRUDAnnotationParser
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 1
    public function __construct(QueryAnnotationParser $queryParser, MutationAnnotationParser $mutationParser)
55
    {
56 1
        $this->queryParser = $queryParser;
57 1
        $this->mutationParser = $mutationParser;
58 1
    }
59
60
    /**
61
     * {@inheritdoc}
62
     */
63 1
    public function supports($annotation): bool
64
    {
65 1
        return $annotation instanceof Annotation\CRUDOperations;
66
    }
67
68
    /**
69
     * {@inheritDoc}
70
     */
71 1
    public function parse($annotation, \ReflectionClass $refClass, Endpoint $endpoint)
72
    {
73 1
        if (!$endpoint->hasTypeForClass($refClass->getName())) {
74
            throw new \Exception(sprintf('Can`t apply CRUD operations to "%s", CRUD operations can only be applied to valid GraphQL object types.', $refClass->getName()));
75
        }
76
77 1
        if (!$refClass->implementsInterface(NodeInterface::class)) {
78
            throw new \Exception(
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 1
        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 1
        $definition = $endpoint->getType($endpoint->getTypeForClass($refClass->getName()));
93
94 1
        $bundleNamespace = ClassUtils::relatedBundleNamespace($refClass->getName());
95
96
        //All query
97 1 View Code Duplication
        if (in_array('list', $annotation->include)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
98 1
            if ($annotation->list) {
99
                $query = $annotation->list;
100
            } else {
101 1
                $query = new Annotation\Query();
102
            }
103 1
            $this->createListOperation($definition, $query, $endpoint, $bundleNamespace);
104
        }
105
106
        //Add mutation
107 1 View Code Duplication
        if (in_array('add', $annotation->include)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
108 1
            if ($annotation->add) {
109
                $mutation = $annotation->add;
110
            } else {
111 1
                $mutation = new Annotation\Mutation();
112
            }
113 1
            $this->createAddOperation($definition, $mutation, $endpoint, $bundleNamespace);
114
        }
115
116
        //Update mutation
117 1 View Code Duplication
        if (in_array('update', $annotation->include)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
118 1
            if ($annotation->update) {
119
                $mutation = $annotation->update;
120
            } else {
121 1
                $mutation = new Annotation\Mutation();
122
            }
123 1
            $this->createUpdateOperation($definition, $mutation, $endpoint, $bundleNamespace);
124
        }
125
126
        //Delete mutation
127 1 View Code Duplication
        if (in_array('delete', $annotation->include)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
128 1
            if ($annotation->delete) {
129
                $mutation = $annotation->delete;
130
            } else {
131 1
                $mutation = new Annotation\Mutation();
132
            }
133 1
            $this->createDeleteOperation($definition, $mutation, $endpoint, $bundleNamespace);
134
        }
135 1
    }
136
137
    /**
138
     * @param ObjectDefinitionInterface $definition
139
     * @param Annotation\Query          $query
140
     * @param Endpoint                  $endpoint
141
     * @param string                    $bundleNamespace
142
     */
143 View Code Duplication
    protected function createGetsOperation(ObjectDefinitionInterface $definition, Annotation\Query $query, Endpoint $endpoint, $bundleNamespace)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
144
    {
145
        $query->name = $query->name ?? Inflector::pluralize(lcfirst($definition->getName()));
146
        $query->node = $query->node ?? $definition->getName();
147
        $query->list = true;
148
        $resolverReflection = new \ReflectionClass(Nodes::class);
149
150
        $resolver = ClassUtils::applyNamingConvention($bundleNamespace, 'Query', $definition->getName(), $query->name);
151
        if (class_exists($resolver)) {
152
            $query->resolver = $resolver;
153
        }
154
155
        $this->queryParser->parse($query, $resolverReflection, $endpoint);
156
    }
157
158
    /**
159
     * @param ObjectDefinitionInterface $definition
160
     * @param Annotation\Query          $query
161
     * @param Endpoint                  $endpoint
162
     * @param string                    $bundleNamespace
163
     */
164 View Code Duplication
    protected function createGetOperation(ObjectDefinitionInterface $definition, Annotation\Query $query, Endpoint $endpoint, $bundleNamespace)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
165
    {
166
        $query->name = $query->name ?? Inflector::singularize(lcfirst($definition->getName()));
167
        $query->node = $query->node ?? $definition->getName();
168
        $resolverReflection = new \ReflectionClass(Node::class);
169
170
        $resolver = ClassUtils::applyNamingConvention($bundleNamespace, 'Query', $definition->getName(), $query->name);
171
        if (class_exists($resolver)) {
172
            $query->resolver = $resolver;
173
        }
174
175
        $this->queryParser->parse($query, $resolverReflection, $endpoint);
176
    }
177
178
    /**
179
     * @param ObjectDefinitionInterface $definition
180
     * @param Annotation\Query          $query
181
     * @param Endpoint                  $endpoint
182
     * @param string                    $bundleNamespace
183
     */
184 1 View Code Duplication
    protected function createListOperation(ObjectDefinitionInterface $definition, Annotation\Query $query, Endpoint $endpoint, $bundleNamespace)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
185
    {
186 1
        $query->name = $query->name ?? Inflector::pluralize(lcfirst($definition->getName()));
187 1
        $query->node = $query->node ?? $definition->getName();
188 1
        $query->options = array_merge(['pagination' => true], $query->options);
189 1
        $resolverReflection = new \ReflectionClass(AllNodesWithPagination::class);
190
191 1
        $resolver = ClassUtils::applyNamingConvention($bundleNamespace, 'Query', $definition->getName(), $query->name);
192 1
        if (class_exists($resolver)) {
193
            $query->resolver = $resolver;
194
        }
195
196 1
        $this->queryParser->parse($query, $resolverReflection, $endpoint);
197 1
    }
198
199
    /**
200
     * @param ObjectDefinitionInterface $definition
201
     * @param Annotation\Mutation       $mutation
202
     * @param Endpoint                  $endpoint
203
     * @param string                    $bundleNamespace
204
     */
205 1 View Code Duplication
    protected function createAddOperation(ObjectDefinitionInterface $definition, Annotation\Mutation $mutation, Endpoint $endpoint, $bundleNamespace)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
206
    {
207 1
        $mutation->name = $mutation->name ?? 'add'.ucfirst($definition->getName());
208 1
        $mutation->payload = $mutation->payload ?? null;
209 1
        if (!$mutation->payload) {
210
            //deep cloning
211
            /** @var ObjectDefinitionInterface $payload */
212 1
            $payload = unserialize(serialize($endpoint->getType(AddNodePayload::class)), [DefinitionInterface::class]);
213 1
            $payload->setName(ucfirst($mutation->name.'Payload'));
214
215 1
            if (!$endpoint->hasType($payload->getName())) {
216 1
                $payload->getField('node')->setType($definition->getName());
217 1
                $endpoint->add($payload);
218
            }
219
220 1
            $mutation->payload = $payload->getName();
221
        }
222 1
        $mutation->node = $mutation->node ?? $definition->getName();
223
224 1
        if ($endpoint->hasTypeForClass($mutation->node)) {
225
            $mutation->node = $endpoint->getTypeForClass($mutation->node);
226
        }
227
228 1
        $formType = true;
229 1
        $options = [];
230 1
        $generalForm = ClassUtils::applyNamingConvention($bundleNamespace, 'Form\Input', $mutation->node, $mutation->node, 'Input');
231 1
        $specificForm = ClassUtils::applyNamingConvention($bundleNamespace, 'Form\Input', $mutation->node, $mutation->name, 'Input');
232 1
        if (class_exists($specificForm)) {
233 1
            $formType = $specificForm;
234
        } elseif (class_exists($generalForm)) {
235
            $formType = $generalForm;
236
            $options['operation'] = $mutation->name;
237
        }
238
239 1
        $mutation->options = array_merge(['form' => ['type' => $formType, 'options' => $options]], $mutation->options);
240 1
        $resolverReflection = new \ReflectionClass(AddNode::class);
241
242 1
        $resolver = ClassUtils::applyNamingConvention($bundleNamespace, 'Mutation', $definition->getName(), $mutation->name);
243 1
        if (class_exists($resolver)) {
244
            $mutation->resolver = $resolver;
245
        }
246
247 1
        $this->mutationParser->parse($mutation, $resolverReflection, $endpoint);
248 1
    }
249
250
    /**
251
     * @param ObjectDefinitionInterface $definition
252
     * @param Annotation\Mutation       $mutation
253
     * @param Endpoint                  $endpoint
254
     * @param string                    $bundleNamespace
255
     */
256 1 View Code Duplication
    protected function createUpdateOperation(ObjectDefinitionInterface $definition, Annotation\Mutation $mutation, Endpoint $endpoint, $bundleNamespace)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
257
    {
258 1
        $mutation->name = $mutation->name ?? 'update'.ucfirst($definition->getName());
259 1
        $mutation->payload = $mutation->payload ?? null;
260 1
        if (!$mutation->payload) {
261
            //deep cloning
262
            /** @var ObjectDefinitionInterface $payload */
263 1
            $payload = unserialize(serialize($endpoint->getType(UpdateNodePayload::class)), [DefinitionInterface::class]);
264 1
            $payload->setName(ucfirst($mutation->name.'Payload'));
265
266 1
            if (!$endpoint->hasType($payload->getName())) {
267 1
                $payload->getField('node')->setType($definition->getName());
268 1
                $endpoint->add($payload);
269
            }
270
271 1
            $mutation->payload = $payload->getName();
272
        }
273 1
        $mutation->node = $mutation->node ?? $definition->getName();
274
275 1
        if ($endpoint->hasTypeForClass($mutation->node)) {
276
            $mutation->node = $endpoint->getTypeForClass($mutation->node);
277
        }
278
279 1
        $formType = true;
280 1
        $options = [];
281 1
        $generalForm = ClassUtils::applyNamingConvention($bundleNamespace, 'Form\Input', $mutation->node, $mutation->node, 'Input');
282 1
        $specificForm = ClassUtils::applyNamingConvention($bundleNamespace, 'Form\Input', $mutation->node, $mutation->name, 'Input');
283 1
        if (class_exists($specificForm)) {
284 1
            $formType = $specificForm;
285
        } elseif (class_exists($generalForm)) {
286
            $formType = $generalForm;
287
            $options['operation'] = $mutation->name;
288
        }
289
290 1
        $mutation->options = array_merge(['form' => ['type' => $formType, 'options' => $options]], $mutation->options);
291 1
        $resolverReflection = new \ReflectionClass(UpdateNode::class);
292
293 1
        $resolver = ClassUtils::applyNamingConvention($bundleNamespace, 'Mutation', $definition->getName(), $mutation->name);
294 1
        if (class_exists($resolver)) {
295
            $mutation->resolver = $resolver;
296
        }
297
298 1
        $this->mutationParser->parse($mutation, $resolverReflection, $endpoint);
299 1
    }
300
301
    /**
302
     * @param ObjectDefinitionInterface $definition
303
     * @param Annotation\Mutation       $mutation
304
     * @param Endpoint                  $endpoint
305
     * @param string                    $bundleNamespace
306
     */
307 1
    protected function createDeleteOperation(ObjectDefinitionInterface $definition, Annotation\Mutation $mutation, Endpoint $endpoint, $bundleNamespace)
308
    {
309 1
        $mutation->name = $mutation->name ?? 'delete'.ucfirst($definition->getName());
310 1
        $mutation->payload = $mutation->payload ?? null;
311 1
        if (!$mutation->payload) {
312 1
            $mutation->payload = DeleteNodePayload::class;
313
        }
314 1
        $mutation->node = $mutation->node ?? $definition->getName();
315 1
        $mutation->options = array_merge(['form' => ['type' => NodeDeleteInput::class]], $mutation->options);
316 1
        $resolverReflection = new \ReflectionClass(DeleteNode::class);
317
318 1
        $resolver = ClassUtils::applyNamingConvention($bundleNamespace, 'Mutation', $definition->getName(), $mutation->name);
319 1
        if (class_exists($resolver)) {
320
            $mutation->resolver = $resolver;
321
        }
322
323 1
        $this->mutationParser->parse($mutation, $resolverReflection, $endpoint);
324 1
    }
325
}
326