Completed
Push — master ( 2975e7...46c3ee )
by Rafael
07:49
created

NamespaceDefinitionPlugin::configure()   F

Complexity

Conditions 20
Paths 325

Size

Total Lines 54

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 27
CRAP Score 20.4

Importance

Changes 0
Metric Value
dl 0
loc 54
ccs 27
cts 30
cp 0.9
rs 1.7708
c 0
b 0
f 0
cc 20
nc 325
nop 3
crap 20.4

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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\Plugin;
12
13
use Doctrine\Common\Util\Inflector;
14
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
15
use Ynlo\GraphQLBundle\Definition\DefinitionInterface;
16
use Ynlo\GraphQLBundle\Definition\ExecutableDefinitionInterface;
17
use Ynlo\GraphQLBundle\Definition\FieldDefinition;
18
use Ynlo\GraphQLBundle\Definition\FieldsAwareDefinitionInterface;
19
use Ynlo\GraphQLBundle\Definition\MutationDefinition;
20
use Ynlo\GraphQLBundle\Definition\NodeAwareDefinitionInterface;
21
use Ynlo\GraphQLBundle\Definition\ObjectDefinition;
22
use Ynlo\GraphQLBundle\Definition\ObjectDefinitionInterface;
23
use Ynlo\GraphQLBundle\Definition\Registry\Endpoint;
24
use Ynlo\GraphQLBundle\Resolver\EmptyObjectResolver;
25
26
/**
27
 * This extension configure namespace in definitions
28
 * using definition node and bundle in the node
29
 */
30
class NamespaceDefinitionPlugin extends AbstractDefinitionPlugin
31
{
32
    protected $globalConfig = [];
33
34
    /**
35
     * NamespaceDefinitionPlugin constructor.
36
     *
37
     * Configuration:
38
     *
39
     * # Group each bundle into a separate schema definition
40
     * bundles:
41
     *      enabled:              true
42
     *
43
     *      # The following suffix will be used for bundle query groups
44
     *      query_suffix:         BundleQuery
45
     *
46
     *      # The following suffix will be used for bundle mutation groups
47
     *      mutation_suffix:      BundleMutation
48
     *
49
     *      # The following bundles will be ignore for grouping, all definitions will be placed in the root query or mutation
50
     *      ignore:
51
     *
52
     *      # Default:
53
     *      - AppBundle
54
     *
55
     *      # Define aliases for bundles to set definitions inside other desired bundle name.
56
     *      # Can be used to group multiple bundles or publish a bundle with a different name
57
     *      aliases:              # Example: SecurityBundle: AppBundle
58
     *
59
     *          # Prototype
60
     *          name:                 ~
61
     *
62
     * # Group queries and mutations of the same node into a node specific schema definition.
63
     * nodes:
64
     *      enabled:              true
65
     *
66
     *      # The following suffix will be used to create the name for queries to the same node
67
     *      query_suffix:         Query
68
     *
69
     *      # The following suffix will be used to create the name for mutations to the same node
70
     *      mutation_suffix:      Mutation
71
     *
72
     *      # The following nodes will be ignore for grouping, all definitions will be placed in the root query or mutation
73
     *      ignore:
74
     *
75
     *      # Default:
76
     *      - Node
77
     *
78
     *      # Define aliases for nodes to set definitions inside other desired node name.
79
     *      # Can be used to group multiple nodes or publish a node with a different group name
80
     *      aliases:              # Example: InvoiceItem: Invoice
81
     *
82
     *          # Prototype
83
     *          name:
84
     *
85
     * @param array $config
86
     */
87 2
    public function __construct(array $config = [])
88
    {
89 2
        $this->globalConfig = $config;
90 2
    }
91
92
    /**
93
     * {@inheritDoc}
94
     */
95
    public function buildConfig(ArrayNodeDefinition $root): void
96
    {
97
        $config = $root
98
            ->info('Enable/Disable namespace for queries and mutations')
99
            ->canBeDisabled()
100
            ->children();
101
102
        $config->scalarNode('node');
103
        $config->scalarNode('bundle');
104
    }
105
106
    /**
107
     * {@inheritdoc}
108
     */
109 2
    public function configure(DefinitionInterface $definition, Endpoint $endpoint, array $config): void
110
    {
111 2
        $node = null;
112 2
        $nodeClass = null;
113
114 2
        if (!($config['enabled'] ?? true)) {
115
            return;
116
        }
117
118 2
        if ($definition instanceof NodeAwareDefinitionInterface && isset($this->globalConfig['nodes']['enabled']) && $definition->getNode()) {
119 2
            $node = $definition->getNode();
120
121 2
            if (class_exists($node)) {
122
                $nodeClass = $node;
123
            } else {
124 2
                $nodeClass = $endpoint->getClassForType($node);
125
            }
126
127 2
            if (isset($this->globalConfig['nodes']['aliases'][$node])) {
128 1
                $node = $this->globalConfig['nodes']['aliases'][$node];
129
            }
130
131 2
            if ($node && \in_array($node, $this->globalConfig['nodes']['ignore'] ?? [], true)) {
132
                $node = null;
133
            }
134
        }
135
136 2
        $bundle = null;
137 2
        if ($this->globalConfig['bundles']['enabled'] ?? false) {
138 2
            if ($node && $nodeClass && $endpoint->hasType($node)) {
139 2
                preg_match_all('/\\\\?(\w+Bundle)\\\\/', $nodeClass, $matches);
140 2
                if ($matches) {
141 2
                    $bundle = current(array_reverse($matches[1]));
142
                }
143
144 2
                if (isset($this->globalConfig['bundles']['aliases'][$bundle])) {
145 2
                    $bundle = $this->globalConfig['bundles']['aliases'][$bundle];
146
                }
147
148 2
                if ($bundle && \in_array($bundle, $this->globalConfig['bundles']['ignore'] ?? [], true)) {
149 2
                    $bundle = null;
150
                }
151
152 2
                if ($bundle) {
153 2
                    $bundle = preg_replace('/Bundle$/', null, $bundle);
154
                }
155
            }
156
        }
157
158 2
        $node = $config['node'] ?? $node;
159 2
        $bundle = $config['bundle'] ?? $bundle;
160
161 2
        if ($bundle || $node) {
162 2
            $definition->setMeta('namespace', ['bundle' => $bundle, 'node' => $node]);
163
        }
164 2
    }
165
166
    /**
167
     * {@inheritDoc}
168
     */
169 1
    public function configureEndpoint(Endpoint $endpoint): void
170
    {
171 1
        $groupByBundle = $this->globalConfig['bundles']['enabled'] ?? false;
172 1
        $groupByNode = $this->globalConfig['bundles']['enabled'] ?? false;
173 1
        if ($groupByBundle || $groupByNode) {
174 1
            $endpoint->setQueries($this->namespaceDefinitions($endpoint->allQueries(), $endpoint));
175 1
            $endpoint->setMutations($this->namespaceDefinitions($endpoint->allMutations(), $endpoint));
176
        }
177 1
    }
178
179
    /**
180
     * @param array    $definitions
181
     * @param Endpoint $endpoint
182
     *
183
     * @return array
184
     */
185 1
    private function namespaceDefinitions(array $definitions, Endpoint $endpoint): array
186
    {
187 1
        $namespacedDefinitions = [];
188
        /** @var DefinitionInterface $definition */
189 1
        foreach ($definitions as $definition) {
190 1
            if (!$definition->hasMeta('namespace') || !$definition->getMeta('namespace')) {
191 1
                $namespacedDefinitions[$definition->getName()] = $definition;
192 1
                continue;
193
            }
194
195 1
            $root = null;
196 1
            $parent = null;
197 1
            $namespace = $definition->getMeta('namespace');
198 1
            if ($bundle = $namespace['bundle'] ?? null) {
199 1
                $bundleQuerySuffix = $this->globalConfig['bundle']['query_suffix'] ?? 'BundleQuery';
200 1
                $bundleMutationSuffix = $this->globalConfig['bundle']['mutation_suffix'] ?? 'BundleMutation';
201
202 1
                $name = lcfirst($bundle);
203 1
                $typeName = ucfirst($name).(($definition instanceof MutationDefinition) ? $bundleMutationSuffix : $bundleQuerySuffix);
204 1
                $root = $this->createRootNamespace(\get_class($definition), $name, $typeName, $endpoint);
205 1
                $parent = $endpoint->getType($root->getType());
206
            }
207
208 1
            if ($nodeName = $namespace['node'] ?? null) {
209 1
                if ($endpoint->hasTypeForClass($nodeName)) {
210 1
                    $nodeName = $endpoint->getTypeForClass($nodeName);
211
                }
212
213 1
                $name = Inflector::pluralize(lcfirst($nodeName));
214
215 1
                $querySuffix = $this->globalConfig['nodes']['query_suffix'] ?? 'Query';
216 1
                $mutationSuffix = $this->globalConfig['nodes']['mutation_suffix'] ?? 'Mutation';
217
218 1
                $typeName = ucfirst($nodeName).(($definition instanceof MutationDefinition) ? $mutationSuffix : $querySuffix);
219 1
                if (!$root) {
220 1
                    $root = $this->createRootNamespace(\get_class($definition), $name, $typeName, $endpoint);
221 1
                    $parent = $endpoint->getType($root->getType());
222 1
                } elseif ($parent) {
223 1
                    $parent = $this->createChildNamespace($parent, $name, $typeName, $endpoint);
224
                }
225
226
                //remove node suffix on namespaced definitions
227 1
                $definition->setName(preg_replace(sprintf("/(\w+)%s$/", $nodeName), '$1', $definition->getName()));
228 1
                $definition->setName(preg_replace(sprintf("/(\w+)%s$/", Inflector::pluralize($nodeName)), '$1', $definition->getName()));
229
            }
230
231 1
            if ($root && $parent) {
232 1
                $this->addDefinitionToNamespace($parent, $definition);
233 1
                $namespacedDefinitions[$root->getName()] = $root;
234
            } else {
235 1
                $namespacedDefinitions[$definition->getName()] = $definition;
236
            }
237
        }
238
239 1
        return $namespacedDefinitions;
240
    }
241
242
    /**
243
     * @param FieldsAwareDefinitionInterface $fieldsAwareDefinition
244
     * @param ExecutableDefinitionInterface  $definition
245
     */
246 1
    private function addDefinitionToNamespace(FieldsAwareDefinitionInterface $fieldsAwareDefinition, ExecutableDefinitionInterface $definition): void
247
    {
248 1
        $field = new FieldDefinition();
249 1
        $field->setName($definition->getName());
250 1
        $field->setType($definition->getType());
251 1
        $field->setResolver($definition->getResolver());
252 1
        $field->setArguments($definition->getArguments());
253 1
        $field->setList($definition->isList());
254 1
        $field->setMetas($definition->getMetas());
255 1
        $field->setNode($definition->getNode());
256 1
        $field->setComplexity($definition->getComplexity());
0 ignored issues
show
Bug introduced by
The method getComplexity() does not exist on Ynlo\GraphQLBundle\Defin...ableDefinitionInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to Ynlo\GraphQLBundle\Defin...ableDefinitionInterface. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

256
        $field->setComplexity($definition->/** @scrutinizer ignore-call */ getComplexity());
Loading history...
257 1
        $fieldsAwareDefinition->addField($field);
258 1
    }
259
260
    /**
261
     * @param ObjectDefinitionInterface $parent   parent definition to add a child field
262
     * @param string                    $name     name of the field
263
     * @param string                    $typeName name of the type to create
264
     * @param Endpoint                  $endpoint Endpoint instance to extract definitions
265
     *
266
     * @return ObjectDefinition
267
     */
268 1
    private function createChildNamespace(ObjectDefinitionInterface $parent, string $name, string $typeName, Endpoint $endpoint): ObjectDefinition
269
    {
270 1
        $child = new FieldDefinition();
271 1
        $child->setName($name);
272 1
        $child->setResolver(EmptyObjectResolver::class);
273
274 1
        $type = new ObjectDefinition();
275 1
        $type->setName($typeName);
276 1
        if ($endpoint->hasType($type->getName())) {
277 1
            $type = $endpoint->getType($type->getName());
278
        } else {
279 1
            $endpoint->add($type);
280
        }
281
282 1
        $child->setType($type->getName());
283 1
        $parent->addField($child);
284
285 1
        return $type;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $type could return the type Ynlo\GraphQLBundle\Definition\DefinitionInterface which includes types incompatible with the type-hinted return Ynlo\GraphQLBundle\Definition\ObjectDefinition. Consider adding an additional type-check to rule them out.
Loading history...
286
    }
287
288
    /**
289
     * @param string   $rootType Class of the root type to create QueryDefinition or MutationDefinition
290
     * @param string   $name     name of the root field
291
     * @param string   $typeName name for the root type
292
     * @param Endpoint $endpoint Endpoint interface to extract existent definitions
293
     *
294
     * @return ExecutableDefinitionInterface
295
     */
296 1
    private function createRootNamespace($rootType, $name, $typeName, Endpoint $endpoint): ExecutableDefinitionInterface
297
    {
298
        /** @var ExecutableDefinitionInterface $rootDefinition */
299 1
        $rootDefinition = new $rootType();
300 1
        $rootDefinition->setName($name);
301 1
        $rootDefinition->setResolver(EmptyObjectResolver::class);
302
303 1
        $type = new ObjectDefinition();
304 1
        $type->setName($typeName);
305 1
        if ($endpoint->hasType($type->getName())) {
306 1
            $type = $endpoint->getType($type->getName());
307
        } else {
308 1
            $endpoint->add($type);
309
        }
310
311 1
        $rootDefinition->setType($type->getName());
312
313 1
        return $rootDefinition;
314
    }
315
}
316