Completed
Push — master ( fcf75e...aa8db4 )
by Rafael
03:53
created

NamespaceDefinitionExtension   B

Complexity

Total Complexity 41

Size/Duplication

Total Lines 215
Duplicated Lines 17.21 %

Test Coverage

Coverage 77.14%

Importance

Changes 0
Metric Value
wmc 41
dl 37
loc 215
ccs 81
cts 105
cp 0.7714
rs 8.2769
c 0
b 0
f 0

7 Methods

Rating   Name   Duplication   Size   Complexity  
A createRootNamespace() 18 18 2
A addDefinitionToNamespace() 0 11 1
D configure() 0 52 21
C namespaceDefinitions() 0 48 11
A createChildNamespace() 18 18 2
A configureEndpoint() 0 7 3
A __construct() 0 3 1

How to fix   Duplicated Code    Complexity   

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:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like NamespaceDefinitionExtension often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use NamespaceDefinitionExtension, and based on these observations, apply Extract Interface, too.

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\Extension;
12
13
use Doctrine\Common\Util\Inflector;
14
use Ynlo\GraphQLBundle\Definition\DefinitionInterface;
15
use Ynlo\GraphQLBundle\Definition\ExecutableDefinitionInterface;
16
use Ynlo\GraphQLBundle\Definition\FieldDefinition;
17
use Ynlo\GraphQLBundle\Definition\FieldsAwareDefinitionInterface;
18
use Ynlo\GraphQLBundle\Definition\MutationDefinition;
19
use Ynlo\GraphQLBundle\Definition\NodeAwareDefinitionInterface;
20
use Ynlo\GraphQLBundle\Definition\ObjectDefinition;
21
use Ynlo\GraphQLBundle\Definition\ObjectDefinitionInterface;
22
use Ynlo\GraphQLBundle\Definition\Registry\Endpoint;
23
use Ynlo\GraphQLBundle\Model\NodeInterface;
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 NamespaceDefinitionExtension extends AbstractDefinitionExtension
31
{
32
    /**
33
     * @var array
34
     */
35
    protected $globalConfig = [];
36
37
    /**
38
     * PaginationDefinitionExtension constructor.
39
     *
40
     * @param array $config
41
     */
42 1
    public function __construct($config = [])
43
    {
44 1
        $this->globalConfig = $config;
45 1
    }
46
47
    /**
48
     * {@inheritdoc}
49
     */
50 1
    public function configure(DefinitionInterface $definition, Endpoint $endpoint, array $config)
51
    {
52 1
        $node = null;
53 1
        $nodeClass = null;
54 1
        if (($this->globalConfig['nodes']['enabled'] ?? false) && $definition instanceof NodeAwareDefinitionInterface && $definition->getNode()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $definition->getNode() of type null|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
55 1
            $node = $definition->getNode();
56
57 1
            if (class_exists($node)) {
58
                $nodeClass = $node;
59
            } else {
60 1
                $nodeClass = $endpoint->getClassForType($node);
61
            }
62
63 1
            if (!is_a($nodeClass, NodeInterface::class, true)) {
64
                return;
65
            }
66
67 1
            if (isset($this->globalConfig['nodes']['aliases'][$node])) {
68
                $node = $this->globalConfig['nodes']['aliases'][$node];
69
            }
70
71 1
            if ($node && in_array($node, $this->globalConfig['nodes']['ignore'])) {
72 1
                $node = null;
73
            }
74
        }
75
76 1
        $bundle = null;
77 1
        if ($this->globalConfig['bundles']['enabled'] ?? false) {
78 1
            if ($node && $nodeClass) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $nodeClass of type null|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
79 1
                if ($endpoint->hasType($node) && $nodeClass) {
80 1
                    preg_match_all('/\\\\(\w+Bundle)\\\\/', $nodeClass, $matches);
81 1
                    if ($matches) {
82 1
                        $bundle = current(array_reverse($matches[1]));
83
                    }
84
85 1
                    if (isset($this->globalConfig['bundles']['aliases'][$bundle])) {
86
                        $bundle = $this->globalConfig['bundles']['aliases'][$bundle];
87
                    }
88
89 1
                    if ($bundle && in_array($bundle, $this->globalConfig['bundles']['ignore'])) {
90 1
                        $bundle = null;
91
                    }
92
93 1
                    if ($bundle) {
94
                        $bundle = preg_replace('/Bundle$/', null, $bundle);
95
                    }
96
                }
97
            }
98
        }
99
100 1
        if ($bundle || $node) {
101 1
            $definition->setMeta('namespace', ['bundle' => $bundle, 'node' => $node]);
102
        }
103 1
    }
104
105
    /**
106
     * {@inheritDoc}
107
     */
108 1
    public function configureEndpoint(Endpoint $endpoint)
109
    {
110 1
        $groupByBundle = $this->globalConfig['bundles']['enabled'] ?? false;
111 1
        $groupByNode = $this->globalConfig['bundles']['enabled'] ?? false;
112 1
        if ($groupByBundle || $groupByNode) {
113 1
            $endpoint->setQueries($this->namespaceDefinitions($endpoint->allQueries(), $endpoint));
114 1
            $endpoint->setMutations($this->namespaceDefinitions($endpoint->allMutations(), $endpoint));
115
        }
116 1
    }
117
118
    /**
119
     * @param array    $definitions
120
     * @param Endpoint $endpoint
121
     *
122
     * @return array
123
     */
124 1
    private function namespaceDefinitions($definitions, Endpoint $endpoint)
125
    {
126 1
        $namespacedDefinitions = [];
127
        /** @var DefinitionInterface $definition */
128 1
        foreach ($definitions as $definition) {
129 1
            if (!$definition->hasMeta('namespace') || !$definition->getMeta('namespace')) {
130 1
                $namespacedDefinitions[] = $definition;
131 1
                continue;
132
            }
133
134 1
            $root = null;
135 1
            $parent = null;
136 1
            $namespace = $definition->getMeta('namespace');
137 1
            if ($bundle = $namespace['bundle'] ?? null) {
138
                $bundleSuffix = $this->globalConfig['bundles']['suffix'] ?? 'Bundle';
139
                $name = lcfirst($bundle);
140
                $typeName = ucfirst($name).$bundleSuffix;
141
                $root = $this->createRootNamespace(get_class($definition), $name, $typeName, $endpoint);
142
                $parent = $endpoint->getType($root->getType());
143
            }
144
145 1
            if ($nodeName = $namespace['node'] ?? null) {
146 1
                $name = Inflector::pluralize(lcfirst($nodeName));
147
148 1
                $querySuffix = $this->globalConfig['nodes']['query_suffix'] ?? 'Query';
149 1
                $mutationSuffix = $this->globalConfig['nodes']['mutation_suffix'] ?? 'Mutation';
150
151 1
                $typeName = ucfirst($nodeName).(($definition instanceof MutationDefinition) ? $mutationSuffix : $querySuffix);
152 1
                if (!$root) {
153 1
                    $root = $this->createRootNamespace(get_class($definition), $name, $typeName, $endpoint);
154 1
                    $parent = $endpoint->getType($root->getType());
155
                } elseif ($parent) {
156
                    $parent = $this->createChildNamespace($parent, $name, $typeName, $endpoint);
157
                }
158
159
                //remove node suffix on namespaced definitions
160 1
                $definition->setName(preg_replace(sprintf("/(\w+)%s$/", $nodeName), '$1', $definition->getName()));
161 1
                $definition->setName(preg_replace(sprintf("/(\w+)%s$/", Inflector::pluralize($nodeName)), '$1', $definition->getName()));
162
163
            }
164
165 1
            if ($root && $parent) {
166 1
                $this->addDefinitionToNamespace($parent, $definition);
167 1
                $namespacedDefinitions[] = $root;
168
            }
169
        }
170
171 1
        return $namespacedDefinitions;
172
    }
173
174
    /**
175
     * @param FieldsAwareDefinitionInterface $fieldsAwareDefinition
176
     * @param ExecutableDefinitionInterface  $definition
177
     */
178 1
    private function addDefinitionToNamespace(FieldsAwareDefinitionInterface $fieldsAwareDefinition, ExecutableDefinitionInterface $definition)
179
    {
180 1
        $field = new FieldDefinition();
181 1
        $field->setName($definition->getName());
182 1
        $field->setType($definition->getType());
183 1
        $field->setResolver($definition->getResolver());
184 1
        $field->setArguments($definition->getArguments());
185 1
        $field->setList($definition->isList());
186 1
        $field->setMetas($definition->getMetas());
187 1
        $field->setNode($definition->getNode());
188 1
        $fieldsAwareDefinition->addField($field);
189 1
    }
190
191
    /**
192
     * @param ObjectDefinitionInterface $parent   parent definition to add a child field
193
     * @param string                    $name     name of the field
194
     * @param string                    $typeName name of the type to create
195
     * @param Endpoint                  $endpoint Endpoint instance to extract definitions
196
     *
197
     * @return ObjectDefinition
198
     */
199 View Code Duplication
    private function createChildNamespace(ObjectDefinitionInterface $parent, $name, $typeName, Endpoint $endpoint): ObjectDefinition
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...
200
    {
201
        $child = new FieldDefinition();
202
        $child->setName($name);
203
        $child->setResolver(EmptyObjectResolver::class);
204
205
        $type = new ObjectDefinition();
206
        $type->setName($typeName);
207
        if ($endpoint->hasType($type->getName())) {
208
            $type = $endpoint->getType($type->getName());
209
        } else {
210
            $endpoint->add($type);
211
        }
212
213
        $child->setType($type->getName());
214
        $parent->addField($child);
215
216
        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...
217
    }
218
219
    /**
220
     * @param string   $rootType Class of the root type to create QueryDefinition or MutationDefinition
221
     * @param string   $name     name of the root field
222
     * @param string   $typeName name for the root type
223
     * @param Endpoint $endpoint Endpoint interface to extract existent definitions
224
     *
225
     * @return ExecutableDefinitionInterface
226
     */
227 1 View Code Duplication
    private function createRootNamespace($rootType, $name, $typeName, Endpoint $endpoint): ExecutableDefinitionInterface
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...
228
    {
229
        /** @var ExecutableDefinitionInterface $rootDefinition */
230 1
        $rootDefinition = new $rootType();
231 1
        $rootDefinition->setName($name);
232 1
        $rootDefinition->setResolver(EmptyObjectResolver::class);
233
234 1
        $type = new ObjectDefinition();
235 1
        $type->setName($typeName);
236 1
        if ($endpoint->hasType($type->getName())) {
237 1
            $type = $endpoint->getType($type->getName());
238
        } else {
239 1
            $endpoint->add($type);
240
        }
241
242 1
        $rootDefinition->setType($type->getName());
243
244 1
        return $rootDefinition;
245
    }
246
}
247