Completed
Push — master ( 4ac004...470d43 )
by Gabriel
06:41
created

Collection::getManager()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2.0625

Importance

Changes 0
Metric Value
dl 0
loc 8
ccs 3
cts 4
cp 0.75
rs 10
c 0
b 0
f 0
cc 2
nc 2
nop 0
crap 2.0625
1
<?php
2
3
namespace Nip\Records\Collections;
4
5
use Nip\Collections\Collection as AbstractCollection;
6
use Nip\HelperBroker;
7
use Nip\Records\AbstractModels\Record as Record;
8
use Nip\Records\AbstractModels\RecordManager as Records;
9
10
/**
11
 * Class Collection
12
 * @package Nip\Records\Collections
13
 */
14
class Collection extends AbstractCollection
15
{
16
    protected $_indexKey = false;
17
18
    /**
19
     * @var Records
20
     */
21
    protected $_manager = null;
22
23
24
    /**
25
     * @param $relations
26
     */
27
    public function loadRelations($relations)
28
    {
29
        if (is_string($relations)) {
30
            $relations = func_get_args();
31
        }
32
33
        foreach ($relations as $relation) {
34
            $this->loadRelation($relation);
35
        }
36
    }
37
38
    /**
39
     * @param $name
40
     * @return Collection
41
     */
42
    public function loadRelation($name)
43
    {
44
        $relation = $this->getRelation($name);
45
        $results = $relation->getEagerResults($this);
46
        $relation->match($this, $results);
47
        return $results;
48
    }
49
50
    /**
51
     * @param $name
52
     * @return \Nip\Records\Relations\Relation|null
53
     */
54
    public function getRelation($name)
55
    {
56
        return $this->getManager()->getRelation($name);
0 ignored issues
show
Documentation Bug introduced by
The method getRelation does not exist on object<Nip\Records\AbstractModels\RecordManager>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
57
    }
58
59
    /**
60
     * @return Records
61
     */
62 1
    public function getManager()
63
    {
64 1
        if ($this->_manager == null) {
65
            $this->initManager();
66
        }
67
68 1
        return $this->_manager;
69
    }
70
71
    /**
72
     * @param Records $manager
73
     * @return $this
74
     */
75 2
    public function setManager(Records $manager)
76
    {
77 2
        $this->_manager = $manager;
78
79 2
        return $this;
80
    }
81
82
    public function initManager()
83
    {
84
        $manager = $this->rewind()->getManager();
85
        $this->setManager($manager);
86
    }
87
88
    /**
89
     * @return string
90
     */
91
    public function toJSON()
92
    {
93
        $return = [];
94
        foreach ($this as $item) {
95
            $return = $item->toArray();
96
        }
97
98
        return json_encode($return);
99
    }
100
101
    public function save()
102
    {
103
        if (count($this) > 0) {
104
            foreach ($this as $item) {
105
                $item->save();
106
            }
107
        }
108
    }
109
110
    /**
111
     * @param Record $record
112
     * @param string $index
113
     */
114 4
    public function add($record, $index = null)
115
    {
116 4
        $index = $this->getRecordKey($record, $index);
0 ignored issues
show
Bug introduced by
It seems like $index defined by $this->getRecordKey($record, $index) on line 116 can also be of type string; however, Nip\Records\Collections\Collection::getRecordKey() does only seem to accept null, 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...
117 4
        parent::add($record, $index);
118 4
    }
119
120
    /**
121
     * @param Record $record
122
     * @param null $index
123
     * @return bool|mixed|null
124
     */
125 4
    public function getRecordKey(Record $record, $index = null)
126
    {
127 4
        if ($index) {
128
            $index = $record->{$index};
129
        } else {
130 4
            $index = $this->getIndexKey();
131 4
            $index = $index ? $record->{$index} : $record->getPrimaryKey();
132 4
            if (!$index) {
133 3
                $index = null;
134
            }
135
        }
136 4
        return $index;
137
    }
138
139
    /**
140
     * @return bool
141
     */
142 4
    public function getIndexKey()
143
    {
144 4
        return $this->_indexKey;
145
    }
146
147
    /**
148
     * @param $key
149
     * @return mixed
150
     */
151
    public function setIndexKey($key)
152
    {
153
        return $this->_indexKey = $key;
154
    }
155
156
    /**
157
     * @param Record $record
158
     * @return bool
159
     */
160
    public function has($record)
161
    {
162
        if ($record instanceof Record) {
163
            return $this->hasRecord($record);
164
        }
165
166
        return parent::has($record);
167
    }
168
169
    /**
170
     * @param Record $record
171
     * @return bool
172
     */
173
    public function hasRecord(Record $record)
174
    {
175
        $index = $this->getRecordKey($record);
176
177
        return parent::has($index) && $this->get($index) == $record;
0 ignored issues
show
Comprehensibility Bug introduced by
It seems like you call parent on a different method (has() instead of hasRecord()). Are you sure this is correct? If so, you might want to change this to $this->has().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
178
    }
179
180
    /**
181
     * @param $record
182
     */
183
    public function remove($record)
184
    {
185
        foreach ($this as $key => $item) {
186
            if ($item == $record) {
187
                unset($this[$key]);
188
            }
189
        }
190
    }
191
192
    /**
193
     * When $each is true, each record gets it's delete() method called.
194
     * Otherwise, a delete query is built for the entire collection
195
     *
196
     * @param bool $each
197
     * @return $this
198
     */
199
    public function delete($each = false)
200
    {
201
        if (count($this) > 0) {
202
            if ($each) {
203
                foreach ($this as $item) {
204
                    $item->delete();
205
                }
206
            } else {
207
                $primaryKey = $this->getManager()->getPrimaryKey();
208
                $pk_list = HelperBroker::get('Arrays')->pluck($this, $primaryKey);
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Nip\Helpers\AbstractHelper as the method pluck() does only exist in the following sub-classes of Nip\Helpers\AbstractHelper: Nip_Helper_Arrays. 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...
209
210
                $query = $this->getManager()->newQuery("delete");
211
                $query->where("$primaryKey IN ?", $pk_list);
212
                $query->execute();
213
            }
214
215
            $this->clear();
216
        }
217
218
        return $this;
219
    }
220
}
221