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

CRUDAnnotationParser::createAddOperation()   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 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