Completed
Push — master ( 79d9b9...58badd )
by Jacob
10s
created

YamlFileDriver::setDefaults()   C

Complexity

Conditions 13
Paths 78

Size

Total Lines 31
Code Lines 16

Duplication

Lines 16
Ratio 51.61 %
Metric Value
dl 16
loc 31
rs 5.1234
cc 13
eloc 16
nc 78
nop 2

How to fix   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
    ];
26
27
    /**
28
     * {@inheritDoc}
29
     */
30
    protected function loadMetadataFromFile($type, $file)
31
    {
32
        $mapping = $this->getMapping('model', $type, $file);
33
34
        $metadata = new Metadata\EntityMetadata($type);
35
36
        if (isset($mapping['entity']['abstract'])) {
37
            $metadata->setAbstract(true);
38
        }
39
40 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...
41
            $metadata->setPolymorphic(true);
42
            $metadata->ownedTypes = $this->getOwnedTypes($metadata->type);
43
        }
44
45
        if (isset($mapping['entity']['extends'])) {
46
            $metadata->extends = $mapping['entity']['extends'];
47
        }
48
49 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...
50
            $metadata->defaultValues = $mapping['entity']['defaultValues'];
51
        }
52
53
        $this->setPersistence($metadata, $mapping['entity']['persistence']);
54
        $this->setSearch($metadata, $mapping['entity']['search']);
55
        $this->setAttributes($metadata, $mapping['attributes']);
56
        $this->setRelationships($metadata, $mapping['relationships']);
57
        $this->setMixins($metadata, $mapping['mixins']);
58
        return $metadata;
59
    }
60
61
    /**
62
     * {@inheritDoc}
63
     */
64
    protected function loadMixinFromFile($mixinName, $file)
65
    {
66
        $mapping = $this->getMapping('mixin', $mixinName, $file);
67
68
        $mixin = new Metadata\MixinMetadata($mixinName);
69
70
        $this->setAttributes($mixin, $mapping['attributes']);
71
        $this->setRelationships($mixin, $mapping['relationships']);
72
        return $mixin;
73
    }
74
75
    /**
76
     * {@inheritDoc}
77
     */
78
    public function getTypeHierarchy($type, array $types = [])
79
    {
80
        $path = $this->getFilePath('model', $type);
81
        $mapping = $this->getMapping('model', $type, $path);
82
83
        $types[] = $type;
84
        if (isset($mapping['entity']['extends']) && $mapping['entity']['extends'] !== $type) {
85
            return $this->getTypeHierarchy($mapping['entity']['extends'], $types);
86
        }
87
        return array_reverse($types);
88
    }
89
90
    /**
91
     * {@inheritDoc}
92
     */
93
    public function getOwnedTypes($type, array $types = [])
94
    {
95
        $path = $this->getFilePath('model', $type);
96
        $superMapping = $this->getMapping('model', $type, $path);
97
98
        $abstract = isset($superMapping['entity']['abstract']) && true === $superMapping['entity']['abstract'];
99
100
        foreach ($this->getAllTypeNames() as $searchType) {
101
102
            if ($type === $searchType && false === $abstract) {
103
                $types[] = $type;
104
                continue;
105
            }
106
            if (0 !== strpos($searchType, $type)) {
107
                continue;
108
            }
109
110
            $path = $this->getFilePath('model', $searchType);
111
            $mapping = $this->getMapping('model', $searchType, $path);
112
113
            if (!isset($mapping['entity']['extends']) || $mapping['entity']['extends'] !== $type) {
114
                continue;
115
            }
116
            $types[] = $searchType;
117
        }
118
        return $types;
119
    }
120
121
    /**
122
     * Gets the metadata mapping information from the YAML file.
123
     *
124
     * @param   string  $metaType   The metadata type, either mixin or model.
125
     * @param   string  $key        The metadata key name, either the mixin name or model type.
126
     * @param   string  $file       The YAML file location.
127
     * @return  array
128
     * @throws  MetadataException
129
     */
130
    private function getMapping($metaType, $key, $file)
131
    {
132
        if (isset($this->mappings[$metaType][$key])) {
133
            // Set to array cache to prevent multiple gets/parses.
134
            return $this->mappings[$metaType][$key];
135
        }
136
137
        $contents = Yaml::parse(file_get_contents($file));
138
        if (!isset($contents[$key])) {
139
            throw MetadataException::fatalDriverError($key, sprintf('No mapping key was found at the beginning of the YAML file. Expected "%s"', $key));
140
        }
141
        return $this->mappings[$metaType][$key] = $this->setDefaults($metaType, $contents[$key]);
142
    }
143
144
    /**
145
     * Sets the entity persistence metadata from the metadata mapping.
146
     *
147
     * @param   Metadata\EntityMetadata     $metadata
148
     * @param   array                       $mapping
149
     * @return  Metadata\EntityMetadata
150
     */
151 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...
152
    {
153
        $persisterKey = isset($mapping['key']) ? $mapping['key'] : null;
154
        $factory = $this->getPersistenceMetadataFactory($persisterKey);
155
156
        $persistence = $factory->createInstance($mapping);
157
158
        $metadata->setPersistence($persistence);
159
        return $metadata;
160
    }
161
162
    /**
163
     * Sets the entity search metadata from the metadata mapping.
164
     *
165
     * @param   Metadata\EntityMetadata     $metadata
166
     * @param   array                       $mapping
167
     * @return  Metadata\EntityMetadata
168
     */
169 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...
170
    {
171
        $clientKey = isset($mapping['key']) ? $mapping['key'] : null;
172
        if (null === $clientKey) {
173
            // Search is not enabled for this model.
174
            return $metadata;
175
        }
176
177
        $factory = $this->getSearchMetadataFactory($clientKey);
178
179
        $search = $factory->createInstance($mapping);
180
181
        $metadata->setSearch($search);
182
        return $metadata;
183
    }
184
185
    /**
186
     * Sets the entity attribute metadata from the metadata mapping.
187
     *
188
     * @param   Metadata\Interfaces\AttributeInterface  $metadata
189
     * @param   array                                   $attrMapping
190
     * @return  Metadata\EntityMetadata
191
     */
192
    protected function setAttributes(Metadata\Interfaces\AttributeInterface $metadata, array $attrMapping)
193
    {
194
        foreach ($attrMapping as $key => $mapping) {
195
            if (!is_array($mapping)) {
196
                $mapping = ['type' => null];
197
            }
198
199
            if (!isset($mapping['type'])) {
200
                $mapping['type'] = null;
201
            }
202
203
            if (!isset($mapping['search'])) {
204
                $mapping['search'] = [];
205
            }
206
207
            $attribute = new Metadata\AttributeMetadata($key, $mapping['type'], $this->isMixin($metadata));
208
209
            if (isset($mapping['description'])) {
210
                $attribute->description = $mapping['description'];
211
            }
212
213
            if (isset($mapping['defaultValue'])) {
214
                $attribute->defaultValue = $mapping['defaultValue'];
215
            }
216
217
            if (isset($mapping['calculated']) && is_array($mapping['calculated'])) {
218
                $calculated = $mapping['calculated'];
219
                if (isset($calculated['class']) && isset($calculated['method'])) {
220
                    $attribute->calculated['class']  =  $calculated['class'];
221
                    $attribute->calculated['method'] =  $calculated['method'];
222
                }
223
            }
224
225
            if (isset($mapping['search']['autocomplete'])) {
226
                $attribute->setAutocomplete(true);
227
            } else if (isset($mapping['search']['store'])) {
228
                $attribute->setSearchProperty(true);
229
            }
230
231
            if (isset($mapping['save'])) {
232
                $attribute->enableSave($mapping['save']);
233
            }
234
235
            $metadata->addAttribute($attribute);
236
        }
237
        return $metadata;
238
    }
239
240
    protected function setMixins(Metadata\EntityMetadata $metadata, array $mixins)
241
    {
242
        foreach ($mixins as $mixinName) {
243
            $mixinMeta = $this->loadMetadataForMixin($mixinName);
244
            $metadata->addMixin($mixinMeta);
0 ignored issues
show
Bug introduced by
It seems like $mixinMeta defined by $this->loadMetadataForMixin($mixinName) on line 243 can also be of type null or object<As3\Modlr\Metadata\EntityMetadata>; however, As3\Modlr\Metadata\EntityMetadata::addMixin() does only seem to accept object<As3\Modlr\Metadata\MixinMetadata>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
245
        }
246
        return $metadata;
247
    }
248
249
    /**
250
     * Sets the entity relationship metadata from the metadata mapping.
251
     *
252
     * @param   Metadata\Interfaces\RelationshipInterface   $metadata
253
     * @param   array                                       $relMapping
254
     * @return  Metadata\Interfaces\RelationshipInterface
255
     * @throws  RuntimeException If the related entity type was not found.
256
     */
257
    protected function setRelationships(Metadata\Interfaces\RelationshipInterface $metadata, array $relMapping)
258
    {
259
        foreach ($relMapping as $key => $mapping) {
260
            if (!is_array($mapping)) {
261
                $mapping = ['type' => null, 'entity' => null];
262
            }
263
264
            if (!isset($mapping['type'])) {
265
                $mapping['type'] = null;
266
            }
267
268
            if (!isset($mapping['entity'])) {
269
                $mapping['entity'] = null;
270
            }
271
272
            if (!isset($mapping['search'])) {
273
                $mapping['search'] = [];
274
            }
275
276
            $relationship = new Metadata\RelationshipMetadata($key, $mapping['type'], $mapping['entity'], $this->isMixin($metadata));
277
278
            if (isset($mapping['description'])) {
279
                $relationship->description = $mapping['description'];
280
            }
281
282
            if (isset($mapping['inverse'])) {
283
                $relationship->isInverse = true;
284
                if (isset($mapping['field'])) {
285
                    $relationship->inverseField = $mapping['field'];
286
                }
287
            }
288
289
            $path = $this->getFilePath('model', $mapping['entity']);
290
            $relatedEntityMapping = $this->getMapping('model', $mapping['entity'], $path);
291
292 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...
293
                $relationship->setPolymorphic(true);
294
                $relationship->ownedTypes = $this->getOwnedTypes($mapping['entity']);
295
            }
296
297
            if (isset($mapping['search']['store'])) {
298
                $relationship->setSearchProperty(true);
299
            }
300
301
            if (isset($mapping['save'])) {
302
                $relationship->enableSave($mapping['save']);
303
            }
304
305
            $metadata->addRelationship($relationship);
306
        }
307
        return $metadata;
308
    }
309
310
    /**
311
     * Determines if a metadata instance is a mixin.
312
     *
313
     * @param   Metadata\Interfaces\PropertyInterface   $metadata
314
     * @return  bool
315
     */
316
    protected function isMixin(Metadata\Interfaces\PropertyInterface $metadata)
317
    {
318
        return $metadata instanceof Metadata\MixinMetadata;
319
    }
320
321
    /**
322
     * Sets default values to the metadata mapping array.
323
     *
324
     * @param   string  $metaType   The metadata type, either model or mixin.
325
     * @param   mixed   $mapping    The parsed mapping data.
326
     * @return  array
327
     */
328
    protected function setDefaults($metaType, $mapping)
329
    {
330
        if (!is_array($mapping)) {
331
            $mapping = [];
332
        }
333
334 View Code Duplication
        foreach (['attributes', 'relationships'] as $key) {
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
            if (!isset($mapping[$key]) || !is_array($mapping[$key])) {
336
                $mapping[$key] = [];
337
            }
338
        }
339
340
        if ('mixin' === $metaType) {
341
            return $mapping;
342
        }
343
344 View Code Duplication
        foreach (['entity', 'mixins'] as $key) {
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...
345
            if (!isset($mapping[$key]) || !is_array($mapping[$key])) {
346
                $mapping[$key] = [];
347
            }
348
        }
349
350 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...
351
            $mapping['entity']['persistence'] = [];
352
        }
353
354 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...
355
            $mapping['entity']['search'] = [];
356
        }
357
        return $mapping;
358
    }
359
360
    /**
361
     * {@inheritDoc}
362
     */
363
    protected function getExtension()
364
    {
365
        return 'yml';
366
    }
367
}
368