Failed Conditions
Push — feature/EVO-4410-username-id-t... ( 832965...980f0e )
by
unknown
23:08 queued 10:56
created

DocumentMap::processDocument()   F

Complexity

Conditions 29
Paths 900

Size

Total Lines 97
Code Lines 73

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 80
CRAP Score 29

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 97
ccs 80
cts 80
cp 1
rs 2.1641
cc 29
eloc 73
nc 900
nop 4
crap 29

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
 * DocumentMap class file
4
 */
5
6
namespace Graviton\DocumentBundle\DependencyInjection\Compiler\Utils;
7
8
use Symfony\Component\Finder\Finder;
9
10
/**
11
 * Document map
12
 *
13
 * @author   List of contributors <https://github.com/libgraviton/graviton/graphs/contributors>
14
 * @license  http://opensource.org/licenses/gpl-license.php GNU Public License
15
 * @link     http://swisscom.ch
16
 */
17
class DocumentMap
18
{
19
    /**
20
     * @var array
21
     */
22
    private $mappings = [];
23
    /**
24
     * @var Document[]
25
     */
26
    private $documents = [];
27
28
    /**
29
     * Constructor
30
     *
31
     * @param Finder $doctrineFinder   Doctrine mapping finder
32
     * @param Finder $serializerFinder Serializer mapping finder
33
     * @param Finder $validationFinder Validation mapping finder
34
     */
35 16
    public function __construct(Finder $doctrineFinder, Finder $serializerFinder, Finder $validationFinder)
36
    {
37 16
        $doctrineMap = $this->loadDoctrineClassMap($doctrineFinder);
38 16
        $serializerMap = $this->loadSerializerClassMap($serializerFinder);
39 16
        $validationMap = $this->loadValidationClassMap($validationFinder);
40
41 16
        foreach ($doctrineMap as $className => $doctrineMapping) {
42 16
            $this->mappings[$className] = [
43 16
                'doctrine'   => $doctrineMap[$className],
44 16
                'serializer' => isset($serializerMap[$className]) ? $serializerMap[$className] : null,
45 16
                'validation' => isset($validationMap[$className]) ? $validationMap[$className] : null,
46
            ];
47 8
        }
48 16
    }
49
50
    /**
51
     * Get document
52
     *
53
     * @param string $className Document class
54
     * @return Document
55
     */
56 16
    public function getDocument($className)
57
    {
58 16
        if (isset($this->documents[$className])) {
59 16
            return $this->documents[$className];
60
        }
61 16
        if (!isset($this->mappings[$className])) {
62
            throw new \InvalidArgumentException(sprintf('No XML mapping found for document "%s"', $className));
63
        }
64
65 16
        return $this->documents[$className] = $this->processDocument(
66 8
            $className,
67 16
            $this->mappings[$className]['doctrine'],
68 16
            $this->mappings[$className]['serializer'],
69 16
            $this->mappings[$className]['validation']
70 8
        );
71
    }
72
73
    /**
74
     * Get all documents
75
     *
76
     * @return Document[]
77
     */
78 12
    public function getDocuments()
79
    {
80 12
        return array_map([$this, 'getDocument'], array_keys($this->mappings));
81
    }
82
83
    /**
84
     * Process document
85
     *
86
     * @param string      $className         Class name
87
     * @param \DOMElement $doctrineMapping   Doctrine XML mapping
88
     * @param \DOMElement $serializerMapping Serializer XML mapping
0 ignored issues
show
Documentation introduced by
Should the type for parameter $serializerMapping not be null|\DOMElement?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
89
     * @param \DOMElement $validationMapping Validation XML mapping
0 ignored issues
show
Documentation introduced by
Should the type for parameter $validationMapping not be null|\DOMElement?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
90
     * @return Document
91
     */
92 16
    private function processDocument(
93
        $className,
94
        \DOMElement $doctrineMapping,
95
        \DOMElement $serializerMapping = null,
96
        \DOMElement $validationMapping = null
97
    ) {
98 16
        if ($serializerMapping === null) {
99 4
            $serializerFields = [];
100 2
        } else {
101 16
            $serializerFields = array_reduce(
102 16
                $this->getSerializerFields($serializerMapping),
103
                function (array $fields, array $field) {
104 16
                    $fields[$field['fieldName']] = $field;
105 16
                    return $fields;
106 16
                },
107 16
                []
108 8
            );
109
        }
110
111 16
        if ($validationMapping === null) {
112 4
            $validationFields = [];
113 2
        } else {
114 16
            $validationFields = array_reduce(
115 16
                $this->getValidationFields($validationMapping),
116
                function (array $fields, array $field) {
117 10
                    $fields[$field['fieldName']] = $field;
118 10
                    return $fields;
119 16
                },
120 16
                []
121 8
            );
122
        }
123
124 16
        $fields = [];
125 16
        foreach ($this->getDoctrineFields($doctrineMapping) as $doctrineField) {
126 16
            $serializerField = isset($serializerFields[$doctrineField['name']]) ?
127 16
                $serializerFields[$doctrineField['name']] :
128 16
                null;
129 16
            $validationField = isset($validationFields[$doctrineField['name']]) ?
130 13
                $validationFields[$doctrineField['name']] :
131 16
                null;
132
133 16
            if ($doctrineField['type'] === 'collection') {
134 4
                $fields[] = new ArrayField(
135 4
                    $serializerField === null ? 'array<string>' : $serializerField['fieldType'],
136 4
                    $doctrineField['name'],
137 4
                    $serializerField === null ? $doctrineField['name'] : $serializerField['exposedName'],
138 4
                    $serializerField === null ? false : $serializerField['readOnly'],
139 4
                    $validationField === null ? false : $validationField['required'],
140 4
                    $serializerField === null ? false : $serializerField['searchable']
0 ignored issues
show
Unused Code introduced by
The call to ArrayField::__construct() has too many arguments starting with $serializerField === nul...izerField['searchable'].

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
141 2
                );
142 2
            } else {
143 16
                $fields[] = new Field(
144 16
                    $doctrineField['type'],
145 16
                    $doctrineField['name'],
146 16
                    $serializerField === null ? $doctrineField['name'] : $serializerField['exposedName'],
147 16
                    $serializerField === null ? false : $serializerField['readOnly'],
148 16
                    $validationField === null ? false : $validationField['required'],
149 16
                    $serializerField === null ? false : $serializerField['searchable']
150 8
                );
151
            }
152 8
        }
153 16
        foreach ($this->getDoctrineEmbedOneFields($doctrineMapping) as $doctrineField) {
154 16
            $serializerField = isset($serializerFields[$doctrineField['name']]) ?
155 16
                $serializerFields[$doctrineField['name']] :
156 16
                null;
157 16
            $validationField = isset($validationFields[$doctrineField['name']]) ?
158 13
                $validationFields[$doctrineField['name']] :
159 16
                null;
160
161 16
            $fields[] = new EmbedOne(
162 16
                $this->getDocument($doctrineField['type']),
163 16
                $doctrineField['name'],
164 16
                $serializerField === null ? $doctrineField['name'] : $serializerField['exposedName'],
165 16
                $serializerField === null ? false : $serializerField['readOnly'],
166 16
                $validationField === null ? false : $validationField['required'],
167 16
                $serializerField === null ? false : $serializerField['searchable']
0 ignored issues
show
Unused Code introduced by
The call to EmbedOne::__construct() has too many arguments starting with $serializerField === nul...izerField['searchable'].

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
168 8
            );
169 8
        }
170 16
        foreach ($this->getDoctrineEmbedManyFields($doctrineMapping) as $doctrineField) {
171 16
            $serializerField = isset($serializerFields[$doctrineField['name']]) ?
172 16
                $serializerFields[$doctrineField['name']] :
173 16
                null;
174 16
            $validationField = isset($validationFields[$doctrineField['name']]) ?
175 10
                $validationFields[$doctrineField['name']] :
176 16
                null;
177
178 16
            $fields[] = new EmbedMany(
179 16
                $this->getDocument($doctrineField['type']),
180 16
                $doctrineField['name'],
181 16
                $serializerField === null ? $doctrineField['name'] : $serializerField['exposedName'],
182 16
                $serializerField === null ? false : $serializerField['readOnly'],
183 16
                $validationField === null ? false : $validationField['required']
184 8
            );
185 8
        }
186
187 16
        return new Document($className, $fields);
188
    }
189
190
    /**
191
     * Load doctrine class map
192
     *
193
     * @param Finder $finder Mapping finder
194
     * @return array
195
     */
196 16 View Code Duplication
    private function loadDoctrineClassMap(Finder $finder)
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...
197
    {
198 16
        $classMap = [];
199 16
        foreach ($finder as $file) {
200 16
            $document = new \DOMDocument();
201 16
            $document->load($file);
202
203 16
            $xpath = new \DOMXPath($document);
204 16
            $xpath->registerNamespace('doctrine', 'http://doctrine-project.org/schemas/odm/doctrine-mongo-mapping');
205
206 16
            $classMap = array_reduce(
207 16
                iterator_to_array($xpath->query('//*[self::doctrine:document or self::doctrine:embedded-document]')),
208
                function (array $classMap, \DOMElement $element) {
209 16
                    $classMap[$element->getAttribute('name')] = $element;
210 16
                    return $classMap;
211 16
                },
212
                $classMap
213 8
            );
214 8
        }
215
216 16
        return $classMap;
217
    }
218
219
    /**
220
     * Load serializer class map
221
     *
222
     * @param Finder $finder Mapping finder
223
     * @return array
224
     */
225 16 View Code Duplication
    private function loadSerializerClassMap(Finder $finder)
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...
226
    {
227 16
        $classMap = [];
228 16
        foreach ($finder as $file) {
229 16
            $document = new \DOMDocument();
230 16
            $document->load($file);
231
232 16
            $xpath = new \DOMXPath($document);
233
234 16
            $classMap = array_reduce(
235 16
                iterator_to_array($xpath->query('//class')),
236
                function (array $classMap, \DOMElement $element) {
237 16
                    $classMap[$element->getAttribute('name')] = $element;
238 16
                    return $classMap;
239 16
                },
240
                $classMap
241 8
            );
242 8
        }
243
244 16
        return $classMap;
245
    }
246
247
    /**
248
     * Load validation class map
249
     *
250
     * @param Finder $finder Mapping finder
251
     * @return array
252
     */
253 16 View Code Duplication
    private function loadValidationClassMap(Finder $finder)
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...
254
    {
255 16
        $classMap = [];
256 16
        foreach ($finder as $file) {
257 16
            $document = new \DOMDocument();
258 16
            $document->load($file);
259
260 16
            $xpath = new \DOMXPath($document);
261 16
            $xpath->registerNamespace('constraint', 'http://symfony.com/schema/dic/constraint-mapping');
262
263 16
            $classMap = array_reduce(
264 16
                iterator_to_array($xpath->query('//constraint:class')),
265
                function (array $classMap, \DOMElement $element) {
266 16
                    $classMap[$element->getAttribute('name')] = $element;
267 16
                    return $classMap;
268 16
                },
269
                $classMap
270 8
            );
271 8
        }
272
273 16
        return $classMap;
274
    }
275
276
    /**
277
     * Get serializer fields
278
     *
279
     * @param \DOMElement $mapping Serializer XML mapping
280
     * @return array
281
     */
282 16
    private function getSerializerFields(\DOMElement $mapping)
283
    {
284 16
        $xpath = new \DOMXPath($mapping->ownerDocument);
285
286 16
        return array_map(
287
            function (\DOMElement $element) {
288
                return [
289 16
                    'fieldName'   => $element->getAttribute('name'),
290 16
                    'fieldType'   => $this->getSerializerFieldType($element),
291 16
                    'exposedName' => $element->getAttribute('serialized-name') ?: $element->getAttribute('name'),
292 16
                    'readOnly'    => $element->getAttribute('read-only') === 'true',
293 16
                    'searchable'  => $element->getAttribute('searchable') === 'true',
294 8
                ];
295 16
            },
296 16
            iterator_to_array($xpath->query('property', $mapping))
297 8
        );
298
    }
299
300
    /**
301
     * Get serializer field type
302
     *
303
     * @param \DOMElement $field Field node
304
     * @return string|null
305
     */
306 16
    private function getSerializerFieldType(\DOMElement $field)
307
    {
308 16
        if ($field->getAttribute('type')) {
309 16
            return $field->getAttribute('type');
310
        }
311
312 4
        $xpath = new \DOMXPath($field->ownerDocument);
313
314 4
        $type = $xpath->query('type', $field)->item(0);
315 4
        return $type === null ? null : $type->nodeValue;
316
    }
317
318
    /**
319
     * Get validation fields
320
     *
321
     * @param \DOMElement $mapping Validation XML mapping
322
     * @return array
323
     */
324 16
    private function getValidationFields(\DOMElement $mapping)
325
    {
326 16
        $xpath = new \DOMXPath($mapping->ownerDocument);
327 16
        $xpath->registerNamespace('constraint', 'http://symfony.com/schema/dic/constraint-mapping');
328
329 16
        return array_map(
330
            function (\DOMElement $element) use ($xpath) {
331 10
                $constraints = $xpath->query('constraint:constraint[@name="NotBlank" or @name="NotNull"]', $element);
332
                return [
333 10
                    'fieldName' => $element->getAttribute('name'),
334 10
                    'required'  => $constraints->length > 0,
335 5
                ];
336 16
            },
337 16
            iterator_to_array($xpath->query('constraint:property', $mapping))
338 8
        );
339
    }
340
341
    /**
342
     * Get doctrine document fields
343
     *
344
     * @param \DOMElement $mapping Doctrine XML mapping
345
     * @return array
346
     */
347 16 View Code Duplication
    private function getDoctrineFields(\DOMElement $mapping)
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...
348
    {
349 16
        $xpath = new \DOMXPath($mapping->ownerDocument);
350 16
        $xpath->registerNamespace('doctrine', 'http://doctrine-project.org/schemas/odm/doctrine-mongo-mapping');
351
352 16
        return array_map(
353
            function (\DOMElement $element) {
354
                return [
355 16
                    'name' => $element->getAttribute('fieldName'),
356 16
                    'type' => $element->getAttribute('type'),
357 8
                ];
358 16
            },
359 16
            iterator_to_array($xpath->query('doctrine:field', $mapping))
360 8
        );
361
    }
362
363
    /**
364
     * Get doctrine document embed-one fields
365
     *
366
     * @param \DOMElement $mapping Doctrine XML mapping
367
     * @return array
368
     */
369 16 View Code Duplication
    private function getDoctrineEmbedOneFields(\DOMElement $mapping)
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...
370
    {
371 16
        $xpath = new \DOMXPath($mapping->ownerDocument);
372 16
        $xpath->registerNamespace('doctrine', 'http://doctrine-project.org/schemas/odm/doctrine-mongo-mapping');
373
374 16
        return array_map(
375
            function (\DOMElement $element) {
376
                return [
377 16
                    'name' => $element->getAttribute('field'),
378 16
                    'type' => $element->getAttribute('target-document'),
379 8
                ];
380 16
            },
381 16
            iterator_to_array($xpath->query('*[self::doctrine:embed-one or self::doctrine:reference-one]', $mapping))
382 8
        );
383
    }
384
385
    /**
386
     * Get doctrine document embed-many fields
387
     *
388
     * @param \DOMElement $mapping Doctrine XML mapping
389
     * @return array
390
     */
391 16 View Code Duplication
    private function getDoctrineEmbedManyFields(\DOMElement $mapping)
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...
392
    {
393 16
        $xpath = new \DOMXPath($mapping->ownerDocument);
394 16
        $xpath->registerNamespace('doctrine', 'http://doctrine-project.org/schemas/odm/doctrine-mongo-mapping');
395
396 16
        return array_map(
397 16
            function (\DOMElement $element) {
398
                return [
399 16
                    'name' => $element->getAttribute('field'),
400 16
                    'type' => $element->getAttribute('target-document'),
401 8
                ];
402 16
            },
403 16
            iterator_to_array($xpath->query('*[self::doctrine:embed-many or self::doctrine:reference-many]', $mapping))
404 8
        );
405
    }
406
}
407