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

Criteriable::addCriterion()   B

Complexity

Conditions 7
Paths 2

Size

Total Lines 15
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 15
rs 8.2222
cc 7
eloc 9
nc 2
nop 2
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
    /**
13
     * List of repository criteria.
14
     *
15
     * @var array
16
     */
17
    protected $criteria = [];
18
19
    /**
20
     * List of default repository criteria.
21
     *
22
     * @var array
23
     */
24
    protected $defaultCriteria = [];
25
26
    /**
27
     * Skip criteria flag.
28
     * If setted to true criteria will not be apply to the query.
29
     *
30
     * @var bool
31
     */
32
    protected $skipCriteria = false;
33
34
    /**
35
     * Skip default criteria flag.
36
     * If setted to true default criteria will not be added to the criteria list.
37
     *
38
     * @var bool
39
     */
40
    protected $skipDefaultCriteria = false;
41
42
    /**
43
     * Return name for the criterion.
44
     * If as criterion in parameter passed string we assume that is criterion class name.
45
     *
46
     * @param CriterionContract|Closure|String $criteria
47
     *
48
     * @return string
49
     */
50
    public function getCriterionName($criteria)
51
    {
52
        if ($criteria instanceof Closure) {
53
            return spl_object_hash($criteria);
54
        }
55
56
        return is_object($criteria) ? get_class($criteria) : $criteria;
57
    }
58
59
    /**
60
     * Add criterion to the specific list.
61
     * low-level implementation of adding criterion to the list
62
     *
63
     * @param Closure|CriterionContract $criterion
64
     * @param string                    $list
65
     *
66
     * @return $this
67
     */
68
    protected function addCriterion($criterion, $list)
69
    {
70
        if (property_exists($this, $list) &&
71
            ($criterion instanceof Closure ||
72
                $criterion instanceof CriterionContract ||
73
                (is_string($criterion) &&
74
                    class_exists($criterion) &&
75
                    ($criterion = new $criterion()) instanceof CriterionContract
76
                ))
77
        ) {
78
            $this->$list[$this->getCriterionName($criterion)] = $criterion;
79
        }
80
81
        return $this;
82
    }
83
84
    /**
85
     * Push criterion to the criteria list.
86
     *
87
     * @param CriterionContract|Closure $criterion
88
     *
89
     * @return $this
90
     */
91
    public function pushCriterion($criterion)
92
    {
93
        $this->addCriterion($criterion, 'criteria');
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
        return $this;
108
    }
109
110
    /**
111
     * Remove provided criteria from criteria list.
112
     *
113
     * @param array $criteria
114
     *
115
     * @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...
116
     */
117
    public function removeCriteria(array $criteria)
118
    {
119
        array_walk($criteria, function ($criterion) {
120
            $this->removeCriterion($criterion);
121
        });
122
123
        return $this;
124
    }
125
126
    /**
127
     * Push array of criteria to the criteria list.
128
     *
129
     * @param array $criteria
130
     *
131
     * @return $this
132
     */
133
    public function pushCriteria(array $criteria)
134
    {
135
        array_walk($criteria, function ($criterion) {
136
            $this->pushCriterion($criterion);
137
        });
138
139
        return $this;
140
    }
141
142
    /**
143
     * Flush criteria list
144
     *
145
     * @return $this
146
     */
147
    public function flushCriteria()
148
    {
149
        $this->criteria = [];
150
        return $this;
151
    }
152
153
    /**
154
     * Set default criteria list.
155
     *
156
     * @param array $criteria
157
     *
158
     * @return $this
159
     */
160
    public function setDefaultCriteria(array $criteria)
161
    {
162
        foreach ($criteria as $criterion) {
163
            $this->addCriterion($criterion, 'defaultCriteria');
164
        }
165
166
        return $this;
167
    }
168
169
    /**
170
     * Return default criteria list.
171
     *
172
     * @return array
173
     */
174
    public function getDefaultCriteria()
175
    {
176
        return $this->defaultCriteria;
177
    }
178
179
    /**
180
     * Return current list of criteria.
181
     *
182
     * @return array
183
     */
184
    public function getCriteria()
185
    {
186
        if ($this->skipCriteria) {
187
            return [];
188
        }
189
190
        return $this->skipDefaultCriteria ? $this->criteria : array_merge($this->getDefaultCriteria(), $this->criteria);
191
    }
192
193
    /**
194
     * Set skipCriteria flag.
195
     *
196
     * @param bool|true $flag
197
     *
198
     * @return $this
199
     */
200
    public function skipCriteria($flag = true)
201
    {
202
        $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...
203
        return $this;
204
    }
205
206
    /**
207
     * Set skipDefaultCriteria flag.
208
     *
209
     * @param bool|true $flag
210
     *
211
     * @return $this
212
     */
213
    public function skipDefaultCriteria($flag = true)
214
    {
215
        $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...
216
        return $this;
217
    }
218
219
    /**
220
     * Check if a given criterion name now in the criteria list.
221
     *
222
     * @param CriterionContract|Closure|string $criterion
223
     *
224
     * @return bool
225
     */
226
    public function hasCriterion($criterion)
227
    {
228
        return isset($this->getCriteria()[$this->getCriterionName($criterion)]);
229
    }
230
231
    /**
232
     * Return criterion object or closure from criteria list by name.
233
     *
234
     * @param $criterion
235
     * @return CriterionContract|Closure|null
236
     */
237
    public function getCriterion($criterion)
238
    {
239
        if ($this->hasCriterion($criterion)) {
240
            return $this->getCriteria()[$this->getCriterionName($criterion)];
241
        }
242
    }
243
244
    /**
245
     * Apply criteria list to the given query.
246
     *
247
     * @param $query
248
     * @param $repository
249
     *
250
     * @return mixed
251
     */
252
    public function applyCriteria($query, $repository)
253
    {
254
        foreach ($this->getCriteria() as $criterion) {
255
            if ($criterion instanceof CriterionContract) {
256
                $query = $criterion->apply($query, $repository);
257
            } elseif ($criterion instanceof Closure) {
258
                $query = $criterion($query, $repository);
259
            }
260
        }
261
262
        return $query;
263
    }
264
}