Completed
Push — master ( e24a08...649c71 )
by Joshua
9s
created

EntityMetadata::applyMixinProperties()   B

Complexity

Conditions 7
Paths 15

Size

Total Lines 21
Code Lines 13

Duplication

Lines 21
Ratio 100 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 21
loc 21
rs 7.551
cc 7
eloc 13
nc 15
nop 1
1
<?php
2
3
namespace As3\Modlr\Metadata;
4
5
use As3\Modlr\Exception\MetadataException;
6
7
/**
8
 * Defines the metadata for an entity (e.g. a database object).
9
 * Should be loaded using the MetadataFactory, not instantiated directly.
10
 *
11
 * @author  Jacob Bare <[email protected]>
12
 * @todo    This should be renamed to ModelMetadata.
13
 */
14
class EntityMetadata implements Interfaces\AttributeInterface, Interfaces\EmbedInterface, Interfaces\MergeableInterface, Interfaces\MixinInterface, Interfaces\RelationshipInterface
15
{
16
    /**
17
     * Uses attributes.
18
     */
19
    use Traits\AttributesTrait;
20
21
    /**
22
     * Uses embeds.
23
     */
24
    use Traits\EmbedsTrait;
25
26
    /**
27
     * Uses mixins.
28
     */
29
    use Traits\MixinsTrait;
30
31
    /**
32
     * Uses merged properties.
33
     */
34
    use Traits\PropertiesTrait;
35
36
    /**
37
     * Uses relationships.
38
     */
39
    use Traits\RelationshipsTrait;
40
41
    /**
42
     * The id key name and type.
43
     */
44
    const ID_KEY  = 'id';
45
    const ID_TYPE = 'string';
46
47
    /**
48
     * The model type key.
49
     */
50
    const TYPE_KEY = 'type';
51
52
    /**
53
     * Whether this class is abstract.
54
     *
55
     * @var bool
56
     */
57
    public $abstract = false;
58
59
    /**
60
     * An array of attribute default values for this model.
61
     * Keyed by field name.
62
     *
63
     * @var array
64
     */
65
    public $defaultValues = [];
66
67
    /**
68
     * The entity type this entity extends.
69
     *
70
     * @var string|null
71
     */
72
    public $extends;
73
74
    /**
75
     * Child entity types this entity owns.
76
     * Only used for polymorphic entities.
77
     *
78
     * @var array
79
     */
80
    public $ownedTypes = [];
81
82
    /**
83
     * The persistence metadata for this entity.
84
     *
85
     * @var Interfaces\StorageLayerInterface
86
     */
87
    public $persistence;
88
89
    /**
90
     * Whether this class is considered polymorphic.
91
     *
92
     * @var bool
93
     */
94
    public $polymorphic = false;
95
96
    /**
97
     * The search metadata for this entity.
98
     *
99
     * @var Interfaces\StorageLayerInterface
100
     */
101
    public $search;
102
103
    /**
104
     * Uniquely defines the type of entity.
105
     *
106
     * @var string
107
     */
108
    public $type;
109
110
    /**
111
     * Constructor.
112
     *
113
     * @param   string  $type   The resource identifier type.
114
     */
115
    public function __construct($type)
116
    {
117
        $this->setType($type);
118
    }
119
120
    /**
121
     * Gets the parent entity type.
122
     * For entities that are extended.
123
     *
124
     * @return  string|null
125
     */
126
    public function getParentEntityType()
127
    {
128
        return $this->extends;
129
    }
130
131
    /**
132
     * {@inheritdoc}
133
     */
134
    public function getProperties()
135
    {
136
        return array_merge($this->getAttributes(), $this->getRelationships(), $this->getEmbeds());
137
    }
138
139
    /**
140
     * Whether this metadata represents an abstract class.
141
     *
142
     * @return  bool
143
     */
144
    public function isAbstract()
145
    {
146
        return (Boolean) $this->abstract;
147
    }
148
149
    /**
150
     * Whether this metadata represents a polymorphic class.
151
     *
152
     * @return  bool
153
     */
154
    public function isPolymorphic()
155
    {
156
        return (Boolean) $this->polymorphic;
157
    }
158
159
    /**
160
     * Deteremines whether search is enabled for this model.
161
     *
162
     * @return  bool
163
     */
164
    public function isSearchEnabled()
165
    {
166
        return null !== $this->search;
167
    }
168
169
    /**
170
     * Determines if this is a child entity of another entity.
171
     *
172
     * @return  bool
173
     */
174
    public function isChildEntity()
175
    {
176
        return null !== $this->getParentEntityType();
177
    }
178
179
    /**
180
     * {@inheritDoc}
181
     */
182
    public function merge(Interfaces\MergeableInterface $metadata)
183
    {
184
        if (!$metadata instanceof EntityMetadata) {
185
            throw new MetadataException('Unable to merge metadata. The provided metadata instance is not compatible.');
186
        }
187
188
        $this->setType($metadata->type);
189
        $this->setPolymorphic($metadata->isPolymorphic());
190
        $this->setAbstract($metadata->isAbstract());
191
        $this->extends = $metadata->extends;
192
        $this->ownedTypes = $metadata->ownedTypes;
193
        $this->defaultValues = array_merge($this->defaultValues, $metadata->defaultValues);
194
195
        $this->persistence->merge($metadata->persistence);
196
        $this->search->merge($metadata->search);
197
198
        $this->mergeAttributes($metadata->getAttributes());
199
        $this->mergeRelationships($metadata->getRelationships());
200
        $this->mergeEmbeds($metadata->getEmbeds());
201
        $this->mergeMixins($metadata->getMixins());
202
203
        return $this;
204
    }
205
206
    /**
207
     * Sets this metadata as representing an abstract class.
208
     *
209
     * @param   bool    $bit
210
     * @return  self
211
     */
212
    public function setAbstract($bit = true)
213
    {
214
        $this->abstract = (Boolean) $bit;
215
        return $this;
216
    }
217
218
    /**
219
     * Sets the persistence metadata for this entity.
220
     *
221
     * @param   Interfaces\StorageLayerInterface    $persistence
222
     * @return  self
223
     */
224
    public function setPersistence(Interfaces\StorageLayerInterface $persistence)
225
    {
226
        $this->persistence = $persistence;
227
        return $this;
228
    }
229
230
    /**
231
     * Sets this metadata as representing a polymorphic class.
232
     *
233
     * @param   bool    $bit
234
     * @return  self
235
     */
236
    public function setPolymorphic($bit = true)
237
    {
238
        $this->polymorphic = (Boolean) $bit;
239
        return $this;
240
    }
241
242
    /**
243
     * Sets the search metadata for this entity.
244
     *
245
     * @param   Interfaces\StorageLayerInterface    $search
246
     * @return  self
247
     */
248
    public function setSearch(Interfaces\StorageLayerInterface $search)
249
    {
250
        $this->search = $search;
251
        return $this;
252
    }
253
254
    /**
255
     * Sets the entity type.
256
     *
257
     * @param   string  $type
258
     * @return  self
259
     * @throws  MetadataException   If the type is not a string or is empty.
260
     */
261
    public function setType($type)
262
    {
263
        if (!is_string($type) || empty($type)) {
264
            throw MetadataException::invalidEntityType($type);
265
        }
266
        $this->type = $type;
267
        return $this;
268
    }
269
270
    /**
271
     * {@inheritdoc}
272
     */
273 View Code Duplication
    protected function applyMixinProperties(MixinMetadata $mixin)
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...
274
    {
275
        foreach ($mixin->getAttributes() as $attribute) {
276
            if (true === $this->hasAttribute($attribute->key)) {
277
                throw MetadataException::mixinPropertyExists($this->type, $mixin->name, 'attribute', $attribute->key);
278
            }
279
            $this->addAttribute($attribute);
280
        }
281
        foreach ($mixin->getRelationships() as $relationship) {
282
            if (true === $this->hasRelationship($relationship->key)) {
283
                throw MetadataException::mixinPropertyExists($this->type, $mixin->name, 'relationship', $relationship->key);
284
            }
285
            $this->addRelationship($relationship);
286
        }
287
        foreach ($mixin->getEmbeds() as $embed) {
288
            if (true === $this->hasEmbed($embed->key)) {
289
                throw MetadataException::mixinPropertyExists($this->type, $mixin->name, 'embed', $embed->key);
290
            }
291
            $this->addEmbed($embed);
292
        }
293
    }
294
295
    /**
296
     * {@inheritdoc}
297
     */
298 View Code Duplication
    protected function validateAttribute(AttributeMetadata $attribute)
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...
299
    {
300
        if (true === $this->hasRelationship($attribute->getKey())) {
301
            throw MetadataException::fieldKeyInUse('attribute', 'relationship', $attribute->getKey(), $this->type);
302
        }
303
        if (true === $this->hasEmbed($attribute->getKey())) {
304
            throw MetadataException::fieldKeyInUse('attribute', 'embed', $attribute->getKey(), $this->type);
305
        }
306
    }
307
308
    /**
309
     * {@inheritdoc}
310
     */
311 View Code Duplication
    protected function validateEmbed(EmbeddedPropMetadata $embed)
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...
312
    {
313
        if (true === $this->hasAttribute($embed->getKey())) {
314
            throw MetadataException::fieldKeyInUse('embed', 'attribute', $embed->getKey(), $this->type);
315
        }
316
        if (true === $this->hasRelationship($embed->getKey())) {
317
            throw MetadataException::fieldKeyInUse('embed', 'relationship', $embed->getKey(), $this->type);
318
        }
319
    }
320
321
    /**
322
     * {@inheritdoc}
323
     */
324 View Code Duplication
    protected function validateRelationship(RelationshipMetadata $relationship)
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...
325
    {
326
        if (true === $this->hasAttribute($relationship->getKey())) {
327
            throw MetadataException::fieldKeyInUse('relationship', 'attribute', $relationship->getKey(), $this->type);
328
        }
329
        if (true === $this->hasEmbed($relationship->getKey())) {
330
            throw MetadataException::fieldKeyInUse('relationship', 'embed', $relationship->getKey(), $this->type);
331
        }
332
    }
333
334
    /**
335
     * Merges attributes with this instance's attributes.
336
     *
337
     * @param   AttributeMetadata[]     $toAdd
338
     * @return  self
339
     */
340
    private function mergeAttributes(array $toAdd)
341
    {
342
        foreach ($toAdd as $attribute) {
343
            $this->addAttribute($attribute);
344
        }
345
        return $this;
346
    }
347
348
    /**
349
     * Merges embeds with this instance's embeds.
350
     *
351
     * @param   EmbeddedPropMetadata[]  $toAdd
352
     * @return  self
353
     */
354
    private function mergeEmbeds(array $toAdd)
355
    {
356
        foreach ($toAdd as $embed) {
357
            $this->addEmbed($embed);
358
        }
359
        return $this;
360
    }
361
362
    /**
363
     * Merges mixins with this instance's mixins.
364
     *
365
     * @param   MixinMetadata[]     $toAdd
366
     * @return  self
367
     */
368
    private function mergeMixins(array $toAdd)
369
    {
370
        foreach ($toAdd as $mixin) {
371
            if (!isset($this->mixins[$mixin->name])) {
372
                $this->mixins[$mixin->name] = $mixin;
373
            }
374
        }
375
        return $this;
376
    }
377
378
    /**
379
     * Merges relationships with this instance's relationships.
380
     *
381
     * @param   RelationshipMetadata[]  $toAdd
382
     * @return  self
383
     */
384
    private function mergeRelationships(array $toAdd)
385
    {
386
        foreach ($toAdd as $relationship) {
387
            $this->addRelationship($relationship);
388
        }
389
        return $this;
390
    }
391
}
392