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

ModelCollection::validateModelClass()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 6
rs 9.4285
cc 2
eloc 3
nc 2
nop 1
1
<?php
2
3
namespace As3\Modlr\Models\Collections;
4
5
use As3\Modlr\Metadata\EntityMetadata;
6
use As3\Modlr\Models\AbstractModel;
7
use As3\Modlr\Models\Model;
8
use As3\Modlr\Store\Store;
9
10
/**
11
 * Model collection that contains record representations from a persistence (database) layer.
12
 *
13
 * @author Jacob Bare <[email protected]>
14
 */
15
abstract class ModelCollection extends AbstractCollection
16
{
17
    /**
18
     * @var EntityMetadata
19
     */
20
    protected $metadata;
21
22
    /**
23
     * Constructor.
24
     *
25
     * @param   EntityMetadata  $metadata
26
     * @param   Store           $store
27
     * @param   AbstractModel[] $models
28
     */
29
    public function __construct(EntityMetadata $metadata, Store $store, array $models = [])
30
    {
31
        $this->metadata = $metadata;
32
        parent::__construct($store, $models);
33
    }
34
35
    /**
36
     * Returns all models in this collection without triggering auto-loading.
37
     *
38
     * @return  AbstractModel[]
39
     */
40
    public function allWithoutLoad()
41
    {
42
        return $this->models;
43
    }
44
45
    /**
46
     * Gets the identifiers for this collection.
47
     *
48
     * @param   bool    $onlyUnloaded   Whether to only include unloaded models in the results.
49
     * @return  array
50
     */
51
    abstract public function getIdentifiers($onlyUnloaded = true);
52
53
    /**
54
     * Gets the metadata for the model collection.
55
     *
56
     * @return  EntityMetadata
57
     */
58
    public function getMetadata()
59
    {
60
        return $this->metadata;
61
    }
62
63
    /**
64
     * Gets the query field for this collection.
65
     *
66
     * @return  string
67
     */
68
    abstract public function getQueryField();
69
70
    /**
71
     * {@inheritdoc}
72
     *
73
     * Overloaded to ensure models are loaded from the store.
74
     *
75
     */
76
    public function getSingleResult()
77
    {
78
        $this->loadFromStore();
79
        return parent::getSingleResult();
80
    }
81
82
    /**
83
     * {@inheritdoc}
84
     */
85
    public function getType()
86
    {
87
        return $this->getMetadata()->type;
88
    }
89
90
    /**
91
     * {@inheritDoc}
92
     */
93
    public function rewind()
94
    {
95
        $this->loadFromStore();
96
        parent::rewind();
97
    }
98
99
    /**
100
     * Loads this collection from the store.
101
     */
102
    protected function loadFromStore()
103
    {
104
        if (false === $this->isLoaded()) {
105
            // Loads collection from the database on iteration.
106
            $models = $this->store->loadCollection($this);
107
            $this->setModels($models);
108
            $this->loaded = true;
109
        }
110
    }
111
112
    /**
113
     * {@inheritdoc}
114
     */
115
    protected function modelsMatch(AbstractModel $model, AbstractModel $loaded)
116
    {
117
        $this->validateModelClass($model);
118
        $this->validateModelClass($loaded);
119
        return $model->getType() === $loaded->getType() && $model->getId() === $loaded->getId();
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class As3\Modlr\Models\AbstractModel as the method getType() does only exist in the following sub-classes of As3\Modlr\Models\AbstractModel: As3\Modlr\Models\Model. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
Bug introduced by
It seems like you code against a specific sub-type and not the parent class As3\Modlr\Models\AbstractModel as the method getId() does only exist in the following sub-classes of As3\Modlr\Models\AbstractModel: As3\Modlr\Models\Model. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
120
    }
121
122
    /**
123
     * {@inheritdoc}
124
     */
125
    protected function validateAdd(AbstractModel $model)
126
    {
127
        $this->validateModelClass($model);
128
        $this->store->validateRelationshipSet($this->getMetadata(), $model->getType());
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class As3\Modlr\Models\AbstractModel as the method getType() does only exist in the following sub-classes of As3\Modlr\Models\AbstractModel: As3\Modlr\Models\Model. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
129
    }
130
131
    /**
132
     * Validates that the model class instance is supported.
133
     *
134
     * @param   AbstractModel   $model
135
     * @throws  \InvalidArgumentException
136
     */
137
    protected function validateModelClass(AbstractModel $model)
138
    {
139
        if (!$model instanceof Model) {
140
            throw new \InvalidArgumentException('The model must be an instanceof of Model');
141
        }
142
    }
143
}
144