Completed
Pull Request — master (#841)
by
unknown
02:09
created

QueryBuilderEngine::getEagerLoads()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 8
c 0
b 0
f 0
cc 2
eloc 4
nc 2
nop 0
rs 9.4285
1
<?php
2
3
namespace Yajra\Datatables\Engines;
4
5
use Closure;
6
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
7
use Illuminate\Database\Eloquent\Relations\HasOneOrMany;
8
use Illuminate\Database\Eloquent\Relations\MorphToMany;
9
use Illuminate\Database\Query\Builder;
10
use Illuminate\Support\Str;
11
use Yajra\Datatables\Helper;
12
use Yajra\Datatables\Request;
13
14
/**
15
 * Class QueryBuilderEngine.
16
 *
17
 * @package Yajra\Datatables\Engines
18
 * @author  Arjay Angeles <[email protected]>
19
 */
20
class QueryBuilderEngine extends BaseEngine
21
{
22
    /**
23
     * @param \Illuminate\Database\Query\Builder $builder
24
     * @param \Yajra\Datatables\Request $request
25
     */
26
    public function __construct(Builder $builder, Request $request)
27
    {
28
        $this->query = $builder;
29
        $this->init($request, $builder);
30
    }
31
32
    /**
33
     * Initialize attributes.
34
     *
35
     * @param  \Yajra\Datatables\Request $request
36
     * @param  \Illuminate\Database\Query\Builder $builder
37
     * @param  string $type
38
     */
39
    protected function init($request, $builder, $type = 'builder')
0 ignored issues
show
Bug introduced by
You have injected the Request via parameter $request. This is generally not recommended as there might be multiple instances during a request cycle (f.e. when using sub-requests). Instead, it is recommended to inject the RequestStack and retrieve the current request each time you need it via getCurrentRequest().
Loading history...
40
    {
41
        $this->request    = $request;
42
        $this->query_type = $type;
43
        $this->columns    = $builder->columns;
44
        $this->connection = $builder->getConnection();
45
        $this->prefix     = $this->connection->getTablePrefix();
46
        $this->database   = $this->connection->getDriverName();
47
        if ($this->isDebugging()) {
48
            $this->connection->enableQueryLog();
49
        }
50
    }
51
52
    /**
53
     * Set auto filter off and run your own filter.
54
     * Overrides global search
55
     *
56
     * @param \Closure $callback
57
     * @param bool $globalSearch
58
     * @return $this
59
     */
60
    public function filter(Closure $callback, $globalSearch = false)
61
    {
62
        $this->overrideGlobalSearch($callback, $this->query, $globalSearch);
63
64
        return $this;
65
    }
66
67
    /**
68
     * Organizes works
69
     *
70
     * @param bool $mDataSupport
71
     * @param bool $orderFirst
72
     * @return \Illuminate\Http\JsonResponse
73
     */
74
    public function make($mDataSupport = false, $orderFirst = false)
75
    {
76
        return parent::make($mDataSupport, $orderFirst);
77
    }
78
79
    /**
80
     * Count total items.
81
     *
82
     * @return integer
83
     */
84
    public function totalCount()
85
    {
86
        return $this->totalRecords ? $this->totalRecords : $this->count();
87
    }
88
89
    /**
90
     * Counts current query.
91
     *
92
     * @return int
93
     */
94
    public function count()
95
    {
96
        $myQuery = clone $this->query;
97
        // if its a normal query ( no union, having and distinct word )
98
        // replace the select with static text to improve performance
99
        if (! Str::contains(Str::lower($myQuery->toSql()), ['union', 'having', 'distinct', 'order by', 'group by'])) {
0 ignored issues
show
Bug introduced by
The method toSql does only exist in Illuminate\Database\Query\Builder, but not in Illuminate\Database\Eloquent\Builder.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
100
            $row_count = $this->wrap('row_count');
101
            $myQuery->select($this->connection->raw("'1' as {$row_count}"));
0 ignored issues
show
Bug introduced by
The method select does only exist in Illuminate\Database\Query\Builder, but not in Illuminate\Database\Eloquent\Builder.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
102
        }
103
104
        // check for select soft deleted records
105
        if (! $this->withTrashed && $this->modelUseSoftDeletes()) {
106
            $myQuery->whereNull($myQuery->getModel()->getTable() . '.deleted_at');
0 ignored issues
show
Bug introduced by
The method getModel does only exist in Illuminate\Database\Eloquent\Builder, but not in Illuminate\Database\Query\Builder.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
Bug introduced by
The method whereNull does only exist in Illuminate\Database\Query\Builder, but not in Illuminate\Database\Eloquent\Builder.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
107
        }
108
109
        return $this->connection->table($this->connection->raw('(' . $myQuery->toSql() . ') count_row_table'))
110
                                ->setBindings($myQuery->getBindings())->count();
0 ignored issues
show
Bug introduced by
The method getBindings does only exist in Illuminate\Database\Query\Builder, but not in Illuminate\Database\Eloquent\Builder.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
111
    }
112
113
    /**
114
     * Wrap column with DB grammar.
115
     *
116
     * @param string $column
117
     * @return string
118
     */
119
    protected function wrap($column)
120
    {
121
        return $this->connection->getQueryGrammar()->wrap($column);
122
    }
123
124
    /**
125
     * Check if model use SoftDeletes trait
126
     *
127
     * @return boolean
128
     */
129
    private function modelUseSoftDeletes()
130
    {
131
        if ($this->query_type == 'eloquent') {
132
            return in_array('Illuminate\Database\Eloquent\SoftDeletes', class_uses($this->query->getModel()));
0 ignored issues
show
Bug introduced by
The method getModel does only exist in Illuminate\Database\Eloquent\Builder, but not in Illuminate\Database\Query\Builder.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
133
        }
134
135
        return false;
136
    }
137
138
    /**
139
     * Perform global search.
140
     *
141
     * @return void
142
     */
143
    public function filtering()
144
    {
145
        $this->query->where(
146
            function ($query) {
147
                $globalKeyword = $this->request->keyword();
148
                $queryBuilder  = $this->getQueryBuilder($query);
149
150
                foreach ($this->request->searchableColumnIndex() as $index) {
151
                    $columnName = $this->getColumnName($index);
152
                    if ($this->isBlacklisted($columnName)) {
153
                        continue;
154
                    }
155
156
                    // check if custom column filtering is applied
157
                    if (isset($this->columnDef['filter'][$columnName])) {
158
                        $columnDef = $this->columnDef['filter'][$columnName];
159
                        // check if global search should be applied for the specific column
160
                        $applyGlobalSearch = count($columnDef['parameters']) == 0 || end($columnDef['parameters']) !== false;
161
                        if (! $applyGlobalSearch) {
162
                            continue;
163
                        }
164
165 View Code Duplication
                        if ($columnDef['method'] instanceof Closure) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
166
                            $whereQuery = $queryBuilder->newQuery();
167
                            call_user_func_array($columnDef['method'], [$whereQuery, $globalKeyword]);
168
                            $queryBuilder->addNestedWhereQuery($whereQuery, 'or');
169
                        } else {
170
                            $this->compileColumnQuery(
171
                                $queryBuilder,
172
                                Helper::getOrMethod($columnDef['method']),
173
                                $columnDef['parameters'],
174
                                $columnName,
175
                                $globalKeyword
176
                            );
177
                        }
178 View Code Duplication
                    } else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
179
                        if (count(explode('.', $columnName)) > 1) {
180
                            $eagerLoads     = $this->getEagerLoads();
181
                            $parts          = explode('.', $columnName);
182
                            $relationColumn = array_pop($parts);
183
                            $relation       = implode('.', $parts);
184
                            if (in_array($relation, $eagerLoads)) {
185
                                $this->compileRelationSearch(
186
                                    $queryBuilder,
187
                                    $relation,
188
                                    $relationColumn,
189
                                    $globalKeyword
190
                                );
191
                            } else {
192
                                $this->compileQuerySearch($queryBuilder, $columnName, $globalKeyword);
193
                            }
194
                        } else {
195
                            $this->compileQuerySearch($queryBuilder, $columnName, $globalKeyword);
196
                        }
197
                    }
198
199
                    $this->isFilterApplied = true;
200
                }
201
            }
202
        );
203
    }
204
205
    /**
206
     * Perform filter column on selected field.
207
     *
208
     * @param mixed $query
209
     * @param string|Closure $method
210
     * @param mixed $parameters
211
     * @param string $column
212
     * @param string $keyword
213
     */
214
    protected function compileColumnQuery($query, $method, $parameters, $column, $keyword)
215
    {
216
        if (method_exists($query, $method)
217
            && count($parameters) <= with(new \ReflectionMethod($query, $method))->getNumberOfParameters()
218
        ) {
219
            if (Str::contains(Str::lower($method), 'raw')
0 ignored issues
show
Bug introduced by
It seems like $method defined by parameter $method on line 214 can also be of type object<Closure>; however, Illuminate\Support\Str::lower() does only seem to accept string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
220
                || Str::contains(Str::lower($method), 'exists')
0 ignored issues
show
Bug introduced by
It seems like $method defined by parameter $method on line 214 can also be of type object<Closure>; however, Illuminate\Support\Str::lower() does only seem to accept string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
221
            ) {
222
                call_user_func_array(
223
                    [$query, $method],
224
                    $this->parameterize($parameters, $keyword)
225
                );
226
            } else {
227
                call_user_func_array(
228
                    [$query, $method],
229
                    $this->parameterize($column, $parameters, $keyword)
230
                );
231
            }
232
        }
233
    }
234
235
    /**
236
     * Build Query Builder Parameters.
237
     *
238
     * @return array
239
     */
240
    protected function parameterize()
241
    {
242
        $args       = func_get_args();
243
        $keyword    = count($args) > 2 ? $args[2] : $args[1];
244
        $parameters = Helper::buildParameters($args);
245
        $parameters = Helper::replacePatternWithKeyword($parameters, $keyword, '$1');
246
247
        return $parameters;
248
    }
249
250
    /**
251
     * Get eager loads keys if eloquent.
252
     *
253
     * @return array
254
     */
255
    protected function getEagerLoads()
256
    {
257
        if ($this->query_type == 'eloquent') {
258
            return array_keys($this->query->getEagerLoads());
0 ignored issues
show
Bug introduced by
The method getEagerLoads does only exist in Illuminate\Database\Eloquent\Builder, but not in Illuminate\Database\Query\Builder.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
259
        }
260
261
        return [];
262
    }
263
264
    /**
265
     * Add relation query on global search.
266
     *
267
     * @param mixed $query
268
     * @param string $relation
269
     * @param string $column
270
     * @param string $keyword
271
     */
272
    protected function compileRelationSearch($query, $relation, $column, $keyword)
273
    {
274
        $myQuery      = clone $this->query;
275
276
        /**
277
         * For compile nested relation, we need store all nested relation as array
278
         * and reverse order to apply where query.
279
         *
280
         * With this method we can create nested sub query with properly relation.
281
         */
282
283
        /**
284
         * Store all relation data that require in next step
285
         */
286
        $relationChunk = [];
287
288
        /**
289
         * Store last eloquent query builder for get next relation.
290
         */
291
        $lastQuery = $query;
292
293
        $relations = explode('.', $relation);
294
        $lastRelation = end($relations);
295
        foreach ($relations as $relation) {
296
            $relationType = $myQuery->getModel()->{$relation}();
297
            $myQuery->orWhereHas($relation, function ($builder) use (
298
                $column,
299
                $keyword,
300
                $query,
301
                $relationType,
302
                $relation,
303
                $lastRelation,
304
                &$relationChunk,
305
                &$lastQuery
306
            ) {
307
                $builder->select($this->connection->raw('count(1)'));
308
309
                // We will perform search on last relation only.
310
                if ($relation == $lastRelation) {
311
                    $this->compileQuerySearch($builder, $column, $keyword, '');
312
                }
313
314
                // Put require object to next step!!
315
                $relationChunk[$relation] = [
316
                    'builder' => $builder,
317
                    'relationType' => $relationType,
318
                    'query' => $lastQuery
319
                ];
320
321
                // This is trick make sub query.
322
                $lastQuery = $builder;
323
            });
324
325
            // This is trick to make nested relation by pass previous relation to be next query eloquent builder
326
            $myQuery = $relationType;
327
        }
328
329
        /**
330
         * Reverse them all
331
         */
332
        $relationChunk = array_reverse($relationChunk, true);
333
334
        /**
335
         * Create valuable for use in check last relation
336
         */
337
        end($relationChunk);
338
        $lastRelation = key($relationChunk);
339
        reset($relationChunk);
340
341
        /**
342
         * Walking ...
343
         */
344
        foreach ($relationChunk as $relation => $chunk) {
345
            // Prepare variables
346
            $builder = $chunk['builder'];
347
            $relationType = $chunk['relationType'];
348
            $query = $chunk['query'];
349
            $builder = "({$builder->toSql()}) >= 1";
350
351
            // Check if it last relation we will use orWhereRaw
352
            if ($lastRelation == $relation) {
353
                $relationMethod = "orWhereRaw";
354
            } else {
355
                // For case parent relation of nested relation.
356
                // We must use and for properly query and get correct result
357
                $relationMethod = "whereRaw";
358
            }
359
360
            if ($relationType instanceof MorphToMany) {
361
                $query->{$relationMethod}($builder, [$relationType->getMorphClass(), $this->prepareKeyword($keyword)]);
362
            } else {
363
                $query->{$relationMethod}($builder, [$this->prepareKeyword($keyword)]);
364
            }
365
        }
366
    }
367
368
    /**
369
     * Compile query builder where clause depending on configurations.
370
     *
371
     * @param mixed $query
372
     * @param string $column
373
     * @param string $keyword
374
     * @param string $relation
375
     */
376
    protected function compileQuerySearch($query, $column, $keyword, $relation = 'or')
377
    {
378
        $column = $this->castColumn($column);
379
        $sql    = $column . ' LIKE ?';
380
381
        if ($this->isCaseInsensitive()) {
382
            $sql = 'LOWER(' . $column . ') LIKE ?';
383
        }
384
385
        $query->{$relation . 'WhereRaw'}($sql, [$this->prepareKeyword($keyword)]);
386
    }
387
388
    /**
389
     * Wrap a column and cast in pgsql.
390
     *
391
     * @param  string $column
392
     * @return string
393
     */
394
    public function castColumn($column)
395
    {
396
        $column = $this->wrap($column);
397
        if ($this->database === 'pgsql') {
398
            $column = 'CAST(' . $column . ' as TEXT)';
399
        } elseif ($this->database === 'firebird') {
400
            $column = 'CAST(' . $column . ' as VARCHAR(255))';
401
        }
402
403
        return $column;
404
    }
405
406
    /**
407
     * Prepare search keyword based on configurations.
408
     *
409
     * @param string $keyword
410
     * @return string
411
     */
412
    protected function prepareKeyword($keyword)
413
    {
414
        if ($this->isCaseInsensitive()) {
415
            $keyword = Str::lower($keyword);
416
        }
417
418
        if ($this->isWildcard()) {
419
            $keyword = $this->wildcardLikeString($keyword);
420
        }
421
422
        if ($this->isSmartSearch()) {
423
            $keyword = "%$keyword%";
424
        }
425
426
        return $keyword;
427
    }
428
429
    /**
430
     * Perform column search.
431
     *
432
     * @return void
433
     */
434
    public function columnSearch()
435
    {
436
        $columns = $this->request->get('columns', []);
437
438
        foreach ($columns as $index => $column) {
439
            if (! $this->request->isColumnSearchable($index)) {
440
                continue;
441
            }
442
443
            $column = $this->getColumnName($index);
444
445
            if (isset($this->columnDef['filter'][$column])) {
446
                $columnDef = $this->columnDef['filter'][$column];
447
                // get a raw keyword (without wildcards)
448
                $keyword = $this->getSearchKeyword($index, true);
449
                $builder = $this->getQueryBuilder();
450
451 View Code Duplication
                if ($columnDef['method'] instanceof Closure) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
452
                    $whereQuery = $builder->newQuery();
453
                    call_user_func_array($columnDef['method'], [$whereQuery, $keyword]);
454
                    $builder->addNestedWhereQuery($whereQuery);
455
                } else {
456
                    $this->compileColumnQuery(
457
                        $builder,
458
                        $columnDef['method'],
459
                        $columnDef['parameters'],
460
                        $column,
461
                        $keyword
462
                    );
463
                }
464 View Code Duplication
            } else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
465
                if (count(explode('.', $column)) > 1) {
466
                    $eagerLoads     = $this->getEagerLoads();
467
                    $parts          = explode('.', $column);
468
                    $relationColumn = array_pop($parts);
469
                    $relation       = implode('.', $parts);
470
                    if (in_array($relation, $eagerLoads)) {
471
                        $column = $this->joinEagerLoadedColumn($relation, $relationColumn);
472
                    }
473
                }
474
475
                $keyword = $this->getSearchKeyword($index);
476
                $this->compileColumnSearch($index, $column, $keyword);
477
            }
478
479
            $this->isFilterApplied = true;
480
        }
481
    }
482
483
    /**
484
     * Get proper keyword to use for search.
485
     *
486
     * @param int $i
487
     * @param bool $raw
488
     * @return string
489
     */
490
    private function getSearchKeyword($i, $raw = false)
491
    {
492
        $keyword = $this->request->columnKeyword($i);
493
        if ($raw || $this->request->isRegex($i)) {
494
            return $keyword;
495
        }
496
497
        return $this->setupKeyword($keyword);
498
    }
499
500
    /**
501
     * Join eager loaded relation and get the related column name.
502
     *
503
     * @param string $relation
504
     * @param string $relationColumn
505
     * @return string
506
     */
507
    protected function joinEagerLoadedColumn($relation, $relationColumn)
508
    {
509
        $joins = [];
510
        foreach ((array) $this->getQueryBuilder()->joins as $key => $join) {
511
            $joins[] = $join->table;
512
        }
513
514
        $model = $this->query->getRelation($relation);
0 ignored issues
show
Bug introduced by
The method getRelation does only exist in Illuminate\Database\Eloquent\Builder, but not in Illuminate\Database\Query\Builder.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
515
        if ($model instanceof BelongsToMany) {
516
            $pivot   = $model->getTable();
517
            $pivotPK = $model->getForeignKey();
518
            $pivotFK = $model->getQualifiedParentKeyName();
519
520
            if (! in_array($pivot, $joins)) {
521
                $this->getQueryBuilder()->leftJoin($pivot, $pivotPK, '=', $pivotFK);
522
            }
523
524
            $related = $model->getRelated();
525
            $table   = $related->getTable();
526
            $tablePK = $related->getForeignKey();
527
            $tableFK = $related->getQualifiedKeyName();
528
529
            if (! in_array($table, $joins)) {
530
                $this->getQueryBuilder()->leftJoin($table, $pivot . '.' . $tablePK, '=', $tableFK);
531
            }
532
        } else {
533
            $table = $model->getRelated()->getTable();
534
            if ($model instanceof HasOneOrMany) {
535
                $foreign = $model->getForeignKey();
536
                $other   = $model->getQualifiedParentKeyName();
537
            } else {
538
                $foreign = $model->getQualifiedForeignKey();
539
                $other   = $model->getQualifiedOtherKeyName();
540
            }
541
542
            if (! in_array($table, $joins)) {
543
                $this->getQueryBuilder()->leftJoin($table, $foreign, '=', $other);
544
            }
545
        }
546
547
        $column = $table . '.' . $relationColumn;
548
549
        return $column;
550
    }
551
552
    /**
553
     * Compile queries for column search.
554
     *
555
     * @param int $i
556
     * @param mixed $column
557
     * @param string $keyword
558
     */
559
    protected function compileColumnSearch($i, $column, $keyword)
560
    {
561
        if ($this->request->isRegex($i)) {
562
            $column = strstr($column, '(') ? $this->connection->raw($column) : $column;
563
            $this->regexColumnSearch($column, $keyword);
564
        } else {
565
            $this->compileQuerySearch($this->query, $column, $keyword, '');
566
        }
567
    }
568
569
    /**
570
     * Compile regex query column search.
571
     *
572
     * @param mixed $column
573
     * @param string $keyword
574
     */
575
    protected function regexColumnSearch($column, $keyword)
576
    {
577
        if ($this->isOracleSql()) {
578
            $sql = ! $this->isCaseInsensitive() ? 'REGEXP_LIKE( ' . $column . ' , ? )' : 'REGEXP_LIKE( LOWER(' . $column . ') , ?, \'i\' )';
579
            $this->query->whereRaw($sql, [$keyword]);
0 ignored issues
show
Bug introduced by
The method whereRaw does only exist in Illuminate\Database\Query\Builder, but not in Illuminate\Database\Eloquent\Builder.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
580
        } else {
581
            $sql = ! $this->isCaseInsensitive() ? $column . ' REGEXP ?' : 'LOWER(' . $column . ') REGEXP ?';
582
            $this->query->whereRaw($sql, [Str::lower($keyword)]);
583
        }
584
    }
585
586
    /**
587
     * Perform sorting of columns.
588
     *
589
     * @return void
590
     */
591
    public function ordering()
592
    {
593
        if ($this->orderCallback) {
594
            call_user_func($this->orderCallback, $this->getQueryBuilder());
595
596
            return;
597
        }
598
599
        foreach ($this->request->orderableColumns() as $orderable) {
600
            $column = $this->getColumnName($orderable['column'], true);
601
602
            if ($this->isBlacklisted($column)) {
603
                continue;
604
            }
605
606
            if (isset($this->columnDef['order'][$column])) {
607
                $method     = $this->columnDef['order'][$column]['method'];
608
                $parameters = $this->columnDef['order'][$column]['parameters'];
609
                $this->compileColumnQuery(
610
                    $this->getQueryBuilder(),
611
                    $method,
612
                    $parameters,
613
                    $column,
614
                    $orderable['direction']
615
                );
616
            } else {
617
                $valid = 1;
618
                if (count(explode('.', $column)) > 1) {
619
                    $eagerLoads     = $this->getEagerLoads();
620
                    $parts          = explode('.', $column);
621
                    $relationColumn = array_pop($parts);
622
                    $relation       = implode('.', $parts);
623
624
                    if (in_array($relation, $eagerLoads)) {
625
                        $relationship = $this->query->getRelation($relation);
0 ignored issues
show
Bug introduced by
The method getRelation does only exist in Illuminate\Database\Eloquent\Builder, but not in Illuminate\Database\Query\Builder.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
626
                        if (! ($relationship instanceof MorphToMany)) {
627
                            $column = $this->joinEagerLoadedColumn($relation, $relationColumn);
628
                        } else {
629
                            $valid = 0;
630
                        }
631
                    }
632
                }
633
634
                if ($valid == 1) {
635
                    $this->getQueryBuilder()->orderBy($column, $orderable['direction']);
636
                }
637
            }
638
        }
639
    }
640
641
    /**
642
     * Perform pagination
643
     *
644
     * @return void
645
     */
646
    public function paging()
647
    {
648
        $this->query->skip($this->request['start'])
0 ignored issues
show
Bug introduced by
The method skip does only exist in Illuminate\Database\Query\Builder, but not in Illuminate\Database\Eloquent\Builder.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
649
                    ->take((int) $this->request['length'] > 0 ? $this->request['length'] : 10);
650
    }
651
652
    /**
653
     * Get results
654
     *
655
     * @return array|static[]
656
     */
657
    public function results()
658
    {
659
        return $this->query->get();
660
    }
661
}
662