Completed
Push — master ( 740d0f...e52344 )
by Rafael
03:15
created

CRUDAnnotationParser   A

Complexity

Total Complexity 33

Size/Duplication

Total Lines 249
Duplicated Lines 48.19 %

Test Coverage

Coverage 81.51%

Importance

Changes 0
Metric Value
dl 120
loc 249
ccs 97
cts 119
cp 0.8151
rs 9.3999
c 0
b 0
f 0
wmc 33

7 Methods

Rating   Name   Duplication   Size   Complexity  
A supports() 0 3 1
A __construct() 0 4 1
C parse() 31 63 12
C createAddOperation() 43 43 7
C createUpdateOperation() 43 43 7
A createListOperation() 0 13 2
A createDeleteOperation() 0 17 3

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

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 1
    protected function createListOperation(ObjectDefinitionInterface $definition, Annotation\Query $query, Endpoint $endpoint, $bundleNamespace)
144
    {
145 1
        $query->name = $query->name ?? Inflector::pluralize(lcfirst($definition->getName()));
146 1
        $query->type = $query->type ?? $definition->getName();
147 1
        $query->options = array_merge(['pagination' => true], $query->options);
148 1
        $resolverReflection = new \ReflectionClass(AllNodesWithPagination::class);
149
150 1
        $resolver = ClassUtils::applyNamingConvention($bundleNamespace, 'Query', $definition->getName(), $query->name);
151 1
        if (class_exists($resolver)) {
152
            $query->resolver = $resolver;
153
        }
154
155 1
        $this->queryParser->parse($query, $resolverReflection, $endpoint);
156 1
    }
157
158
    /**
159
     * @param ObjectDefinitionInterface $definition
160
     * @param Annotation\Mutation       $mutation
161
     * @param Endpoint                  $endpoint
162
     * @param string                    $bundleNamespace
163
     */
164 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...
165
    {
166 1
        $mutation->name = $mutation->name ?? 'add'.ucfirst($definition->getName());
167 1
        $mutation->payload = $mutation->payload ?? null;
168 1
        if (!$mutation->payload) {
169
            //deep cloning
170
            /** @var ObjectDefinitionInterface $payload */
171 1
            $payload = unserialize(serialize($endpoint->getType(AddNodePayload::class)), [DefinitionInterface::class]);
172 1
            $payload->setName(ucfirst($mutation->name.'Payload'));
173
174 1
            if (!$endpoint->hasType($payload->getName())) {
175 1
                $payload->getField('node')->setType($definition->getName());
176 1
                $endpoint->add($payload);
177
            }
178
179 1
            $mutation->payload = $payload->getName();
180
        }
181 1
        $mutation->node = $mutation->node ?? $definition->getName();
182
183 1
        if ($endpoint->hasTypeForClass($mutation->node)) {
184
            $mutation->node = $endpoint->getTypeForClass($mutation->node);
185
        }
186
187 1
        $formType = true;
188 1
        $options = [];
189 1
        $generalForm = ClassUtils::applyNamingConvention($bundleNamespace, 'Form\Input', $mutation->node, $mutation->node, 'Input');
190 1
        $specificForm = ClassUtils::applyNamingConvention($bundleNamespace, 'Form\Input', $mutation->node, $mutation->name, 'Input');
191 1
        if (class_exists($specificForm)) {
192 1
            $formType = $specificForm;
193
        } elseif (class_exists($generalForm)) {
194
            $formType = $generalForm;
195
            $options['operation'] = $mutation->name;
196
        }
197
198 1
        $mutation->options = array_merge(['form' => ['type' => $formType, 'options' => $options]], $mutation->options);
199 1
        $resolverReflection = new \ReflectionClass(AddNode::class);
200
201 1
        $resolver = ClassUtils::applyNamingConvention($bundleNamespace, 'Mutation', $definition->getName(), $mutation->name);
202 1
        if (class_exists($resolver)) {
203
            $mutation->resolver = $resolver;
204
        }
205
206 1
        $this->mutationParser->parse($mutation, $resolverReflection, $endpoint);
207 1
    }
208
209
    /**
210
     * @param ObjectDefinitionInterface $definition
211
     * @param Annotation\Mutation       $mutation
212
     * @param Endpoint                  $endpoint
213
     * @param string                    $bundleNamespace
214
     */
215 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...
216
    {
217 1
        $mutation->name = $mutation->name ?? 'update'.ucfirst($definition->getName());
218 1
        $mutation->payload = $mutation->payload ?? null;
219 1
        if (!$mutation->payload) {
220
            //deep cloning
221
            /** @var ObjectDefinitionInterface $payload */
222 1
            $payload = unserialize(serialize($endpoint->getType(UpdateNodePayload::class)), [DefinitionInterface::class]);
223 1
            $payload->setName(ucfirst($mutation->name.'Payload'));
224
225 1
            if (!$endpoint->hasType($payload->getName())) {
226 1
                $payload->getField('node')->setType($definition->getName());
227 1
                $endpoint->add($payload);
228
            }
229
230 1
            $mutation->payload = $payload->getName();
231
        }
232 1
        $mutation->node = $mutation->node ?? $definition->getName();
233
234 1
        if ($endpoint->hasTypeForClass($mutation->node)) {
235
            $mutation->node = $endpoint->getTypeForClass($mutation->node);
236
        }
237
238 1
        $formType = true;
239 1
        $options = [];
240 1
        $generalForm = ClassUtils::applyNamingConvention($bundleNamespace, 'Form\Input', $mutation->node, $mutation->node, 'Input');
241 1
        $specificForm = ClassUtils::applyNamingConvention($bundleNamespace, 'Form\Input', $mutation->node, $mutation->name, 'Input');
242 1
        if (class_exists($specificForm)) {
243 1
            $formType = $specificForm;
244
        } elseif (class_exists($generalForm)) {
245
            $formType = $generalForm;
246
            $options['operation'] = $mutation->name;
247
        }
248
249 1
        $mutation->options = array_merge(['form' => ['type' => $formType, 'options' => $options]], $mutation->options);
250 1
        $resolverReflection = new \ReflectionClass(UpdateNode::class);
251
252 1
        $resolver = ClassUtils::applyNamingConvention($bundleNamespace, 'Mutation', $definition->getName(), $mutation->name);
253 1
        if (class_exists($resolver)) {
254
            $mutation->resolver = $resolver;
255
        }
256
257 1
        $this->mutationParser->parse($mutation, $resolverReflection, $endpoint);
258 1
    }
259
260
    /**
261
     * @param ObjectDefinitionInterface $definition
262
     * @param Annotation\Mutation       $mutation
263
     * @param Endpoint                  $endpoint
264
     * @param string                    $bundleNamespace
265
     */
266 1
    protected function createDeleteOperation(ObjectDefinitionInterface $definition, Annotation\Mutation $mutation, Endpoint $endpoint, $bundleNamespace)
267
    {
268 1
        $mutation->name = $mutation->name ?? 'delete'.ucfirst($definition->getName());
269 1
        $mutation->payload = $mutation->payload ?? null;
270 1
        if (!$mutation->payload) {
271 1
            $mutation->payload = DeleteNodePayload::class;
272
        }
273 1
        $mutation->node = $mutation->node ?? $definition->getName();
274 1
        $mutation->options = array_merge(['form' => ['type' => NodeDeleteInput::class]], $mutation->options);
275 1
        $resolverReflection = new \ReflectionClass(DeleteNode::class);
276
277 1
        $resolver = ClassUtils::applyNamingConvention($bundleNamespace, 'Mutation', $definition->getName(), $mutation->name);
278 1
        if (class_exists($resolver)) {
279
            $mutation->resolver = $resolver;
280
        }
281
282 1
        $this->mutationParser->parse($mutation, $resolverReflection, $endpoint);
283 1
    }
284
}
285