Failed Conditions
Push — refactor/improve-static-analys... ( a7b39f...bdf823 )
by Bas
12:11
created

BuildsWhereClauses   A

Complexity

Total Complexity 35

Size/Duplication

Total Lines 326
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
wmc 35
eloc 96
c 2
b 0
f 0
dl 0
loc 326
ccs 69
cts 69
cp 1
rs 9.6

9 Methods

Rating   Name   Duplication   Size   Complexity  
A whereBetween() 0 15 2
A addWhereNotExistsQuery() 0 20 4
A addWhereExistsQuery() 0 25 5
A whereIn() 0 27 4
A whereJsonContains() 0 9 1
A whereJsonLength() 0 13 1
C where() 0 81 14
A addDateBasedWhere() 0 7 1
A whereSub() 0 25 3
1
<?php
2
3
declare(strict_types=1);
4
5
namespace LaravelFreelancerNL\Aranguent\Query\Concerns;
6
7
use Carbon\CarbonPeriod;
8
use Closure;
9
use DateTimeInterface;
10
use Illuminate\Contracts\Support\Arrayable;
11
use Illuminate\Database\Query\Builder as IlluminateQueryBuilder;
12
use Illuminate\Database\Query\Expression;
13
use Illuminate\Support\Arr;
14
use LaravelFreelancerNL\Aranguent\Query\Builder;
15
16
trait BuildsWhereClauses
17
{
18
    /**
19
     * Add a date based (year, month, day, time) statement to the query.
20
     *
21
     * @param  string  $type
22 77
     * @param  string  $column
23
     * @param  string  $operator
24
     * @param  mixed  $value
25
     * @param  string  $boolean
26
     * @return IlluminateQueryBuilder
27 77
     */
28 20
    protected function addDateBasedWhere($type, $column, $operator, $value, $boolean = 'and')
29
    {
30
        $value = $this->bindValue($value);
0 ignored issues
show
Bug introduced by
It seems like bindValue() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

30
        /** @scrutinizer ignore-call */ 
31
        $value = $this->bindValue($value);
Loading history...
31
32
        $this->wheres[] = compact('column', 'type', 'boolean', 'operator', 'value');
0 ignored issues
show
Bug Best Practice introduced by
The property wheres does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
33
34 77
        return $this;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this returns the type LaravelFreelancerNL\Aran...erns\BuildsWhereClauses which is incompatible with the documented return type Illuminate\Database\Query\Builder.
Loading history...
35 77
    }
36
37 77
    /**
38
     * Add an exists clause to the query.
39
     *
40
     * @SuppressWarnings(PHPMD.BooleanArgumentFlag)
41
     *
42
     * @param  Builder  $query
43 77
     * @param  string  $boolean
44 1
     * @param  bool  $not
45
     * @return Builder
46
     */
47
    public function addWhereExistsQuery(IlluminateQueryBuilder $query, $boolean = 'and', $not = false)
48
    {
49
        if ($not) {
50 77
            return $this->addWhereNotExistsQuery($query, $boolean = 'and');
51 1
        }
52 1
53
        $type = 'Exists';
54
        $operator = '>';
55
        $value = 0;
56
57
        $query->grammar->compileSelect($query);
58 77
59 1
        if ($query->limit != 1) {
60
            $query->aqb = $query->aqb->length($query->aqb);
61
        }
62
63
        if (isset($query->limit) && $query->limit == 1) {
64
            $query->aqb = $query->aqb->first($query->aqb);
65 77
            $operator = '!=';
66 1
            $value = null;
67
        }
68
69
        $this->wheres[] = compact('type', 'query', 'operator', 'value', 'boolean');
0 ignored issues
show
Bug Best Practice introduced by
The property wheres does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
70
71
        return $this;
72 77
    }
73 17
74
    /**
75
     * Add an not exists clause to the query.
76 77
     *
77
     * @param  Builder  $query
78
     * @param  string  $boolean
79
     * @return Builder
80
     */
81 77
    public function addWhereNotExistsQuery(IlluminateQueryBuilder $query, $boolean = 'and')
82 77
    {
83 77
        $type = 'Exists';
84 77
        $operator = '==';
85 77
        $value = 0;
86 77
87
        $query->grammar->compileSelect($query);
88
89 77
        if ($query->limit != 1) {
90
            $query->aqb = $query->aqb->length($query->aqb);
91
        }
92
93
        if (isset($query->limit) && $query->limit == 1) {
94
            $query->aqb = $query->aqb->first($query->aqb);
95
            $value = null;
96
        }
97
98
        $this->wheres[] = compact('type', 'query', 'operator', 'value', 'boolean');
0 ignored issues
show
Bug Best Practice introduced by
The property wheres does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
99
100
        return $this;
101
    }
102 7
103
104 7
    /**
105 3
     * Add a basic where clause to the query.
106
     *
107
     * @param  \Closure|string|array  $column
108 4
     * @param  mixed  $operator
109 4
     * @param  mixed  $value
110 4
     * @param  string  $boolean
111
     * @return IlluminateQueryBuilder
112 4
     */
113
    public function where($column, $operator = null, $value = null, $boolean = 'and')
114 4
    {
115 3
        // If the column is an array, we will assume it is an array of key-value pairs
116
        // and can add them each as a where clause. We will maintain the boolean we
117
        // received when the method was called and pass it into the nested where.
118 4
        if (is_array($column)) {
119 1
            return $this->addArrayOfWheres($column, $boolean);
0 ignored issues
show
Bug introduced by
It seems like addArrayOfWheres() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

119
            return $this->/** @scrutinizer ignore-call */ addArrayOfWheres($column, $boolean);
Loading history...
120 1
        }
121 1
122
        // Here we will make some assumptions about the operator. If only 2 values are
123
        // passed to the method, we will assume that the operator is an equals sign
124 4
        // and keep going. Otherwise, we'll require the operator to be passed in.
125
        [$value, $operator] = $this->prepareValueAndOperator(
0 ignored issues
show
Bug introduced by
It seems like prepareValueAndOperator() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

125
        /** @scrutinizer ignore-call */ 
126
        [$value, $operator] = $this->prepareValueAndOperator(
Loading history...
126 4
            $value, $operator, func_num_args() === 2
127
        );
128
129
        // If the column is actually a Closure instance, we will assume the developer
130
        // wants to begin a nested where statement which is wrapped in parentheses.
131
        // We will add that Closure to the query and return back out immediately.
132
        if ($column instanceof Closure && is_null($operator)) {
133
            return $this->whereNested($column, $boolean);
0 ignored issues
show
Bug introduced by
The method whereNested() does not exist on LaravelFreelancerNL\Aran...erns\BuildsWhereClauses. Did you maybe mean where()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

133
            return $this->/** @scrutinizer ignore-call */ whereNested($column, $boolean);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
134
        }
135
136 3
        // If the column is a Closure instance and there is an operator value, we will
137
        // assume the developer wants to run a subquery and then compare the result
138 3
        // of that subquery with the given value that was provided to the method.
139 3
        if ($this->isQueryable($column) && ! is_null($operator)) {
0 ignored issues
show
Bug introduced by
It seems like isQueryable() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

139
        if ($this->/** @scrutinizer ignore-call */ isQueryable($column) && ! is_null($operator)) {
Loading history...
140 3
            [$sub, $bindings] = $this->createSub($column);
0 ignored issues
show
Bug introduced by
It seems like createSub() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

140
            /** @scrutinizer ignore-call */ 
141
            [$sub, $bindings] = $this->createSub($column);
Loading history...
141
142 3
            return $this->addBinding($bindings, 'where')
0 ignored issues
show
Bug introduced by
It seems like addBinding() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

142
            return $this->/** @scrutinizer ignore-call */ addBinding($bindings, 'where')
Loading history...
143
                ->where(new Expression('('.$sub.')'), $operator, $value, $boolean);
144 3
        }
145 2
146
        // If the given operator is not found in the list of valid operators we will
147
        // assume that the developer is just short-cutting the '=' operators and
148 3
        // we will set the operators to '=' and set the values appropriately.
149 1
        if ($this->invalidOperator($operator)) {
0 ignored issues
show
Bug introduced by
It seems like invalidOperator() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

149
        if ($this->/** @scrutinizer ignore-call */ invalidOperator($operator)) {
Loading history...
150 1
            [$value, $operator] = [$operator, '=='];
151
        }
152
153 3
        // If the value is a Closure, it means the developer is performing an entire
154
        // sub-select within the query and we will need to compile the sub-select
155 3
        // within the where clause to get the appropriate query record results.
156
        if ($value instanceof Closure) {
157
            return $this->whereSub($column, $operator, $value, $boolean);
0 ignored issues
show
Bug introduced by
It seems like $column can also be of type Closure; however, parameter $column of LaravelFreelancerNL\Aran...hereClauses::whereSub() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

157
            return $this->whereSub(/** @scrutinizer ignore-type */ $column, $operator, $value, $boolean);
Loading history...
158
        }
159
160
        // If the value is "null", we will just assume the developer wants to add a
161
        // where null clause to the query. So, we will allow a short-cut here to
162
        // that method for convenience so the developer doesn't have to check.
163
        if (is_null($value)) {
164
            return $this->whereNull($column, $boolean, $operator !== '==');
0 ignored issues
show
Bug introduced by
The method whereNull() does not exist on LaravelFreelancerNL\Aran...erns\BuildsWhereClauses. Did you maybe mean where()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

164
            return $this->/** @scrutinizer ignore-call */ whereNull($column, $boolean, $operator !== '==');

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
165
        }
166
167
        $type = 'Basic';
168
169 1
        // If the column is making a JSON reference we'll check to see if the value
170
        // is a boolean. If it is, we'll add the raw boolean string as an actual
171 1
        // value to the query to ensure this is properly handled by the query.
172
        if (str_contains($column, '->') && is_bool($value)) {
0 ignored issues
show
Bug introduced by
It seems like $column can also be of type Closure; however, parameter $haystack of str_contains() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

172
        if (str_contains(/** @scrutinizer ignore-type */ $column, '->') && is_bool($value)) {
Loading history...
173 1
            $value = new Expression($value ? 'true' : 'false');
174
175 1
            if (is_string($column)) {
176
                $type = 'JsonBoolean';
177
            }
178
        }
179
180
        if ($this->isBitwiseOperator($operator)) {
0 ignored issues
show
Bug introduced by
It seems like isBitwiseOperator() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

180
        if ($this->/** @scrutinizer ignore-call */ isBitwiseOperator($operator)) {
Loading history...
181
            $type = 'Bitwise';
182
        }
183
184
        $value = $this->bindValue($value);
185
186
        // Now that we are working with just a simple query we can put the elements
187 1
        // in our array and add the query binding to our array of bindings that
188
        // will be bound to each SQL statements when it is finally executed.
189 1
        $this->wheres[] = compact(
0 ignored issues
show
Bug Best Practice introduced by
The property wheres does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
190
            'type', 'column', 'operator', 'value', 'boolean'
191
        );
192
193
        return $this;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this returns the type LaravelFreelancerNL\Aran...erns\BuildsWhereClauses which is incompatible with the documented return type Illuminate\Database\Query\Builder.
Loading history...
194 1
    }
195
196 1
    /**
197
     * Add a where between statement to the query.
198 1
     *
199
     * @param  string|Expression $column
200 1
     * @param  iterable  $values
201
     * @param  string  $boolean
202
     * @param  bool  $not
203 1
     * @return IlluminateQueryBuilder
204 1
     */
205 1
    public function whereBetween($column, iterable $values, $boolean = 'and', $not = false)
206 1
    {
207 1
        $type = 'between';
208 1
209
        if ($values instanceof CarbonPeriod) {
210
            $values = $values->toArray();
211 1
        }
212
213
        $values = array_slice($this->cleanBindings(Arr::flatten($values)), 0, 2);
0 ignored issues
show
Bug introduced by
It seems like cleanBindings() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

213
        $values = array_slice($this->/** @scrutinizer ignore-call */ cleanBindings(Arr::flatten($values)), 0, 2);
Loading history...
214
        $values[0] = $this->bindValue($values[0]);
215
        $values[1] = $this->bindValue($values[1]);
216
217
        $this->wheres[] = compact('type', 'column', 'values', 'boolean', 'not');
0 ignored issues
show
Bug Best Practice introduced by
The property wheres does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
218
219
        return $this;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this returns the type LaravelFreelancerNL\Aran...erns\BuildsWhereClauses which is incompatible with the documented return type Illuminate\Database\Query\Builder.
Loading history...
220
    }
221
222
    /**
223
     * Add a "where in" clause to the query.
224
     *
225
     * @param  string  $column
226
     * @param  mixed  $values
227
     * @param  string  $boolean
228
     * @param  bool  $not
229
     * @return IlluminateQueryBuilder
230
     */
231
    public function whereIn($column, $values, $boolean = 'and', $not = false)
232
    {
233
        $type = $not ? 'NotIn' : 'In';
234
235
        // If the value is a query builder instance we will assume the developer wants to
236
        // look for any values that exist within this given query. So, we will add the
237
        // query accordingly so that this query is properly executed when it is run.
238
        if ($this->isQueryable($values)) {
239
            [$query, $bindings] = $this->createSub($values);
240
241
            $values = [new Expression($query)];
242
243
            $this->addBinding($bindings, 'where');
244
        }
245
246
        // Next, if the value is Arrayable we need to cast it to its raw array form so we
247
        // have the underlying array value instead of an Arrayable object which is not
248
        // able to be added as a binding, etc. We will then add to the wheres array.
249
        if ($values instanceof Arrayable) {
250
            $values = $values->toArray();
251
        }
252
253
        $values = $this->bindValue($this->cleanBindings($values));
254
255
        $this->wheres[] = compact('type', 'column', 'values', 'boolean');
0 ignored issues
show
Bug Best Practice introduced by
The property wheres does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
256
257
        return $this;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this returns the type LaravelFreelancerNL\Aran...erns\BuildsWhereClauses which is incompatible with the documented return type Illuminate\Database\Query\Builder.
Loading history...
258
    }
259
260
261
    /**
262
     * Add a "where JSON contains" clause to the query.
263
     *
264
     * @SuppressWarnings(PHPMD.BooleanArgumentFlag)
265
     *
266
     * @param  string  $column
267
     * @param  mixed  $value
268
     * @param  string  $boolean
269
     * @param  bool  $not
270
     * @return IlluminateQueryBuilder
271
     */
272
    public function whereJsonContains($column, $value, $boolean = 'and', $not = false)
273
    {
274
        $type = 'JsonContains';
275
276
        $value = $this->bindValue($value);
277
278
        $this->wheres[] = compact('type', 'column', 'value', 'boolean', 'not');
0 ignored issues
show
Bug Best Practice introduced by
The property wheres does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
279
280
        return $this;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this returns the type LaravelFreelancerNL\Aran...erns\BuildsWhereClauses which is incompatible with the documented return type Illuminate\Database\Query\Builder.
Loading history...
281
    }
282
283
    /**
284
     * Add a "where JSON length" clause to the query.
285
     *
286
     * @param  string  $column
287
     * @param  mixed  $operator
288
     * @param  mixed  $value
289
     * @param  string  $boolean
290
     * @return IlluminateQueryBuilder
291
     */
292
    public function whereJsonLength($column, $operator, $value = null, $boolean = 'and')
293
    {
294
        $type = 'JsonLength';
295
296
        [$value, $operator] = $this->prepareValueAndOperator(
297
            $value, $operator, func_num_args() === 2
298
        );
299
300
        $value = $this->bindValue((int) $this->flattenValue($value));
0 ignored issues
show
Bug introduced by
It seems like flattenValue() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

300
        $value = $this->bindValue((int) $this->/** @scrutinizer ignore-call */ flattenValue($value));
Loading history...
301
302
        $this->wheres[] = compact('type', 'column', 'operator', 'value', 'boolean');
0 ignored issues
show
Bug Best Practice introduced by
The property wheres does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
303
304
        return $this;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this returns the type LaravelFreelancerNL\Aran...erns\BuildsWhereClauses which is incompatible with the documented return type Illuminate\Database\Query\Builder.
Loading history...
305
    }
306
307
308
    /**
309
     * Add a full sub-select to the query.
310
     *
311
     * @param  string  $column
312
     * @param  string  $operator
313
     * @param  \Closure  $callback
314
     * @param  string  $boolean
315
     * @return Builder
316
     */
317
    protected function whereSub($column, $operator, Closure $callback, $boolean)
318
    {
319
        $type = 'Sub';
320
321
        // Once we have the query instance we can simply execute it so it can add all
322
        // of the sub-select's conditions to itself, and then we can cache it off
323
        // in the array of where clauses for the "main" parent query instance.
324
        call_user_func($callback, $query = $this->forSubQuery());
0 ignored issues
show
Bug introduced by
It seems like forSubQuery() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

324
        call_user_func($callback, $query = $this->/** @scrutinizer ignore-call */ forSubQuery());
Loading history...
325
326
        $query->grammar->compileSelect($query);
327
328
        if (isset($query->limit) && $query->limit == 1) {
329
            //Return the value, not an array of values
330
            $query->aqb = $query->aqb->first($query->aqb);
331
        }
332
333
        $this->wheres[] = compact(
0 ignored issues
show
Bug Best Practice introduced by
The property wheres does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
334
            'type',
335
            'column',
336
            'operator',
337
            'query',
338
            'boolean'
339
        );
340
341
        return $this;
342
    }
343
}
344