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

EntityMetadata::mergeAttributes()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 7
rs 9.4285
cc 2
eloc 4
nc 2
nop 1
1
<?php
2
3
namespace As3\Modlr\Metadata;
4
5
use As3\Modlr\Exception\MetadataException;
6
use As3\Modlr\Metadata\Interfaces\AttributeInterface;
7
use As3\Modlr\Metadata\Interfaces\MergeableInterface;
8
use As3\Modlr\Metadata\Interfaces\RelationshipInterface;
9
use As3\Modlr\Metadata\Interfaces\StorageLayerInterface;
10
11
/**
12
 * Defines the metadata for an entity (e.g. a database object).
13
 * Should be loaded using the MetadataFactory, not instantiated directly.
14
 *
15
 * @author  Jacob Bare <[email protected]>
16
 * @todo    This should be renamed to ModelMetadata, which extends an abstract EntityMetadata class, which MixinMetadata also extends?
17
 */
18
class EntityMetadata implements AttributeInterface, RelationshipInterface, MergeableInterface
19
{
20
    /**
21
     * Uses properties (attributes and relationships)
22
     */
23
    use Traits\PropertiesTrait;
24
25
    /**
26
     * The id key name and type.
27
     */
28
    const ID_KEY  = 'id';
29
    const ID_TYPE = 'string';
30
31
    /**
32
     * Whether this class is abstract.
33
     *
34
     * @var bool
35
     */
36
    public $abstract = false;
37
38
    /**
39
     * An array of attribute default values for this model.
40
     * Keyed by field name.
41
     *
42
     * @var array
43
     */
44
    public $defaultValues = [];
45
46
    /**
47
     * The entity type this entity extends.
48
     *
49
     * @var bool
50
     */
51
    public $extends;
52
53
    /**
54
     * All mixins assigned to this entity.
55
     *
56
     * @var     MixinMetadata[]
57
     */
58
    public $mixins = [];
59
60
    /**
61
     * Child entity types this entity owns.
62
     * Only used for polymorphic entities.
63
     *
64
     * @var array
65
     */
66
    public $ownedTypes = [];
67
68
    /**
69
     * The persistence metadata for this entity.
70
     *
71
     * @var StorageLayerInterface
72
     */
73
    public $persistence;
74
75
    /**
76
     * Whether this class is considered polymorphic.
77
     *
78
     * @var bool
79
     */
80
    public $polymorphic = false;
81
82
    /**
83
     * The search metadata for this entity.
84
     *
85
     * @var StorageLayerInterface
86
     */
87
    public $search;
88
89
    /**
90
     * Uniquely defines the type of entity.
91
     *
92
     * @var string
93
     */
94
    public $type;
95
96
    /**
97
     * Constructor.
98
     *
99
     * @param   string  $type   The resource identifier type.
100
     */
101
    public function __construct($type)
102
    {
103
        $this->setType($type);
104
    }
105
106
    /**
107
     * Adds a mixin (and its attributes and relationships) to this entity.
108
     *
109
     * @param   MixinMetadata   $mixin
110
     * @return  self
111
     */
112
    public function addMixin(MixinMetadata $mixin)
113
    {
114
        if (isset($this->mixins[$mixin->name])) {
115
            return $this;
116
        }
117
        foreach ($mixin->getAttributes() as $attribute) {
118
            if (true === $this->hasAttribute($attribute->key)) {
119
                throw MetadataException::mixinPropertyExists($this->type, $mixin->name, 'attribute', $attribute->key);
120
            }
121
            $this->addAttribute($attribute);
122
        }
123
        foreach ($mixin->getRelationships() as $relationship) {
124
            if (true === $this->hasRelationship($relationship->key)) {
125
                throw MetadataException::mixinPropertyExists($this->type, $mixin->name, 'relationship', $relationship->key);
126
            }
127
            $this->addRelationship($relationship);
128
        }
129
        $this->mixins[$mixin->name] = $mixin;
130
        return $this;
131
    }
132
133
    /**
134
     * Determines if a mixin exists.
135
     *
136
     * @param   string  $mixinName
137
     * @return  bool
138
     */
139
    public function hasMixin($mixinName)
140
    {
141
        return isset($this->mixins[$mixinName]);
142
    }
143
144
    /**
145
     * Gets the parent entity type.
146
     * For entities that are extended.
147
     *
148
     * @return  string|null
149
     */
150
    public function getParentEntityType()
151
    {
152
        return $this->extends;
153
    }
154
155
    /**
156
     * Whether this metadata represents an abstract class.
157
     *
158
     * @return  bool
159
     */
160
    public function isAbstract()
161
    {
162
        return (Boolean) $this->abstract;
163
    }
164
165
    /**
166
     * Whether this metadata represents a polymorphic class.
167
     *
168
     * @return  bool
169
     */
170
    public function isPolymorphic()
171
    {
172
        return (Boolean) $this->polymorphic;
173
    }
174
175
    /**
176
     * Deteremines whether search is enabled for this model.
177
     *
178
     * @return  bool
179
     */
180
    public function isSearchEnabled()
181
    {
182
        return null !== $this->search;
183
    }
184
185
    /**
186
     * Determines if this is a child entity of another entity.
187
     *
188
     * @return  bool
189
     */
190
    public function isChildEntity()
191
    {
192
        return null !== $this->getParentEntityType();
193
    }
194
195
    /**
196
     * {@inheritDoc}
197
     */
198
    public function merge(MergeableInterface $metadata)
199
    {
200
        $this->setType($metadata->type);
0 ignored issues
show
Bug introduced by
Accessing type on the interface As3\Modlr\Metadata\Interfaces\MergeableInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
201
        $this->setPolymorphic($metadata->isPolymorphic());
202
        $this->setAbstract($metadata->isAbstract());
203
        $this->extends = $metadata->extends;
0 ignored issues
show
Bug introduced by
Accessing extends on the interface As3\Modlr\Metadata\Interfaces\MergeableInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
204
        $this->ownedTypes = $metadata->ownedTypes;
0 ignored issues
show
Bug introduced by
Accessing ownedTypes on the interface As3\Modlr\Metadata\Interfaces\MergeableInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
205
        $this->defaultValues = array_merge($this->defaultValues, $metadata->defaultValues);
0 ignored issues
show
Bug introduced by
Accessing defaultValues on the interface As3\Modlr\Metadata\Interfaces\MergeableInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
206
207
        $this->persistence->merge($metadata->persistence);
0 ignored issues
show
Bug introduced by
Accessing persistence on the interface As3\Modlr\Metadata\Interfaces\MergeableInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
208
        $this->search->merge($metadata->search);
0 ignored issues
show
Bug introduced by
Accessing search on the interface As3\Modlr\Metadata\Interfaces\MergeableInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
209
210
        $this->mergeAttributes($metadata->getAttributes());
211
        $this->mergeRelationships($metadata->getRelationships());
212
        $this->mergeMixins($metadata->mixins);
0 ignored issues
show
Bug introduced by
Accessing mixins on the interface As3\Modlr\Metadata\Interfaces\MergeableInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
213
214
        return $this;
215
    }
216
217
    /**
218
     * Sets this metadata as representing an abstract class.
219
     *
220
     * @param   bool    $bit
221
     * @return  self
222
     */
223
    public function setAbstract($bit = true)
224
    {
225
        $this->abstract = (Boolean) $bit;
226
        return $this;
227
    }
228
229
    /**
230
     * Sets the persistence metadata for this entity.
231
     *
232
     * @param   StorageLayerInterface   $persistence
233
     * @return  self
234
     */
235
    public function setPersistence(StorageLayerInterface $persistence)
236
    {
237
        $this->persistence = $persistence;
238
        return $this;
239
    }
240
241
    /**
242
     * Sets this metadata as representing a polymorphic class.
243
     *
244
     * @param   bool    $bit
245
     * @return  self
246
     */
247
    public function setPolymorphic($bit = true)
248
    {
249
        $this->polymorphic = (Boolean) $bit;
250
        return $this;
251
    }
252
253
    /**
254
     * Sets the search metadata for this entity.
255
     *
256
     * @param   StorageLayerInterface   $search
257
     * @return  self
258
     */
259
    public function setSearch(StorageLayerInterface $search)
260
    {
261
        $this->search = $search;
262
        return $this;
263
    }
264
265
    /**
266
     * Sets the entity type.
267
     *
268
     * @param   string  $type
269
     * @return  self
270
     * @throws  MetadataException   If the type is not a string or is empty.
271
     */
272
    public function setType($type)
273
    {
274
        if (!is_string($type) || empty($type)) {
275
            throw MetadataException::invalidEntityType($type);
276
        }
277
        $this->type = $type;
278
        return $this;
279
    }
280
281
    /**
282
     * Merges attributes with this instance's attributes.
283
     *
284
     * @param   array   $toAdd
285
     * @return  self
286
     */
287
    private function mergeAttributes(array $toAdd)
288
    {
289
        foreach ($toAdd as $attribute) {
290
            $this->addAttribute($attribute);
291
        }
292
        return $this;
293
    }
294
295
    /**
296
     * Merges mixins with this instance's mixins.
297
     *
298
     * @param   array   $toAdd
299
     * @return  self
300
     */
301
    private function mergeMixins(array $toAdd)
302
    {
303
        foreach ($toAdd as $mixin) {
304
            if (!isset($this->mixins[$mixin->name])) {
305
                $this->mixins[$mixin->name] = $mixin;
306
            }
307
        }
308
        return $this;
309
    }
310
311
    /**
312
     * Merges relationships with this instance's relationships.
313
     *
314
     * @param   array   $toAdd
315
     * @return  self
316
     */
317
    private function mergeRelationships(array $toAdd)
318
    {
319
        foreach ($toAdd as $relationship) {
320
            $this->addRelationship($relationship);
321
        }
322
        return $this;
323
    }
324
}
325