Completed
Push — develop ( 391d43...2463ed )
by Bastian
10s
created

DocumentMap::loadDoctrineClassMap()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 22
Code Lines 14

Duplication

Lines 22
Ratio 100 %

Code Coverage

Tests 13
CRAP Score 2
Metric Value
dl 22
loc 22
ccs 13
cts 13
cp 1
rs 9.2
cc 2
eloc 14
nc 2
nop 1
crap 2
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 8
    public function __construct(Finder $doctrineFinder, Finder $serializerFinder, Finder $validationFinder)
36
    {
37 8
        $doctrineMap = $this->loadDoctrineClassMap($doctrineFinder);
38 8
        $serializerMap = $this->loadSerializerClassMap($serializerFinder);
39 8
        $validationMap = $this->loadValidationClassMap($validationFinder);
40
41 8
        foreach ($doctrineMap as $className => $doctrineMapping) {
42 8
            $this->mappings[$className] = [
43 8
                'doctrine'   => $doctrineMap[$className],
44 8
                'serializer' => isset($serializerMap[$className]) ? $serializerMap[$className] : null,
45 8
                'validation' => isset($validationMap[$className]) ? $validationMap[$className] : null,
46
            ];
47
        }
48 8
    }
49
50
    /**
51
     * Get document
52
     *
53
     * @param string $className Document class
54
     * @return Document
55
     */
56 8
    public function getDocument($className)
57
    {
58 8
        if (isset($this->documents[$className])) {
59 8
            return $this->documents[$className];
60
        }
61 8
        if (!isset($this->mappings[$className])) {
62
            throw new \InvalidArgumentException(sprintf('No XML mapping found for document "%s"', $className));
63
        }
64
65 8
        return $this->documents[$className] = $this->processDocument(
66
            $className,
67 8
            $this->mappings[$className]['doctrine'],
68 8
            $this->mappings[$className]['serializer'],
69 8
            $this->mappings[$className]['validation']
70
        );
71
    }
72
73
    /**
74
     * Get all documents
75
     *
76
     * @return Document[]
77
     */
78 6
    public function getDocuments()
79
    {
80 6
        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
89
     * @param \DOMElement $validationMapping Validation XML mapping
90
     * @return Document
91
     */
92 8
    private function processDocument(
93
        $className,
94
        \DOMElement $doctrineMapping,
95
        \DOMElement $serializerMapping = null,
96
        \DOMElement $validationMapping = null
97
    ) {
98 8
        if ($serializerMapping === null) {
99 2
            $serializerFields = [];
100
        } else {
101 8
            $serializerFields = array_reduce(
102 8
                $this->getSerializerFields($serializerMapping),
103
                function (array $fields, array $field) {
104 8
                    $fields[$field['fieldName']] = $field;
105 8
                    return $fields;
106 8
                },
107 8
                []
108
            );
109
        }
110
111 8
        if ($validationMapping === null) {
112 2
            $validationFields = [];
113
        } else {
114 8
            $validationFields = array_reduce(
115 8
                $this->getValidationFields($validationMapping),
116
                function (array $fields, array $field) {
117 5
                    $fields[$field['fieldName']] = $field;
118 5
                    return $fields;
119 8
                },
120 8
                []
121
            );
122
        }
123
124 8
        $fields = [];
125 8
        foreach ($this->getDoctrineFields($doctrineMapping) as $doctrineField) {
126 8
            $serializerField = isset($serializerFields[$doctrineField['name']]) ?
127 8
                $serializerFields[$doctrineField['name']] :
128 8
                null;
129 8
            $validationField = isset($validationFields[$doctrineField['name']]) ?
130 5
                $validationFields[$doctrineField['name']] :
131 8
                null;
132
133 8
            if ($doctrineField['type'] === 'collection') {
134 2
                $fields[] = new ArrayField(
135 2
                    $serializerField === null ? 'array<string>' : $serializerField['fieldType'],
136 2
                    $doctrineField['name'],
137 2
                    $serializerField === null ? $doctrineField['name'] : $serializerField['exposedName'],
138 2
                    $serializerField === null ? false : $serializerField['readOnly'],
139 2
                    $validationField === null ? false : $validationField['required']
140
                );
141
            } else {
142 8
                $fields[] = new Field(
143 8
                    $doctrineField['type'],
144 8
                    $doctrineField['name'],
145 8
                    $serializerField === null ? $doctrineField['name'] : $serializerField['exposedName'],
146 8
                    $serializerField === null ? false : $serializerField['readOnly'],
147 8
                    $validationField === null ? false : $validationField['required']
148
                );
149
            }
150
        }
151 8 View Code Duplication
        foreach ($this->getDoctrineEmbedOneFields($doctrineMapping) as $doctrineField) {
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...
152 8
            $serializerField = isset($serializerFields[$doctrineField['name']]) ?
153 8
                $serializerFields[$doctrineField['name']] :
154 8
                null;
155 8
            $validationField = isset($validationFields[$doctrineField['name']]) ?
156 5
                $validationFields[$doctrineField['name']] :
157 8
                null;
158
159 8
            $fields[] = new EmbedOne(
160 8
                $this->getDocument($doctrineField['type']),
161 8
                $doctrineField['name'],
162 8
                $serializerField === null ? $doctrineField['name'] : $serializerField['exposedName'],
163 8
                $serializerField === null ? false : $serializerField['readOnly'],
164 8
                $validationField === null ? false : $validationField['required']
165
            );
166
        }
167 8 View Code Duplication
        foreach ($this->getDoctrineEmbedManyFields($doctrineMapping) as $doctrineField) {
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...
168 8
            $serializerField = isset($serializerFields[$doctrineField['name']]) ?
169 8
                $serializerFields[$doctrineField['name']] :
170 8
                null;
171 8
            $validationField = isset($validationFields[$doctrineField['name']]) ?
172 2
                $validationFields[$doctrineField['name']] :
173 8
                null;
174
175 8
            $fields[] = new EmbedMany(
176 8
                $this->getDocument($doctrineField['type']),
177 8
                $doctrineField['name'],
178 8
                $serializerField === null ? $doctrineField['name'] : $serializerField['exposedName'],
179 8
                $serializerField === null ? false : $serializerField['readOnly'],
180 8
                $validationField === null ? false : $validationField['required']
181
            );
182
        }
183
184 8
        return new Document($className, $fields);
185
    }
186
187
    /**
188
     * Load doctrine class map
189
     *
190
     * @param Finder $finder Mapping finder
191
     * @return array
192
     */
193 8 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...
194
    {
195 8
        $classMap = [];
196 8
        foreach ($finder as $file) {
197 8
            $document = new \DOMDocument();
198 8
            $document->load($file);
199
200 8
            $xpath = new \DOMXPath($document);
201 8
            $xpath->registerNamespace('doctrine', 'http://doctrine-project.org/schemas/odm/doctrine-mongo-mapping');
202
203 8
            $classMap = array_reduce(
204 8
                iterator_to_array($xpath->query('//*[self::doctrine:document or self::doctrine:embedded-document]')),
205
                function (array $classMap, \DOMElement $element) {
206 8
                    $classMap[$element->getAttribute('name')] = $element;
207 8
                    return $classMap;
208 8
                },
209
                $classMap
210
            );
211
        }
212
213 8
        return $classMap;
214
    }
215
216
    /**
217
     * Load serializer class map
218
     *
219
     * @param Finder $finder Mapping finder
220
     * @return array
221
     */
222 8 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...
223
    {
224 8
        $classMap = [];
225 8
        foreach ($finder as $file) {
226 8
            $document = new \DOMDocument();
227 8
            $document->load($file);
228
229 8
            $xpath = new \DOMXPath($document);
230
231 8
            $classMap = array_reduce(
232 8
                iterator_to_array($xpath->query('//class')),
233
                function (array $classMap, \DOMElement $element) {
234 8
                    $classMap[$element->getAttribute('name')] = $element;
235 8
                    return $classMap;
236 8
                },
237
                $classMap
238
            );
239
        }
240
241 8
        return $classMap;
242
    }
243
244
    /**
245
     * Load validation class map
246
     *
247
     * @param Finder $finder Mapping finder
248
     * @return array
249
     */
250 8 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...
251
    {
252 8
        $classMap = [];
253 8
        foreach ($finder as $file) {
254 8
            $document = new \DOMDocument();
255 8
            $document->load($file);
256
257 8
            $xpath = new \DOMXPath($document);
258 8
            $xpath->registerNamespace('constraint', 'http://symfony.com/schema/dic/constraint-mapping');
259
260 8
            $classMap = array_reduce(
261 8
                iterator_to_array($xpath->query('//constraint:class')),
262
                function (array $classMap, \DOMElement $element) {
263 8
                    $classMap[$element->getAttribute('name')] = $element;
264 8
                    return $classMap;
265 8
                },
266
                $classMap
267
            );
268
        }
269
270 8
        return $classMap;
271
    }
272
273
    /**
274
     * Get serializer fields
275
     *
276
     * @param \DOMElement $mapping Serializer XML mapping
277
     * @return array
278
     */
279 8
    private function getSerializerFields(\DOMElement $mapping)
280
    {
281 8
        $xpath = new \DOMXPath($mapping->ownerDocument);
282
283 8
        return array_map(
284
            function (\DOMElement $element) {
285
                return [
286 8
                    'fieldName'   => $element->getAttribute('name'),
287 8
                    'fieldType'   => $this->getSerializerFieldType($element),
288 8
                    'exposedName' => $element->getAttribute('serialized-name') ?: $element->getAttribute('name'),
289 8
                    'readOnly'    => $element->getAttribute('read-only') === 'true',
290
                ];
291 8
            },
292 8
            iterator_to_array($xpath->query('property', $mapping))
293
        );
294
    }
295
296
    /**
297
     * Get serializer field type
298
     *
299
     * @param \DOMElement $field Field node
300
     * @return string|null
301
     */
302 8
    private function getSerializerFieldType(\DOMElement $field)
303
    {
304 8
        if ($field->getAttribute('type')) {
305 8
            return $field->getAttribute('type');
306
        }
307
308 2
        $xpath = new \DOMXPath($field->ownerDocument);
309
310 2
        $type = $xpath->query('type', $field)->item(0);
311 2
        return $type === null ? null : $type->nodeValue;
312
    }
313
314
    /**
315
     * Get validation fields
316
     *
317
     * @param \DOMElement $mapping Validation XML mapping
318
     * @return array
319
     */
320 8
    private function getValidationFields(\DOMElement $mapping)
321
    {
322 8
        $xpath = new \DOMXPath($mapping->ownerDocument);
323 8
        $xpath->registerNamespace('constraint', 'http://symfony.com/schema/dic/constraint-mapping');
324
325 8
        return array_map(
326
            function (\DOMElement $element) use ($xpath) {
327 5
                $constraints = $xpath->query('constraint:constraint[@name="NotBlank" or @name="NotNull"]', $element);
328
                return [
329 5
                    'fieldName' => $element->getAttribute('name'),
330 5
                    'required'  => $constraints->length > 0,
331
                ];
332 8
            },
333 8
            iterator_to_array($xpath->query('constraint:property', $mapping))
334
        );
335
    }
336
337
    /**
338
     * Get doctrine document fields
339
     *
340
     * @param \DOMElement $mapping Doctrine XML mapping
341
     * @return array
342
     */
343 8 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...
344
    {
345 8
        $xpath = new \DOMXPath($mapping->ownerDocument);
346 8
        $xpath->registerNamespace('doctrine', 'http://doctrine-project.org/schemas/odm/doctrine-mongo-mapping');
347
348 8
        return array_map(
349
            function (\DOMElement $element) {
350
                return [
351 8
                    'name' => $element->getAttribute('fieldName'),
352 8
                    'type' => $element->getAttribute('type'),
353
                ];
354 8
            },
355 8
            iterator_to_array($xpath->query('doctrine:field', $mapping))
356
        );
357
    }
358
359
    /**
360
     * Get doctrine document embed-one fields
361
     *
362
     * @param \DOMElement $mapping Doctrine XML mapping
363
     * @return array
364
     */
365 8 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...
366
    {
367 8
        $xpath = new \DOMXPath($mapping->ownerDocument);
368 8
        $xpath->registerNamespace('doctrine', 'http://doctrine-project.org/schemas/odm/doctrine-mongo-mapping');
369
370 8
        return array_map(
371
            function (\DOMElement $element) {
372
                return [
373 8
                    'name' => $element->getAttribute('field'),
374 8
                    'type' => $element->getAttribute('target-document'),
375
                ];
376 8
            },
377 8
            iterator_to_array($xpath->query('*[self::doctrine:embed-one or self::doctrine:reference-one]', $mapping))
378
        );
379
    }
380
381
    /**
382
     * Get doctrine document embed-many fields
383
     *
384
     * @param \DOMElement $mapping Doctrine XML mapping
385
     * @return array
386
     */
387 8 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...
388
    {
389 8
        $xpath = new \DOMXPath($mapping->ownerDocument);
390 8
        $xpath->registerNamespace('doctrine', 'http://doctrine-project.org/schemas/odm/doctrine-mongo-mapping');
391
392 8
        return array_map(
393 8
            function (\DOMElement $element) {
394
                return [
395 8
                    'name' => $element->getAttribute('field'),
396 8
                    'type' => $element->getAttribute('target-document'),
397
                ];
398 8
            },
399 8
            iterator_to_array($xpath->query('*[self::doctrine:embed-many or self::doctrine:reference-many]', $mapping))
400
        );
401
    }
402
}
403