YamlFileDriver::setRelationships()   F
last analyzed

Complexity

Conditions 13
Paths 1537

Size

Total Lines 56
Code Lines 30

Duplication

Lines 7
Ratio 12.5 %

Importance

Changes 0
Metric Value
dl 7
loc 56
rs 3.3579
c 0
b 0
f 0
cc 13
eloc 30
nc 1537
nop 2

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
namespace As3\Modlr\Metadata\Driver;
4
5
use As3\Modlr\Metadata;
6
use As3\Modlr\Exception\RuntimeException;
7
use As3\Modlr\Exception\MetadataException;
8
use Symfony\Component\Yaml\Yaml;
9
10
/**
11
 * The YAML metadata file driver.
12
 *
13
 * @author Jacob Bare <[email protected]>
14
 */
15
final class YamlFileDriver extends AbstractFileDriver
16
{
17
    /**
18
     * An in-memory cache of parsed metadata mappings (from file).
19
     *
20
     * @var array
21
     */
22
    private $mappings = [
23
        'model' => [],
24
        'mixin' => [],
25
        'embed' => [],
26
    ];
27
28
    /**
29
     * {@inheritDoc}
30
     */
31
    protected function loadMetadataFromFile($type, $file)
32
    {
33
        $mapping = $this->getMapping('model', $type, $file);
34
35
        $metadata = new Metadata\EntityMetadata($type);
36
37
        if (isset($mapping['entity']['abstract'])) {
38
            $metadata->setAbstract(true);
39
        }
40
41 View Code Duplication
        if (isset($mapping['entity']['polymorphic'])) {
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...
42
            $metadata->setPolymorphic(true);
43
            $metadata->ownedTypes = $this->getOwnedTypes($metadata->type);
44
        }
45
46
        if (isset($mapping['entity']['extends'])) {
47
            $metadata->extends = $mapping['entity']['extends'];
48
        }
49
50 View Code Duplication
        if (isset($mapping['entity']['defaultValues']) && is_array($mapping['entity']['defaultValues'])) {
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...
51
            $metadata->defaultValues = $mapping['entity']['defaultValues'];
52
        }
53
54
        $this->setPersistence($metadata, $mapping['entity']['persistence']);
55
        $this->setSearch($metadata, $mapping['entity']['search']);
56
        $this->setAttributes($metadata, $mapping['attributes']);
57
        $this->setRelationships($metadata, $mapping['relationships']);
58
        $this->setEmbeds($metadata, $mapping['embeds']);
59
        $this->setMixins($metadata, $mapping['mixins']);
60
        return $metadata;
61
    }
62
63
    /**
64
     * {@inheritDoc}
65
     */
66 View Code Duplication
    protected function loadEmbedFromFile($embedName, $file)
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...
67
    {
68
        $mapping = $this->getMapping('embed', $embedName, $file);
69
70
        $embed = new Metadata\EmbedMetadata($embedName);
71
        $this->setAttributes($embed, $mapping['attributes']);
72
        $this->setEmbeds($embed, $mapping['embeds']);
73
        $this->setMixins($embed, $mapping['mixins']);
74
        return $embed;
75
    }
76
77
    /**
78
     * {@inheritDoc}
79
     */
80 View Code Duplication
    protected function loadMixinFromFile($mixinName, $file)
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...
81
    {
82
        $mapping = $this->getMapping('mixin', $mixinName, $file);
83
84
        $mixin = new Metadata\MixinMetadata($mixinName);
85
86
        $this->setAttributes($mixin, $mapping['attributes']);
87
        $this->setEmbeds($mixin, $mapping['embeds']);
88
        $this->setRelationships($mixin, $mapping['relationships']);
89
        return $mixin;
90
    }
91
92
    /**
93
     * {@inheritDoc}
94
     */
95
    public function getTypeHierarchy($type, array $types = [])
96
    {
97
        $path = $this->getFilePath('model', $type);
98
        $mapping = $this->getMapping('model', $type, $path);
99
100
        $types[] = $type;
101
        if (isset($mapping['entity']['extends']) && $mapping['entity']['extends'] !== $type) {
102
            return $this->getTypeHierarchy($mapping['entity']['extends'], $types);
103
        }
104
        return array_reverse($types);
105
    }
106
107
    /**
108
     * {@inheritDoc}
109
     */
110
    public function getOwnedTypes($type, array $types = [])
111
    {
112
        $path = $this->getFilePath('model', $type);
113
        $superMapping = $this->getMapping('model', $type, $path);
114
115
        $abstract = isset($superMapping['entity']['abstract']) && true === $superMapping['entity']['abstract'];
116
117
        foreach ($this->getAllTypeNames() as $searchType) {
118
119
            if ($type === $searchType && false === $abstract) {
120
                $types[] = $type;
121
                continue;
122
            }
123
            if (0 !== strpos($searchType, $type)) {
124
                continue;
125
            }
126
127
            $path = $this->getFilePath('model', $searchType);
128
            $mapping = $this->getMapping('model', $searchType, $path);
129
130
            if (!isset($mapping['entity']['extends']) || $mapping['entity']['extends'] !== $type) {
131
                continue;
132
            }
133
            $types[] = $searchType;
134
        }
135
        return $types;
136
    }
137
138
    /**
139
     * Gets the metadata mapping information from the YAML file.
140
     *
141
     * @param   string  $metaType   The metadata type, either mixin or model.
142
     * @param   string  $key        The metadata key name, either the mixin name or model type.
143
     * @param   string  $file       The YAML file location.
144
     * @return  array
145
     * @throws  MetadataException
146
     */
147
    private function getMapping($metaType, $key, $file)
148
    {
149
        if (isset($this->mappings[$metaType][$key])) {
150
            // Set to array cache to prevent multiple gets/parses.
151
            return $this->mappings[$metaType][$key];
152
        }
153
154
        $contents = Yaml::parse(file_get_contents($file));
155
        if (!isset($contents[$key])) {
156
            throw MetadataException::fatalDriverError($key, sprintf('No mapping key was found at the beginning of the YAML file. Expected "%s"', $key));
157
        }
158
        return $this->mappings[$metaType][$key] = $this->setDefaults($metaType, $contents[$key]);
159
    }
160
161
    /**
162
     * Sets the entity persistence metadata from the metadata mapping.
163
     *
164
     * @param   Metadata\EntityMetadata     $metadata
165
     * @param   array                       $mapping
166
     * @return  Metadata\EntityMetadata
167
     */
168 View Code Duplication
    protected function setPersistence(Metadata\EntityMetadata $metadata, array $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...
169
    {
170
        $persisterKey = isset($mapping['key']) ? $mapping['key'] : null;
171
        $factory = $this->getPersistenceMetadataFactory($persisterKey);
172
173
        $persistence = $factory->createInstance($mapping);
174
175
        $metadata->setPersistence($persistence);
176
        return $metadata;
177
    }
178
179
    /**
180
     * Sets the entity search metadata from the metadata mapping.
181
     *
182
     * @param   Metadata\EntityMetadata     $metadata
183
     * @param   array                       $mapping
184
     * @return  Metadata\EntityMetadata
185
     */
186 View Code Duplication
    protected function setSearch(Metadata\EntityMetadata $metadata, array $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...
187
    {
188
        $clientKey = isset($mapping['key']) ? $mapping['key'] : null;
189
        if (null === $clientKey) {
190
            // Search is not enabled for this model.
191
            return $metadata;
192
        }
193
194
        $factory = $this->getSearchMetadataFactory($clientKey);
195
196
        $search = $factory->createInstance($mapping);
197
198
        $metadata->setSearch($search);
199
        return $metadata;
200
    }
201
202
    /**
203
     * Sets the entity attribute metadata from the metadata mapping.
204
     *
205
     * @param   Metadata\Interfaces\AttributeInterface  $metadata
206
     * @param   array                                   $attrMapping
207
     * @return  Metadata\EntityMetadata
208
     */
209
    protected function setAttributes(Metadata\Interfaces\AttributeInterface $metadata, array $attrMapping)
210
    {
211
        foreach ($attrMapping as $key => $mapping) {
212
            if (!is_array($mapping)) {
213
                $mapping = ['type' => null];
214
            }
215
216
            if (!isset($mapping['type'])) {
217
                $mapping['type'] = null;
218
            }
219
220
            if (!isset($mapping['search'])) {
221
                $mapping['search'] = [];
222
            }
223
224
            $attribute = new Metadata\AttributeMetadata($key, $mapping['type'], $this->isMixin($metadata));
225
226
            if (isset($mapping['description'])) {
227
                $attribute->description = $mapping['description'];
228
            }
229
230
            if (isset($mapping['defaultValue'])) {
231
                $attribute->defaultValue = $mapping['defaultValue'];
232
            }
233
234
            if (isset($mapping['calculated']) && is_array($mapping['calculated'])) {
235
                $calculated = $mapping['calculated'];
236
                if (isset($calculated['class']) && isset($calculated['method'])) {
237
                    $attribute->calculated['class']  =  $calculated['class'];
238
                    $attribute->calculated['method'] =  $calculated['method'];
239
                }
240
            }
241
242
            if (isset($mapping['search']['autocomplete'])) {
243
                $attribute->setAutocomplete(true);
244
            } else if (isset($mapping['search']['store'])) {
245
                $attribute->setSearchProperty(true);
246
            }
247
248
            if (isset($mapping['save'])) {
249
                $attribute->enableSave($mapping['save']);
250
            }
251
252
            if (isset($mapping['serialize'])) {
253
                $attribute->enableSerialize($mapping['serialize']);
254
            }
255
256
            $metadata->addAttribute($attribute);
257
        }
258
        return $metadata;
259
    }
260
261
    /**
262
     * Sets the entity embed metadata from the metadata mapping.
263
     *
264
     * @param   Metadata\Interfaces\EmbedInterface  $metadata
265
     * @param   array                               $embedMapping
266
     * @return  Metadata\EntityMetadata
267
     */
268
    protected function setEmbeds(Metadata\Interfaces\EmbedInterface $metadata, array $embedMapping)
269
    {
270
        foreach ($embedMapping as $key => $mapping) {
271 View Code Duplication
            if (!is_array($mapping)) {
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...
272
                $mapping = ['type' => null, 'entity' => null];
273
            }
274
275
            if (!isset($mapping['type'])) {
276
                $mapping['type'] = null;
277
            }
278
279
            if (!isset($mapping['entity'])) {
280
                $mapping['entity'] = null;
281
            }
282
283
            if ($metadata instanceof Metadata\EmbedMetadata && $mapping['entity'] === $metadata->name) {
284
                // This embed meta is referencing itself as an embed property. As such, do not recreate.
285
                // This prevents infinite recursion.
286
                $embedMeta = $metadata;
287
            } else {
288
                $embedMeta = $this->loadMetadataForEmbed($mapping['entity']);
289
            }
290
            if (null === $embedMeta) {
291
                continue;
292
            }
293
            $property = new Metadata\EmbeddedPropMetadata($key, $mapping['type'], $embedMeta, $this->isMixin($metadata));
294
295
            if (isset($mapping['serialize'])) {
296
                $property->enableSerialize($mapping['serialize']);
297
            }
298
299
            $metadata->addEmbed($property);
300
        }
301
        return $metadata;
302
    }
303
304
    /**
305
     * Sets creates mixin metadata instances from a set of mixin mappings ands sets them to the entity metadata instance.
306
     *
307
     * @param   Metadata\Interfaces\MixinInterface  $metadata
308
     * @param   array                   $mixins
309
     * @return  Metadata\Interfaces\MixinInterface
310
     */
311
    protected function setMixins(Metadata\Interfaces\MixinInterface $metadata, array $mixins)
312
    {
313
        foreach ($mixins as $mixinName) {
314
            $mixinMeta = $this->loadMetadataForMixin($mixinName);
315
            if (null === $mixinMeta) {
316
                continue;
317
            }
318
            $metadata->addMixin($mixinMeta);
319
        }
320
        return $metadata;
321
    }
322
323
    /**
324
     * Sets the entity relationship metadata from the metadata mapping.
325
     *
326
     * @param   Metadata\Interfaces\RelationshipInterface   $metadata
327
     * @param   array                                       $relMapping
328
     * @return  Metadata\Interfaces\RelationshipInterface
329
     * @throws  RuntimeException If the related entity type was not found.
330
     */
331
    protected function setRelationships(Metadata\Interfaces\RelationshipInterface $metadata, array $relMapping)
332
    {
333
        foreach ($relMapping as $key => $mapping) {
334 View Code Duplication
            if (!is_array($mapping)) {
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...
335
                $mapping = ['type' => null, 'entity' => null];
336
            }
337
338
            if (!isset($mapping['type'])) {
339
                $mapping['type'] = null;
340
            }
341
342
            if (!isset($mapping['entity'])) {
343
                $mapping['entity'] = null;
344
            }
345
346
            if (!isset($mapping['search'])) {
347
                $mapping['search'] = [];
348
            }
349
350
            $relationship = new Metadata\RelationshipMetadata($key, $mapping['type'], $mapping['entity'], $this->isMixin($metadata));
351
352
            if (isset($mapping['description'])) {
353
                $relationship->description = $mapping['description'];
354
            }
355
356
            if (isset($mapping['inverse'])) {
357
                $relationship->isInverse = true;
358
                if (isset($mapping['field'])) {
359
                    $relationship->inverseField = $mapping['field'];
360
                }
361
            }
362
363
            $path = $this->getFilePath('model', $mapping['entity']);
364
            $relatedEntityMapping = $this->getMapping('model', $mapping['entity'], $path);
365
366 View Code Duplication
            if (isset($relatedEntityMapping['entity']['polymorphic'])) {
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...
367
                $relationship->setPolymorphic(true);
368
                $relationship->ownedTypes = $this->getOwnedTypes($mapping['entity']);
369
            }
370
371
            if (isset($mapping['search']['store'])) {
372
                $relationship->setSearchProperty(true);
373
            }
374
375
            if (isset($mapping['save'])) {
376
                $relationship->enableSave($mapping['save']);
377
            }
378
379
            if (isset($mapping['serialize'])) {
380
                $relationship->enableSerialize($mapping['serialize']);
381
            }
382
383
            $metadata->addRelationship($relationship);
384
        }
385
        return $metadata;
386
    }
387
388
    /**
389
     * Determines if a metadata instance is a mixin.
390
     *
391
     * @param   Metadata\Interfaces\PropertyInterface   $metadata
392
     * @return  bool
393
     */
394
    protected function isMixin(Metadata\Interfaces\PropertyInterface $metadata)
395
    {
396
        return $metadata instanceof Metadata\MixinMetadata;
397
    }
398
399
    /**
400
     * Sets default values to the metadata mapping array.
401
     *
402
     * @param   string  $metaType   The metadata type, either model or mixin.
403
     * @param   mixed   $mapping    The parsed mapping data.
404
     * @return  array
405
     */
406
    protected function setDefaults($metaType, $mapping)
407
    {
408
        if (!is_array($mapping)) {
409
            $mapping = [];
410
        }
411
412
        $mapping = $this->setRootDefault('attributes', $mapping);
413
        $mapping = $this->setRootDefault('embeds', $mapping);
414
        $mapping = $this->setRootDefault('mixins', $mapping);
415
        if ('embed' === $metaType) {
416
            return $mapping;
417
        }
418
419
        $mapping = $this->setRootDefault('relationships', $mapping);
420
        if ('mixin' === $metaType) {
421
            return $mapping;
422
        }
423
        $this->setRootDefault('entity', $mapping);
424
425 View Code Duplication
        if (!isset($mapping['entity']['persistence']) || !is_array($mapping['entity']['persistence'])) {
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...
426
            $mapping['entity']['persistence'] = [];
427
        }
428
429 View Code Duplication
        if (!isset($mapping['entity']['search']) || !is_array($mapping['entity']['search'])) {
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...
430
            $mapping['entity']['search'] = [];
431
        }
432
        return $mapping;
433
    }
434
435
    /**
436
     * Sets a root level default value to a metadata mapping array.
437
     *
438
     * @param   string  $key
439
     * @param   array   $mapping
440
     * @return  array
441
     */
442
    private function setRootDefault($key, array $mapping)
443
    {
444
        if (!isset($mapping[$key]) || !is_array($mapping[$key])) {
445
            $mapping[$key] = [];
446
        }
447
        return $mapping;
448
    }
449
450
    /**
451
     * {@inheritDoc}
452
     */
453
    protected function getExtension()
454
    {
455
        return 'yml';
456
    }
457
}
458