Completed
Pull Request — master (#47)
by Jacob
02:45
created

Model::filterNotSavedProperties()   B

Complexity

Conditions 5
Paths 6

Size

Total Lines 11
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 11
rs 8.8571
cc 5
eloc 7
nc 6
nop 2
1
<?php
2
3
namespace As3\Modlr\Models;
4
5
use As3\Modlr\Models\Relationships;
6
use As3\Modlr\Persister\Record;
7
use As3\Modlr\Store\Store;
8
use As3\Modlr\Metadata\EntityMetadata;
9
10
/**
11
 * Represents a data record from a persistence (database) layer.
12
 *
13
 * @author Jacob Bare <[email protected]>
14
 */
15
class Model
16
{
17
    /**
18
     * The id value of this model.
19
     * Always converted to a string when in the model context.
20
     *
21
     * @var string
22
     */
23
    protected $identifier;
24
25
    /**
26
     * The Model's attributes
27
     *
28
     * @var Attributes
29
     */
30
    protected $attributes;
31
32
    /**
33
     * The Model's has-one relationships
34
     *
35
     * @var Relationships\HasOne
36
     */
37
    protected $hasOneRelationships;
38
39
    /**
40
     * The Model's has-many relationships
41
     *
42
     * @var Relationships\HasMany
43
     */
44
    protected $hasManyRelationships;
45
46
    /**
47
     * Enables/disables collection auto-initialization on iteration.
48
     * Will not load/fill the collection from the database if false.
49
     * Is useful for large hasMany iterations where only id and type are required (ala serialization).
50
     *
51
     * @var bool
52
     */
53
    protected $collectionAutoInit = true;
54
55
    /**
56
     * The model state.
57
     *
58
     * @var State
59
     */
60
    protected $state;
61
62
    /**
63
     * The EntityMetadata that defines this Model.
64
     *
65
     * @var EntityMetadata
66
     */
67
    protected $metadata;
68
69
    /**
70
     * The Model Store for handling lifecycle operations.
71
     *
72
     * @var Store
73
     */
74
    protected $store;
75
76
    /**
77
     * Constructor.
78
     *
79
     * @param   EntityMetadata  $metadata   The internal entity metadata that supports this Model.
80
     * @param   string          $identifier The database identifier.
81
     * @param   Store           $store      The model store service for handling persistence operations.
82
     * @param   Record|null     $record     The model's attributes and relationships from the db layer to init the model with. New models will constructed with a null record.
83
     */
84
    public function __construct(EntityMetadata $metadata, $identifier, Store $store, Record $record = null)
85
    {
86
        $this->metadata = $metadata;
87
        $this->identifier = $identifier;
88
        $this->store = $store;
89
        $this->state = new State();
90
        $this->initialize($record);
91
    }
92
93
    /**
94
     * Gets the unique identifier of this model.
95
     *
96
     * @api
97
     * @return  string
98
     */
99
    public function getId()
100
    {
101
        return $this->identifier;
102
    }
103
104
    /**
105
     * Gets the model type.
106
     *
107
     * @api
108
     * @return  string
109
     */
110
    public function getType()
111
    {
112
        return $this->metadata->type;
113
    }
114
115
    /**
116
     * Gets the composite key of the model by combining the model type with the unique id.
117
     *
118
     * @api
119
     * @return  string
120
     */
121
    public function getCompositeKey()
122
    {
123
        return sprintf('%s.%s', $this->getType(), $this->getId());
124
    }
125
126
    /**
127
     * Enables or disables has-many collection auto-initialization from the database.
128
     *
129
     * @param   bool    $bit    Whether to enable/disable.
130
     * @return  self
131
     */
132
    public function enableCollectionAutoInit($bit = true)
133
    {
134
        $this->collectionAutoInit = (Boolean) $bit;
135
        return $this;
136
    }
137
138
    /**
139
     * Gets a model property.
140
     * Will either be an attribute value, a has-one model, or an array representation of a has-many collection.
141
     * Returns null if the property does not exist on the model or is not set.
142
     * Is a proxy for @see getAttribute($key) and getRelationship($key)
143
     *
144
     * @api
145
     * @param   string  $key    The property field key.
146
     * @return  Model|Model[]|null|mixed
147
     */
148
    public function get($key)
149
    {
150
        if (true === $this->isAttribute($key)) {
151
            return $this->getAttribute($key);
152
        }
153
        return $this->getRelationship($key);
154
    }
155
156
    /**
157
     * Determines if a property key is an attribute.
158
     *
159
     * @api
160
     * @param   string  $key    The property key.
161
     * @return  bool
162
     */
163
    public function isAttribute($key)
164
    {
165
        return $this->getMetadata()->hasAttribute($key);
166
    }
167
168
    /**
169
     * Determines if an attribute key is calculated.
170
     *
171
     * @param   string  $key    The attribute key.
172
     * @return  bool
173
     */
174
    protected function isCalculatedAttribute($key)
175
    {
176
        if (false === $this->isAttribute($key)) {
177
            return false;
178
        }
179
        return $this->getMetadata()->getAttribute($key)->isCalculated();
180
    }
181
182
    /**
183
     * Gets an attribute value.
184
     *
185
     * @param   string  $key    The attribute key (field) name.
186
     * @return  mixed
187
     */
188
    protected function getAttribute($key)
189
    {
190
        if (true === $this->isCalculatedAttribute($key)) {
191
            return $this->getCalculatedAttribute($key);
192
        }
193
        $this->touch();
194
        return $this->attributes->get($key);
195
    }
196
197
    /**
198
     * Gets a calculated attribute value.
199
     *
200
     * @param   string  $key    The attribute key (field) name.
201
     * @return  mixed
202
     */
203
    protected function getCalculatedAttribute($key)
204
    {
205
        $attrMeta = $this->getMetadata()->getAttribute($key);
206
        $class  = $attrMeta->calculated['class'];
207
        $method = $attrMeta->calculated['method'];
208
209
        $value = $class::$method($this);
210
        return $this->convertAttributeValue($key, $value);
211
    }
212
213
    /**
214
     * Determines if a property key is a relationship (either has-one or has-many).
215
     *
216
     * @api
217
     * @param   string  $key    The property key.
218
     * @return  bool
219
     */
220
    public function isRelationship($key)
221
    {
222
        return $this->getMetadata()->hasRelationship($key);
223
    }
224
225
    /**
226
     * Determines if a property key is a an inverse relationship.
227
     *
228
     * @api
229
     * @param   string  $key    The property key.
230
     * @return  bool
231
     */
232
    public function isInverse($key)
233
    {
234
        if (false === $this->isRelationship($key)) {
235
            return false;
236
        }
237
        return $this->getMetadata()->getRelationship($key)->isInverse;
238
    }
239
240
    /**
241
     * Determines if a property key is a has-one relationship.
242
     *
243
     * @api
244
     * @param   string  $key    The property key.
245
     * @return  bool
246
     */
247
    public function isHasOne($key)
248
    {
249
        if (false === $this->isRelationship($key)) {
250
            return false;
251
        }
252
        return $this->getMetadata()->getRelationship($key)->isOne();
253
    }
254
255
    /**
256
     * Determines if a property key is a has-many relationship.
257
     *
258
     * @api
259
     * @param   string  $key    The property key.
260
     * @return  bool
261
     */
262
    public function isHasMany($key)
263
    {
264
        if (false === $this->isRelationship($key)) {
265
            return false;
266
        }
267
        return $this->getMetadata()->getRelationship($key)->isMany();
268
    }
269
270
    /**
271
     * Gets a relationship value.
272
     *
273
     * @param   string  $key    The relationship key (field) name.
274
     * @return  Model|array|null
275
     * @throws  \RuntimeException If hasMany relationships are accessed directly.
276
     */
277
    protected function getRelationship($key)
278
    {
279
        if (true === $this->isHasOne($key)) {
280
            $this->touch();
281
            return $this->hasOneRelationships->get($key);
282
        }
283
        if (true === $this->isHasMany($key)) {
284
            $this->touch();
285
            $collection = $this->hasManyRelationships->get($key);
286
            if ($collection->isLoaded($collection)) {
287
                return iterator_to_array($collection);
288
            }
289
            return (true === $this->collectionAutoInit) ? iterator_to_array($collection) : $collection->allWithoutLoad();
290
        }
291
        return null;
292
    }
293
294
    /**
295
     * Pushes a Model into a has-many relationship collection.
296
     * This method must be used for has-many relationships. Direct set is not supported.
297
     * To completely replace a has-many, call clear() first and then push() the new Models.
298
     *
299
     * @api
300
     * @param   string  $key
301
     * @param   Model   $model
302
     * @return  self
303
     */
304
    public function push($key, Model $model)
305
    {
306
        if (true === $this->isHasOne($key)) {
307
            return $this->setHasOne($key, $model);
308
        }
309
        if (false === $this->isHasMany($key)) {
310
            return $this;
311
        }
312
        if (true === $this->isInverse($key)) {
313
            throw ModelException::cannotModifyInverse($this, $key);
314
        }
315
        $this->touch();
316
        $collection = $this->hasManyRelationships->get($key);
317
        $collection->push($model);
318
        $this->doDirtyCheck();
319
        return $this;
320
    }
321
322
    /**
323
     * Clears a has-many relationship collection, sets an attribute to null, or sets a has-one relationship to null.
324
     *
325
     * @api
326
     * @param   string  $key    The property key.
327
     * @return  self
328
     */
329
    public function clear($key)
330
    {
331
        if (true === $this->isAttribute($key)) {
332
            return $this->setAttribute($key, null);
333
        }
334
        if (true === $this->isHasOne($key)) {
335
            return $this->setHasOne($key, null);
336
        }
337
        if (true === $this->isInverse($key)) {
338
            throw ModelException::cannotModifyInverse($this, $key);
339
        }
340
        if (true === $this->isHasMany($key)) {
341
            $collection = $this->hasManyRelationships->get($key);
342
            $collection->clear();
343
            $this->doDirtyCheck();
344
            return $this;
345
        }
346
        return $this;
347
    }
348
349
    /**
350
     * Removes a specific Model from a has-many relationship collection.
351
     *
352
     * @api
353
     * @param   string  $key    The has-many relationship key.
354
     * @param   Model   $model  The model to remove from the collection.
355
     * @return  self
356
     */
357 View Code Duplication
    public function remove($key, Model $model)
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...
358
    {
359
        if (false === $this->isHasMany($key)) {
360
            return $this;
361
        }
362
        if (true === $this->isInverse($key)) {
363
            throw ModelException::cannotModifyInverse($this, $key);
364
        }
365
        $this->touch();
366
        $collection = $this->hasManyRelationships->get($key);
367
        $collection->remove($model);
368
        $this->doDirtyCheck();
369
        return $this;
370
    }
371
372
    /**
373
     * Sets a model property: an attribute value, a has-one model, or an entire has-many model collection.
374
     * Note: To push/remove a single Model into a has-many collection, or clear a collection, use @see push(), remove() and clear().
375
     * Is a proxy for @see setAttribute() and setRelationship()
376
     *
377
     * @api
378
     * @param   string  $key                The property field key.
379
     * @param   Model|Collection|null|mixed The value to set.
380
     * @return  self.
0 ignored issues
show
Documentation introduced by
The doc-type self. could not be parsed: Unknown type name "self." at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
381
     */
382
    public function set($key, $value)
383
    {
384
        if (true === $this->isAttribute($key)) {
385
            return $this->setAttribute($key, $value);
386
        }
387
        return $this->setRelationship($key, $value);
388
    }
389
390
    /**
391
     * Sets an attribute value.
392
     * Will convert the value to the proper, internal PHP/Modlr data type.
393
     * Will do a dirty check immediately after setting.
394
     *
395
     * @param   string  $key    The attribute key (field) name.
396
     * @param   mixed   $value  The value to apply.
397
     * @return  self
398
     */
399
    protected function setAttribute($key, $value)
400
    {
401
        if (true === $this->isCalculatedAttribute($key)) {
402
            return $this;
403
        }
404
        $this->touch();
405
        $value = $this->convertAttributeValue($key, $value);
406
        $this->attributes->set($key, $value);
407
        $this->doDirtyCheck();
408
        return $this;
409
    }
410
411
    protected function convertAttributeValue($key, $value)
412
    {
413
        return $this->store->convertAttributeValue($this->getDataType($key), $value);
414
    }
415
416
    /**
417
     * Gets a data type from an attribute key.
418
     *
419
     * @param   string  $key The attribute key.
420
     * @return  string
421
     */
422
    protected function getDataType($key)
423
    {
424
        return $this->getMetadata()->getAttribute($key)->dataType;
425
    }
426
427
    /**
428
     * Sets a relationship value.
429
     *
430
     * @param   string      $key
431
     * @param   Model|null  $value
432
     * @return  self
433
     */
434
    protected function setRelationship($key, $value)
435
    {
436
        if (true === $this->isHasOne($key)) {
437
            return $this->setHasOne($key, $value);
438
        }
439
        if (true === $this->isHasMany($key)) {
440
            throw new \RuntimeException('You cannot set a hasMany relationship directly. Please access using push(), clear(), and/or remove()');
441
        }
442
        return $this;
443
    }
444
445
    /**
446
     * Sets a has-one relationship.
447
     *
448
     * @param   string      $key    The relationship key (field) name.
449
     * @param   Model|null  $model  The model to relate.
450
     * @return  self
451
     */
452 View Code Duplication
    protected function setHasOne($key, Model $model = null)
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...
453
    {
454
        if (true === $this->isInverse($key)) {
455
            throw ModelException::cannotModifyInverse($this, $key);
456
        }
457
        if (null !== $model) {
458
            $this->validateRelSet($key, $model->getType());
459
        }
460
        $this->touch();
461
        $this->hasOneRelationships->set($key, $model);
462
        $this->doDirtyCheck();
463
        return $this;
464
    }
465
466
    /**
467
     * Validates that the model type (from a Model or Collection instance) can be set to the relationship field.
468
     *
469
     * @param   string  $relKey The relationship field key.
470
     * @param   string  $type   The model type that is being related.
471
     * @return  self
472
     */
473
    protected function validateRelSet($relKey, $type)
474
    {
475
        $relMeta = $this->getMetadata()->getRelationship($relKey);
476
        $relatedModelMeta = $this->store->getMetadataForRelationship($relMeta);
0 ignored issues
show
Bug introduced by
It seems like $relMeta defined by $this->getMetadata()->getRelationship($relKey) on line 475 can be null; however, As3\Modlr\Store\Store::g...tadataForRelationship() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
477
        $this->store->validateRelationshipSet($relatedModelMeta, $type);
478
        return $this;
479
    }
480
481
    /**
482
     * Determines if the model uses a particlar mixin.
483
     *
484
     * @api
485
     * @param   string  $name
486
     * @return  bool
487
     */
488
    public function usesMixin($name)
489
    {
490
        return $this->metadata->hasMixin($name);
491
    }
492
493
    /**
494
     * Saves the model.
495
     *
496
     * @api
497
     * @param   Implement cascade relationship saves. Or should the store handle this?
498
     * @return  self
499
     */
500
    public function save()
501
    {
502
        if (true === $this->getState()->is('deleted')) {
503
            return $this;
504
        }
505
        $this->store->commit($this);
506
        return $this;
507
    }
508
509
    /**
510
     * Rolls back a model to its original, database values.
511
     *
512
     * @api
513
     * @return  self
514
     */
515
    public function rollback()
516
    {
517
        $this->attributes->rollback();
518
        $this->hasOneRelationships->rollback();
519
        $this->hasManyRelationships->rollback();
520
        $this->doDirtyCheck();
521
        return $this;
522
    }
523
524
    /**
525
     * Reloads the model from the database.
526
     *
527
     * @api
528
     * @return  self
529
     */
530
    public function reload()
531
    {
532
        return $this->touch(true);
533
    }
534
535
    /**
536
     * Restores an in-memory deleted object back to the database.
537
     *
538
     * @api
539
     * @todo    Implement if needed. Or should restore clear a pending delete?
540
     * @return  self
541
     */
542
    public function restore()
543
    {
544
        return $this;
545
    }
546
547
    /**
548
     * Marks the record for deletion.
549
     * Will not remove from the database until $this->save() is called.
550
     *
551
     * @api
552
     * @return  self
553
     * @throws  \RuntimeException   If a new (unsaved) model is deleted.
554
     */
555
    public function delete()
556
    {
557
        if (true === $this->getState()->is('new')) {
558
            throw new \RuntimeException('You cannot delete a new model');
559
        }
560
        if (true === $this->getState()->is('deleted')) {
561
            return $this;
562
        }
563
        $this->getState()->setDeleting();
564
        return $this;
565
    }
566
567
    /**
568
     * Touches the model.
569
     * If the model is currently empty, it will query the database and fill/load the model.
570
     *
571
     * @param   bool    $force  Whether to force the load, even if the model is currently loaded.
572
     * @return  self
573
     */
574
    protected function touch($force = false)
575
    {
576
        if (true === $this->getState()->is('deleted')) {
577
            return $this;
578
        }
579
        if (true === $this->getState()->is('empty') || true === $force) {
580
            $record = $this->store->retrieveRecord($this->getType(), $this->getId());
581
            $this->initialize($record);
582
            $this->state->setLoaded();
583
            // @todo Should this trigger a postReload event? Likely not.
584
        }
585
        return $this;
586
    }
587
588
    /**
589
     * Applies an array of raw model properties (attributes and relationships) to the model instance.
590
     *
591
     * @todo    Confirm that we want this method. It's currently used for creating and updating via the API adapter. Also see initialize()
592
     * @param   array   $properties     The properties to apply.
593
     * @return  self
594
     */
595
    public function apply(array $properties)
596
    {
597
        $properties = $this->applyDefaultAttrValues($properties);
598
        foreach ($properties as $key => $value) {
599
            if (true === $this->isAttribute($key)) {
600
                $this->set($key, $value);
601
                continue;
602
            }
603
            if (true === $this->isHasOne($key)) {
604
                if (empty($value)) {
605
                    $this->clear($key);
606
                    continue;
607
                }
608
                $value = $this->store->loadProxyModel($value['type'], $value['id']);
609
                $this->set($key, $value);
610
                continue;
611
            }
612
613
        }
614
615
        foreach ($this->getMetadata()->getRelationships() as $key => $relMeta) {
616
            if (true === $relMeta->isOne()) {
617
                continue;
618
            }
619
            // Array key exists must exist to determine if the
620
            if (!isset($properties[$key]) || true === $relMeta->isInverse) {
621
                continue;
622
            }
623
624
            $this->clear($key);
625
            $collection = $this->store->createCollection($relMeta, $properties[$key]);
626
            foreach ($collection->allWithoutLoad() as $value) {
627
                $this->push($key, $value);
628
            }
629
        }
630
        $this->doDirtyCheck();
631
        return $this;
632
    }
633
634
    /**
635
     * Initializes the model and loads its attributes and relationships.
636
     *
637
     * @todo    Made public so collections can initialize models. Not sure if we want this??
638
     * @param   Record|null   $record     The db attributes and relationships to apply.
639
     * @return  self
640
     */
641
    public function initialize(Record $record = null)
642
    {
643
        $hasOne = [];
644
        $hasMany = [];
645
        $attributes = [];
646
647
        if (null !== $record) {
648
            $attributes = $this->applyDefaultAttrValues($attributes);
649
            foreach ($record->getProperties() as $key => $value) {
650
                if (true === $this->isAttribute($key)) {
651
                    // Load attribute.
652
                    $attributes[$key] = $this->convertAttributeValue($key, $value);
653
                    continue;
654
                }
655
                if (true === $this->isHasOne($key)) {
656
                    // Load hasOne relationship.
657
                    $hasOne[$key] = $this->store->loadProxyModel($value['type'], $value['id']);
658
                    continue;
659
                }
660
            }
661
        }
662
663
        foreach ($this->getMetadata()->getRelationships() as $key => $relMeta) {
664
            if (true === $relMeta->isOne()) {
665
                continue;
666
            }
667
            if (true === $relMeta->isInverse) {
668
                $hasMany[$key] = $this->store->createInverseCollection($relMeta, $this);
669
            } else {
670
                $references = (null === $record || !isset($record->getProperties()[$key])) ? [] : $record->getProperties()[$key];
671
                $hasMany[$key] = $this->store->createCollection($relMeta, $references);
672
            }
673
        }
674
675
        $this->attributes           = (null === $this->attributes) ? new Attributes($attributes) : $this->attributes->replace($attributes);
676
        $this->hasOneRelationships  = (null === $this->hasOneRelationships) ? new Relationships\HasOne($hasOne) : $this->hasOneRelationships->replace($hasOne);
677
        $this->hasManyRelationships = (null === $this->hasManyRelationships) ? new Relationships\HasMany($hasMany) : $this->hasManyRelationships->replace($hasMany);
678
        $this->doDirtyCheck();
679
        return $this;
680
    }
681
682
    /**
683
     * Applies default attribute values from metadata, if set.
684
     *
685
     * @param   array   $attributes     The attributes to apply the defaults to.
686
     * @return  array
687
     */
688
    protected function applyDefaultAttrValues(array $attributes = [])
689
    {
690
        // Set defaults for each attribute.
691
        foreach ($this->getMetadata()->getAttributes() as $key => $attrMeta) {
692
            if (!isset($attrMeta->defaultValue)) {
693
                continue;
694
            }
695
            $attributes[$key] = $this->convertAttributeValue($key, $attrMeta->defaultValue);
696
        }
697
698
        // Set defaults for the entire entity.
699
        foreach ($this->getMetadata()->defaultValues as $key => $value) {
700
            $attributes[$key] = $this->convertAttributeValue($key, $value);
701
        }
702
        return $attributes;
703
    }
704
705
    /**
706
     * Determines if the model is currently dirty.
707
     * Checks against the attribute and relationship dirty states.
708
     *
709
     * @api
710
     * @return  bool
711
     */
712
    public function isDirty()
713
    {
714
        return true === $this->attributes->areDirty()
715
            || true === $this->hasOneRelationships->areDirty()
716
            || true === $this->hasManyRelationships->areDirty()
717
        ;
718
    }
719
720
    /**
721
     * Does a dirty check and sets the state to this model.
722
     *
723
     * @return  self
724
     */
725
    protected function doDirtyCheck()
726
    {
727
        $this->state->setDirty($this->isDirty());
728
        return $this;
729
    }
730
731
    /**
732
     * Gets the current change set of attributes and relationships.
733
     *
734
     * @api
735
     * @return  array
736
     */
737
    public function getChangeSet()
738
    {
739
        $changeset = [
740
            'attributes'    => $this->attributes->calculateChangeSet(),
741
            'hasOne'        => $this->hasOneRelationships->calculateChangeSet(),
742
            'hasMany'       => $this->hasManyRelationships->calculateChangeSet(),
743
        ];
744
745
        foreach ($changeset as $type => $properties) {
746
            $changeset[$type] = $this->filterNotSavedProperties($type, $properties);
747
        }
748
        return $changeset;
749
    }
750
751
    /**
752
     * Gets the model state object.
753
     *
754
     * @todo    Should this be public? State setting should likely be locked from the outside world.
755
     * @return  State
756
     */
757
    public function getState()
758
    {
759
        return $this->state;
760
    }
761
762
    /**
763
     * Gets the metadata for this model.
764
     *
765
     * @return  EntityMetadata
766
     */
767
    public function getMetadata()
768
    {
769
        return $this->metadata;
770
    }
771
772
    /**
773
     * Removes properties marked as non-saved.
774
     *
775
     * @param   string  $propType
776
     * @param   array   $properties
777
     * @return  array
778
     */
779
    private function filterNotSavedProperties($propType, array $properties)
780
    {
781
        $method = ('attributes' === $propType) ? 'getAttributes' : 'getRelationships';
782
        foreach ($this->getMetadata()->$method() as $fieldKey => $propMeta) {
783
            if (true === $propMeta->shouldSave() || !isset($properties[$fieldKey])) {
784
                continue;
785
            }
786
            unset($properties[$fieldKey]);
787
        }
788
        return $properties;
789
    }
790
}
791