Issues (121)

src/Database/Eloquent/BuilderMixin.php (21 issues)

1
<?php
2
3
namespace BiiiiiigMonster\Hasin\Database\Eloquent;
4
5
use Closure;
6
use Illuminate\Database\Eloquent\Builder;
7
use Illuminate\Database\Eloquent\Relations\MorphTo;
8
use Illuminate\Database\Eloquent\Relations\Relation;
9
use Illuminate\Support\Str;
10
11
class BuilderMixin
12
{
13
    /**
14
     * Add a relationship count / whereIn condition to the query.
15
     *
16
     * @return Closure
17
     */
18
    public function hasIn(): Closure
19
    {
20
        return function ($relation, $operator = '>=', $count = 1, $boolean = 'and', Closure $callback = null): Builder {
21
            /** @var Builder $this */
22
            if (is_string($relation)) {
23
                if (str_contains($relation, '.')) {
24
                    return $this->hasInNested($relation, $operator, $count, $boolean, $callback);
0 ignored issues
show
The method hasInNested() does not exist on Illuminate\Database\Eloquent\Builder. Did you maybe mean hasIn()? ( Ignorable by Annotation )

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

24
                    return $this->/** @scrutinizer ignore-call */ hasInNested($relation, $operator, $count, $boolean, $callback);

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...
25
                }
26
27
                $relation = $this->getRelationWithoutConstraints($relation);
0 ignored issues
show
The method getRelationWithoutConstraints() does not exist on Illuminate\Database\Eloquent\Builder. ( Ignorable by Annotation )

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

27
                /** @scrutinizer ignore-call */ 
28
                $relation = $this->getRelationWithoutConstraints($relation);

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...
28
            }
29
30
            if ($relation instanceof MorphTo) {
31
                return $this->hasMorphIn($relation, ['*'], $operator, $count, $boolean, $callback);
32
            }
33
34
            // If we only need to check for the existence of the relation, then we can optimize
35
            // the subquery to only run a "where in" clause instead of this full "count"
36
            // clause. This will make these queries run much faster compared with a count.
37
            $method = $this->canUseExistsForExistenceCheck($operator, $count)
0 ignored issues
show
The method canUseExistsForExistenceCheck() does not exist on Illuminate\Database\Eloquent\Builder. ( Ignorable by Annotation )

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

37
            $method = $this->/** @scrutinizer ignore-call */ canUseExistsForExistenceCheck($operator, $count)

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...
38
                            ? 'getRelationExistenceInQuery'
39
                            : 'getRelationExistenceCountQuery';
40
41
            $hasInQuery = $relation->{$method}(
42
                $relation->getRelated()->newQueryWithoutRelationships(),
43
                $this
44
            );
45
46
            // Next we will call any given callback as an "anonymous" scope so they can get the
47
            // proper logical grouping of the where clauses if needed by this Eloquent query
48
            // builder. Then, we will be ready to finalize and return this query instance.
49
            if ($callback) {
50
                $hasInQuery->callScope($callback);
51
            }
52
53
            return $this->addHasInWhere(
0 ignored issues
show
The method addHasInWhere() does not exist on Illuminate\Database\Eloquent\Builder. ( Ignorable by Annotation )

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

53
            return $this->/** @scrutinizer ignore-call */ addHasInWhere(

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...
54
                $hasInQuery,
55
                $relation,
56
                $operator,
57
                $count,
58
                $boolean
59
            );
60
        };
61
    }
62
63
    /**
64
     * Add nested relationship count / whereIn conditions to the query.
65
     *
66
     * Sets up recursive call to whereHas until we finish the nested relation.
67
     *
68
     * @return Closure
69
     */
70
    protected function hasInNested(): Closure
71
    {
72
        return function ($relations, $operator = '>=', $count = 1, $boolean = 'and', $callback = null): Builder {
73
            /** @var Builder $this */
74
            $relations = explode('.', $relations);
75
76
            $doesntHave = $operator === '<' && $count === 1;
77
78
            if ($doesntHave) {
79
                $operator = '>=';
80
                $count = 1;
81
            }
82
83
            $closure = function ($q) use (&$closure, &$relations, $operator, $count, $callback) {
84
                // In order to nest "hasIn", we need to add count relation constraints on the
85
                // callback Closure. We'll do this by simply passing the Closure its own
86
                // reference to itself so it calls itself recursively on each segment.
87
                count($relations) > 1
88
                    ? $q->whereHasIn(array_shift($relations), $closure)
89
                    : $q->hasIn(array_shift($relations), $operator, $count, 'and', $callback);
90
            };
91
92
            return $this->hasIn(array_shift($relations), $doesntHave ? '<' : '>=', 1, $boolean, $closure);
93
        };
94
    }
95
96
    /**
97
     * Add a relationship count / whereIn condition to the query with an "or".
98
     *
99
     * @return Closure
100
     */
101
    public function orHasIn(): Closure
102
    {
103
        return function ($relation, $operator = '>=', $count = 1): Builder {
104
            /** @var Builder $this */
105
            return $this->hasIn($relation, $operator, $count, 'or');
106
        };
107
    }
108
109
    /**
110
     * Add a relationship count / whereIn condition to the query.
111
     *
112
     * @return Closure
113
     */
114
    public function doesntHaveIn(): Closure
115
    {
116
        return function ($relation, $boolean = 'and', Closure $callback = null): Builder {
117
            /** @var Builder $this */
118
            return $this->hasIn($relation, '<', 1, $boolean, $callback);
119
        };
120
    }
121
122
    /**
123
     * Add a relationship count / whereIn condition to the query with an "or".
124
     *
125
     * @return Closure
126
     */
127
    public function orDoesntHaveIn(): Closure
128
    {
129
        return function ($relation): Builder {
130
            /** @var Builder $this */
131
            return $this->doesntHaveIn($relation, 'or');
132
        };
133
    }
134
135
    /**
136
     * Add a relationship count / whereIn condition to the query with where clauses.
137
     *
138
     * @return Closure
139
     */
140
    public function whereHasIn(): Closure
141
    {
142
        return function ($relation, Closure $callback = null, $operator = '>=', $count = 1): Builder {
143
            /** @var Builder $this */
144
            return $this->hasIn($relation, $operator, $count, 'and', $callback);
145
        };
146
    }
147
148
    /**
149
     * Add a relationship count / exists condition to the query with whereIn clauses.
150
     *
151
     * Also load the relationship with same condition.
152
     *
153
     * @return Closure
154
     */
155
    public function withWhereHasIn(): Closure
156
    {
157
        return function ($relation, Closure $callback = null, $operator = '>=', $count = 1): Builder {
158
            /** @var Builder $this */
159
            return $this->whereHasIn(Str::before($relation, ':'), $callback, $operator, $count)
160
                ->with($callback ? [$relation => fn ($query) => $callback($query)] : $relation);
0 ignored issues
show
The method with() does not exist on Illuminate\Database\Eloquent\Builder. ( Ignorable by Annotation )

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

160
                ->/** @scrutinizer ignore-call */ with($callback ? [$relation => fn ($query) => $callback($query)] : $relation);

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...
161
        };
162
    }
163
164
    /**
165
     * Add a relationship count / whereIn condition to the query with where clauses and an "or".
166
     *
167
     * @return Closure
168
     */
169
    public function orWhereHasIn(): Closure
170
    {
171
        return function ($relation, Closure $callback = null, $operator = '>=', $count = 1): Builder {
172
            /** @var Builder $this */
173
            return $this->hasIn($relation, $operator, $count, 'or', $callback);
174
        };
175
    }
176
177
    /**
178
     * Add a relationship count / whereIn condition to the query with where clauses.
179
     *
180
     * @return Closure
181
     */
182
    public function whereDoesntHaveIn(): Closure
183
    {
184
        return function ($relation, Closure $callback = null): Builder {
185
            /** @var Builder $this */
186
            return $this->doesntHaveIn($relation, 'and', $callback);
187
        };
188
    }
189
190
    /**
191
     * Add a relationship count / whereIn condition to the query with where clauses and an "or".
192
     *
193
     * @return Closure
194
     */
195
    public function orWhereDoesntHaveIn(): Closure
196
    {
197
        return function ($relation, Closure $callback = null): Builder {
198
            /** @var Builder $this */
199
            return $this->doesntHaveIn($relation, 'or', $callback);
200
        };
201
    }
202
203
    /**
204
     * Add a polymorphic relationship count / whereIn condition to the query.
205
     *
206
     * @return Closure
207
     */
208
    public function hasMorphIn(): Closure
209
    {
210
        return function ($relation, $types, $operator = '>=', $count = 1, $boolean = 'and', Closure $callback = null): Builder {
211
            /** @var Builder $this */
212
            if (is_string($relation)) {
213
                $relation = $this->getRelationWithoutConstraints($relation);
214
            }
215
216
            $types = (array) $types;
217
218
            if ($types === ['*']) {
219
                $types = $this->model->newModelQuery()->distinct()->pluck($relation->getMorphType())->filter()->all();
0 ignored issues
show
The property model does not seem to exist on Illuminate\Database\Eloquent\Builder.
Loading history...
220
            }
221
222
            foreach ($types as &$type) {
223
                $type = Relation::getMorphedModel($type) ?? $type;
224
            }
225
226
            return $this->where(function ($query) use ($relation, $callback, $operator, $count, $types) {
0 ignored issues
show
The method where() does not exist on Illuminate\Database\Eloquent\Builder. Did you maybe mean whereHasIn()? ( Ignorable by Annotation )

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

226
            return $this->/** @scrutinizer ignore-call */ where(function ($query) use ($relation, $callback, $operator, $count, $types) {

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...
227
                foreach ($types as $type) {
228
                    $query->orWhere(function ($query) use ($relation, $callback, $operator, $count, $type) {
229
                        $belongsTo = $this->getBelongsToRelation($relation, $type);
0 ignored issues
show
The method getBelongsToRelation() does not exist on BiiiiiigMonster\Hasin\Da...e\Eloquent\BuilderMixin. ( Ignorable by Annotation )

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

229
                        /** @scrutinizer ignore-call */ 
230
                        $belongsTo = $this->getBelongsToRelation($relation, $type);

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...
230
231
                        if ($callback) {
232
                            $callback = function ($query) use ($callback, $type) {
233
                                return $callback($query, $type);
234
                            };
235
                        }
236
237
                        $query->where($this->qualifyColumn($relation->getMorphType()), '=', (new $type())->getMorphClass())
0 ignored issues
show
The method qualifyColumn() does not exist on BiiiiiigMonster\Hasin\Da...e\Eloquent\BuilderMixin. ( Ignorable by Annotation )

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

237
                        $query->where($this->/** @scrutinizer ignore-call */ qualifyColumn($relation->getMorphType()), '=', (new $type())->getMorphClass())

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...
238
                            ->whereHasIn($belongsTo, $callback, $operator, $count);
239
                    });
240
                }
241
            }, null, null, $boolean);
242
        };
243
    }
244
245
    /**
246
     * Add a polymorphic relationship count / whereIn condition to the query with an "or".
247
     *
248
     * @return Closure
249
     */
250
    public function orHasMorphIn(): Closure
251
    {
252
        return function ($relation, $types, $operator = '>=', $count = 1): Builder {
253
            /** @var Builder $this */
254
            return $this->hasMorphIn($relation, $types, $operator, $count, 'or');
255
        };
256
    }
257
258
    /**
259
     * Add a polymorphic relationship count / whereIn condition to the query.
260
     *
261
     * @return Closure
262
     */
263
    public function doesntHaveMorphIn(): Closure
264
    {
265
        return function ($relation, $types, $boolean = 'and', Closure $callback = null): Builder {
266
            /** @var Builder $this */
267
            return $this->hasMorphIn($relation, $types, '<', 1, $boolean, $callback);
268
        };
269
    }
270
271
    /**
272
     * Add a polymorphic relationship count / whereIn condition to the query with an "or".
273
     *
274
     * @return Closure
275
     */
276
    public function orDoesntHaveMorphIn(): Closure
277
    {
278
        return function ($relation, $types): Builder {
279
            /** @var Builder $this */
280
            return $this->doesntHaveMorphIn($relation, $types, 'or');
281
        };
282
    }
283
284
    /**
285
     * Add a polymorphic relationship count / whereIn condition to the query with where clauses.
286
     *
287
     * @return Closure
288
     */
289
    public function whereHasMorphIn(): Closure
290
    {
291
        return function ($relation, $types, Closure $callback = null, $operator = '>=', $count = 1): Builder {
292
            /** @var Builder $this */
293
            return $this->hasMorphIn($relation, $types, $operator, $count, 'and', $callback);
294
        };
295
    }
296
297
    /**
298
     * Add a polymorphic relationship count / whereIn condition to the query with where clauses and an "or".
299
     *
300
     * @return Closure
301
     */
302
    public function orWhereHasMorphIn(): Closure
303
    {
304
        return function ($relation, $types, Closure $callback = null, $operator = '>=', $count = 1): Builder {
305
            /** @var Builder $this */
306
            return $this->hasMorphIn($relation, $types, $operator, $count, 'or', $callback);
307
        };
308
    }
309
310
    /**
311
     * Add a polymorphic relationship count / whereIn condition to the query with where clauses.
312
     *
313
     * @return Closure
314
     */
315
    public function whereDoesntHaveMorphIn(): Closure
316
    {
317
        return function ($relation, $types, Closure $callback = null): Builder {
318
            /** @var Builder $this */
319
            return $this->doesntHaveMorphIn($relation, $types, 'and', $callback);
320
        };
321
    }
322
323
    /**
324
     * Add a polymorphic relationship count / whereIn condition to the query with where clauses and an "or".
325
     *
326
     * @return Closure
327
     */
328
    public function orWhereDoesntHaveMorphIn(): Closure
329
    {
330
        return function ($relation, $types, Closure $callback = null): Builder {
331
            /** @var Builder $this */
332
            return $this->doesntHaveMorphIn($relation, $types, 'or', $callback);
333
        };
334
    }
335
336
    /**
337
     * Add a basic where clause to a relationship query.
338
     *
339
     * @return Closure
340
     */
341
    public function whereRelationIn(): Closure
342
    {
343
        return function ($relation, $column, $operator = null, $value = null): Builder {
344
            return $this->whereHasIn($relation, function ($query) use ($column, $operator, $value) {
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->whereHasIn...ion(...) { /* ... */ }) returns the type callable which is incompatible with the type-hinted return Illuminate\Database\Eloquent\Builder.
Loading history...
The call to BiiiiiigMonster\Hasin\Da...lderMixin::whereHasIn() has too many arguments starting with $relation. ( Ignorable by Annotation )

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

344
            return $this->/** @scrutinizer ignore-call */ whereHasIn($relation, function ($query) use ($column, $operator, $value) {

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
345
                if ($column instanceof Closure) {
346
                    $column($query);
347
                } else {
348
                    $query->where($column, $operator, $value);
349
                }
350
            });
351
        };
352
    }
353
354
    /**
355
     * Add an "or where" clause to a relationship query.
356
     *
357
     * @return Closure
358
     */
359
    public function orWhereRelationIn(): Closure
360
    {
361
        return function ($relation, $column, $operator = null, $value = null): Builder {
362
            return $this->orWhereHasIn($relation, function ($query) use ($column, $operator, $value) {
0 ignored issues
show
The call to BiiiiiigMonster\Hasin\Da...erMixin::orWhereHasIn() has too many arguments starting with $relation. ( Ignorable by Annotation )

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

362
            return $this->/** @scrutinizer ignore-call */ orWhereHasIn($relation, function ($query) use ($column, $operator, $value) {

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
Bug Best Practice introduced by
The expression return $this->orWhereHas...ion(...) { /* ... */ }) returns the type callable which is incompatible with the type-hinted return Illuminate\Database\Eloquent\Builder.
Loading history...
363
                if ($column instanceof Closure) {
364
                    $column($query);
365
                } else {
366
                    $query->where($column, $operator, $value);
367
                }
368
            });
369
        };
370
    }
371
372
    /**
373
     * Add a polymorphic relationship condition to the query with a where clause.
374
     *
375
     * @return Closure
376
     */
377
    public function whereMorphRelationIn(): Closure
378
    {
379
        return function ($relation, $types, $column, $operator = null, $value = null): Builder {
380
            return $this->whereHasMorphIn($relation, $types, function ($query) use ($column, $operator, $value) {
0 ignored issues
show
The call to BiiiiiigMonster\Hasin\Da...ixin::whereHasMorphIn() has too many arguments starting with $relation. ( Ignorable by Annotation )

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

380
            return $this->/** @scrutinizer ignore-call */ whereHasMorphIn($relation, $types, function ($query) use ($column, $operator, $value) {

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
Bug Best Practice introduced by
The expression return $this->whereHasMo...ion(...) { /* ... */ }) returns the type callable which is incompatible with the type-hinted return Illuminate\Database\Eloquent\Builder.
Loading history...
381
                $query->where($column, $operator, $value);
382
            });
383
        };
384
    }
385
386
    /**
387
     * Add a polymorphic relationship condition to the query with an "or where" clause.
388
     *
389
     * @return Closure
390
     */
391
    public function orWhereMorphRelationIn(): Closure
392
    {
393
        return function ($relation, $types, $column, $operator = null, $value = null): Builder {
394
            return $this->orWhereHasMorphIn($relation, $types, function ($query) use ($column, $operator, $value) {
0 ignored issues
show
The call to BiiiiiigMonster\Hasin\Da...in::orWhereHasMorphIn() has too many arguments starting with $relation. ( Ignorable by Annotation )

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

394
            return $this->/** @scrutinizer ignore-call */ orWhereHasMorphIn($relation, $types, function ($query) use ($column, $operator, $value) {

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
Bug Best Practice introduced by
The expression return $this->orWhereHas...ion(...) { /* ... */ }) returns the type callable which is incompatible with the type-hinted return Illuminate\Database\Eloquent\Builder.
Loading history...
395
                $query->where($column, $operator, $value);
396
            });
397
        };
398
    }
399
400
    /**
401
     * Add the "hasin" condition whereIn clause to the query.
402
     *
403
     * @return Closure
404
     */
405
    protected function addHasInWhere(): Closure
406
    {
407
        return function (Builder $hasInQuery, Relation $relation, $operator, $count, $boolean): Builder {
408
            /** @var Builder $this */
409
            $hasInQuery->mergeConstraintsFrom($relation->getQuery());
0 ignored issues
show
The method mergeConstraintsFrom() does not exist on Illuminate\Database\Eloquent\Builder. ( Ignorable by Annotation )

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

409
            $hasInQuery->/** @scrutinizer ignore-call */ 
410
                         mergeConstraintsFrom($relation->getQuery());

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...
410
411
            return $this->canUseExistsForExistenceCheck($operator, $count)
412
                ? $this->whereIn($relation->getRelationWhereInKey(), $hasInQuery->toBase(), $boolean, $operator === '<' && $count === 1)
0 ignored issues
show
The method whereIn() does not exist on Illuminate\Database\Eloquent\Builder. Did you maybe mean whereHasIn()? ( Ignorable by Annotation )

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

412
                ? $this->/** @scrutinizer ignore-call */ whereIn($relation->getRelationWhereInKey(), $hasInQuery->toBase(), $boolean, $operator === '<' && $count === 1)

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...
The method toBase() does not exist on Illuminate\Database\Eloquent\Builder. ( Ignorable by Annotation )

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

412
                ? $this->whereIn($relation->getRelationWhereInKey(), $hasInQuery->/** @scrutinizer ignore-call */ toBase(), $boolean, $operator === '<' && $count === 1)

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...
413
                : $this->addWhereCountQuery($hasInQuery->toBase(), $operator, $count, $boolean);
0 ignored issues
show
The method addWhereCountQuery() does not exist on Illuminate\Database\Eloquent\Builder. ( Ignorable by Annotation )

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

413
                : $this->/** @scrutinizer ignore-call */ addWhereCountQuery($hasInQuery->toBase(), $operator, $count, $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...
414
        };
415
    }
416
}
417