Completed
Pull Request — develop (#76)
by
unknown
02:41
created

Criteriable   A

Complexity

Total Complexity 30

Size/Duplication

Total Lines 260
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 260
wmc 30
lcom 1
cbo 1
rs 10

15 Methods

Rating   Name   Duplication   Size   Complexity  
A getCriterionName() 0 8 3
B addCriterion() 0 15 7
A pushCriterion() 0 6 1
A removeCriterion() 0 6 1
A removeCriteria() 0 8 1
A pushCriteria() 0 8 1
A flushCriteria() 0 5 1
A setDefaultCriteria() 0 8 2
A getDefaultCriteria() 0 4 1
A getCriteria() 0 8 3
A skipCriteria() 0 6 1
A skipDefaultCriteria() 0 6 1
A hasCriterion() 0 4 1
A getCriterion() 0 6 2
A applyCriteria() 0 12 4
1
<?php
2
3
namespace Rinvex\Repository\Traits;
4
5
use Closure;
6
use Rinvex\Repository\Contracts\CriterionContract;
7
use Rinvex\Repository\Contracts\RepositoryContract;
8
9
trait Criteriable
10
{
11
    /**
12
     * List of repository criteria.
13
     *
14
     * @var array
15
     */
16
    protected $criteria = [];
17
18
    /**
19
     * List of default repository criteria.
20
     *
21
     * @var array
22
     */
23
    protected $defaultCriteria = [];
24
25
    /**
26
     * Skip criteria flag.
27
     * If setted to true criteria will not be apply to the query.
28
     *
29
     * @var bool
30
     */
31
    protected $skipCriteria = false;
32
33
    /**
34
     * Skip default criteria flag.
35
     * If setted to true default criteria will not be added to the criteria list.
36
     *
37
     * @var bool
38
     */
39
    protected $skipDefaultCriteria = false;
40
41
    /**
42
     * Return name for the criterion.
43
     * If as criterion in parameter passed string we assume that is criterion class name.
44
     *
45
     * @param CriterionContract|Closure|string $criteria
46
     *
47
     * @return string
48
     */
49
    public function getCriterionName($criteria)
50
    {
51
        if ($criteria instanceof Closure) {
52
            return spl_object_hash($criteria);
53
        }
54
55
        return is_object($criteria) ? get_class($criteria) : $criteria;
56
    }
57
58
    /**
59
     * Add criterion to the specific list.
60
     * low-level implementation of adding criterion to the list.
61
     *
62
     * @param Closure|CriterionContract $criterion
63
     * @param string                    $list
64
     *
65
     * @return $this
66
     */
67
    protected function addCriterion($criterion, $list)
68
    {
69
        if (property_exists($this, $list) &&
70
            ($criterion instanceof Closure ||
71
                $criterion instanceof CriterionContract ||
72
                (is_string($criterion) &&
73
                    class_exists($criterion) &&
74
                    ($criterion = new $criterion()) instanceof CriterionContract
75
                ))
76
        ) {
77
            $this->$list[$this->getCriterionName($criterion)] = $criterion;
78
        }
79
80
        return $this;
81
    }
82
83
    /**
84
     * Push criterion to the criteria list.
85
     *
86
     * @param CriterionContract|Closure $criterion
87
     *
88
     * @return $this
89
     */
90
    public function pushCriterion($criterion)
91
    {
92
        $this->addCriterion($criterion, 'criteria');
93
94
        return $this;
95
    }
96
97
    /**
98
     * Remove provided criterion from criteria list.
99
     *
100
     * @param CriterionContract|Closure|string $criterion
101
     *
102
     * @return $this
103
     */
104
    public function removeCriterion($criterion)
105
    {
106
        unset($this->criteria[$this->getCriterionName($criterion)]);
107
108
        return $this;
109
    }
110
111
    /**
112
     * Remove provided criteria from criteria list.
113
     *
114
     * @param array $criteria
115
     *
116
     * @return RepositoryContract
0 ignored issues
show
Documentation introduced by
Should the return type not be Criteriable?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
117
     */
118
    public function removeCriteria(array $criteria)
119
    {
120
        array_walk($criteria, function ($criterion) {
121
            $this->removeCriterion($criterion);
122
        });
123
124
        return $this;
125
    }
126
127
    /**
128
     * Push array of criteria to the criteria list.
129
     *
130
     * @param array $criteria
131
     *
132
     * @return $this
133
     */
134
    public function pushCriteria(array $criteria)
135
    {
136
        array_walk($criteria, function ($criterion) {
137
            $this->pushCriterion($criterion);
138
        });
139
140
        return $this;
141
    }
142
143
    /**
144
     * Flush criteria list.
145
     *
146
     * @return $this
147
     */
148
    public function flushCriteria()
149
    {
150
        $this->criteria = [];
151
        return $this;
152
    }
153
154
    /**
155
     * Set default criteria list.
156
     *
157
     * @param array $criteria
158
     *
159
     * @return $this
160
     */
161
    public function setDefaultCriteria(array $criteria)
162
    {
163
        foreach ($criteria as $criterion) {
164
            $this->addCriterion($criterion, 'defaultCriteria');
165
        }
166
167
        return $this;
168
    }
169
170
    /**
171
     * Return default criteria list.
172
     *
173
     * @return array
174
     */
175
    public function getDefaultCriteria()
176
    {
177
        return $this->defaultCriteria;
178
    }
179
180
    /**
181
     * Return current list of criteria.
182
     *
183
     * @return array
184
     */
185
    public function getCriteria()
186
    {
187
        if ($this->skipCriteria) {
188
            return [];
189
        }
190
191
        return $this->skipDefaultCriteria ? $this->criteria : array_merge($this->getDefaultCriteria(), $this->criteria);
192
    }
193
194
    /**
195
     * Set skipCriteria flag.
196
     *
197
     * @param bool|true $flag
198
     *
199
     * @return $this
200
     */
201
    public function skipCriteria($flag = true)
202
    {
203
        $this->skipCriteria = $flag;
0 ignored issues
show
Documentation Bug introduced by
It seems like $flag can also be of type object<Rinvex\Repository\Traits\true>. However, the property $skipCriteria is declared as type boolean. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
204
205
        return $this;
206
    }
207
208
    /**
209
     * Set skipDefaultCriteria flag.
210
     *
211
     * @param bool|true $flag
212
     *
213
     * @return $this
214
     */
215
    public function skipDefaultCriteria($flag = true)
216
    {
217
        $this->skipDefaultCriteria = $flag;
0 ignored issues
show
Documentation Bug introduced by
It seems like $flag can also be of type object<Rinvex\Repository\Traits\true>. However, the property $skipDefaultCriteria is declared as type boolean. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
218
219
        return $this;
220
    }
221
222
    /**
223
     * Check if a given criterion name now in the criteria list.
224
     *
225
     * @param CriterionContract|Closure|string $criterion
226
     *
227
     * @return bool
228
     */
229
    public function hasCriterion($criterion)
230
    {
231
        return isset($this->getCriteria()[$this->getCriterionName($criterion)]);
232
    }
233
234
    /**
235
     * Return criterion object or closure from criteria list by name.
236
     *
237
     * @param $criterion
238
     *
239
     * @return CriterionContract|Closure|null
240
     */
241
    public function getCriterion($criterion)
242
    {
243
        if ($this->hasCriterion($criterion)) {
244
            return $this->getCriteria()[$this->getCriterionName($criterion)];
245
        }
246
    }
247
248
    /**
249
     * Apply criteria list to the given query.
250
     *
251
     * @param $query
252
     * @param $repository
253
     *
254
     * @return mixed
255
     */
256
    public function applyCriteria($query, $repository)
257
    {
258
        foreach ($this->getCriteria() as $criterion) {
259
            if ($criterion instanceof CriterionContract) {
260
                $query = $criterion->apply($query, $repository);
261
            } elseif ($criterion instanceof Closure) {
262
                $query = $criterion($query, $repository);
263
            }
264
        }
265
266
        return $query;
267
    }
268
}