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

Criteriable   A

Complexity

Total Complexity 30

Size/Duplication

Total Lines 247
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 247
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 5 1
A removeCriterion() 0 5 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 5 1
A skipDefaultCriteria() 0 5 1
A hasCriterion() 0 4 1
A getCriterion() 0 8 2
A applyCriteria() 0 12 4
1
<?php
2
namespace Rinvex\Repository\Traits;
3
4
use Closure;
5
use Rinvex\Repository\Contracts\CriterionContract;
6
use Rinvex\Repository\Contracts\RepositoryContract;
7
8
trait Criteriable
9
{
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
     * @return string
47
     */
48
    public function getCriterionName($criteria)
49
    {
50
        if ($criteria instanceof Closure) {
51
            return spl_object_hash($criteria);
52
        }
53
54
        return is_object($criteria) ? get_class($criteria) : $criteria;
55
    }
56
57
    /**
58
     * Add criterion to the specific list.
59
     * low-level implementation of adding criterion to the list
60
     *
61
     * @param Closure|CriterionContract $criterion
62
     * @param string $list
63
     *
64
     * @return $this
65
     */
66
    protected function addCriterion($criterion, $list)
67
    {
68
        if (property_exists($this, $list) &&
69
            ($criterion instanceof Closure ||
70
                $criterion instanceof CriterionContract ||
71
                (is_string($criterion) &&
72
                    class_exists($criterion) &&
73
                    ($criterion = new $criterion) instanceof CriterionContract
74
                ))
75
        ) {
76
            $this->$list[$this->getCriterionName($criterion)] = $criterion;
77
        }
78
79
        return $this;
80
    }
81
82
    /**
83
     * Push criterion to the criteria list.
84
     *
85
     * @param CriterionContract|Closure $criterion
86
     * @return $this
87
     */
88
    public function pushCriterion($criterion)
89
    {
90
        $this->addCriterion($criterion, 'criteria');
91
        return $this;
92
    }
93
94
    /**
95
     * Remove provided criterion from criteria list
96
     *
97
     * @param CriterionContract|Closure|string $criterion
98
     * @return $this
99
     */
100
    public function removeCriterion($criterion)
101
    {
102
        unset($this->criteria[$this->getCriterionName($criterion)]);
103
        return $this;
104
    }
105
106
    /**
107
     * Remove provided criteria from criteria list
108
     *
109
     * @param array $criteria
110
     * @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...
111
     */
112
    public function removeCriteria(array $criteria)
113
    {
114
        array_walk($criteria, function ($criterion) {
115
            $this->removeCriterion($criterion);
116
        });
117
118
        return $this;
119
    }
120
121
    /**
122
     * Flush criteria list
123
     *
124
     * @return $this
125
     */
126
    public function pushCriteria(array $criteria)
127
    {
128
        array_walk($criteria, function ($criterion) {
129
            $this->pushCriterion($criterion);
130
        });
131
132
        return $this;
133
    }
134
135
    /**
136
     * Flush criteria list
137
     *
138
     * @return $this
139
     */
140
    public function flushCriteria()
141
    {
142
        $this->criteria = [];
143
        return $this;
144
    }
145
146
    /**
147
     * Set default criteria list.
148
     *
149
     * @param array $criteria
150
     * @return $this
151
     */
152
    public function setDefaultCriteria(array $criteria)
153
    {
154
        foreach ($criteria as $criterion) {
155
            $this->addCriterion($criterion, 'defaultCriteria');
156
        }
157
158
        return $this;
159
    }
160
161
    /**
162
     * Return default criteria list
163
     *
164
     * @return array
165
     */
166
    public function getDefaultCriteria()
167
    {
168
        return $this->defaultCriteria;
169
    }
170
171
    /**
172
     * Return current list of criteria.
173
     *
174
     * @return array
175
     */
176
    public function getCriteria()
177
    {
178
        if ($this->skipCriteria) {
179
            return [];
180
        }
181
182
        return $this->skipDefaultCriteria ? $this->criteria : array_merge($this->getDefaultCriteria(), $this->criteria);
183
    }
184
185
    /**
186
     * Set skipCriteria flag.
187
     *
188
     * @param bool|true $flag
189
     * @return $this
190
     */
191
    public function skipCriteria($flag = true)
192
    {
193
        $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...
194
        return $this;
195
    }
196
197
    /**
198
     * Set skipDefaultCriteria flag.
199
     *
200
     * @param bool|true $flag
201
     * @return $this
202
     */
203
    public function skipDefaultCriteria($flag = true)
204
    {
205
        $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...
206
        return $this;
207
    }
208
209
    /**
210
     * Check if a given criterion name now in the criteria list.
211
     *
212
     * @param CriterionContract|Closure|string $criterion
213
     * @return bool
214
     */
215
    public function hasCriterion($criterion)
216
    {
217
        return isset($this->getCriteria()[$this->getCriterionName($criterion)]);
218
    }
219
220
    /**
221
     * Return criterion object or closure from criteria list by name.
222
     *
223
     * @param $criterion
224
     * @return CriterionContract|Closure|null
225
     */
226
    public function getCriterion($criterion)
227
    {
228
        if ($this->hasCriterion($criterion)) {
229
            return $this->getCriteria()[$this->getCriterionName($criterion)];
230
        }
231
232
        return null;
233
    }
234
235
    /**
236
     * Apply criteria list to the given query
237
     *
238
     * @param $query
239
     * @param $repository
240
     * @return mixed
241
     */
242
    public function applyCriteria($query, $repository)
243
    {
244
        foreach ($this->getCriteria() as $criterion) {
245
            if ($criterion instanceof CriterionContract) {
246
                $query = $criterion->apply($query, $repository);
247
            } elseif ($criterion instanceof Closure) {
248
                $query = $criterion($query, $repository);
249
            }
250
        }
251
252
        return $query;
253
    }
254
}