Completed
Push — master ( 818c2f...233141 )
by Arjay
05:20
created

QueryBuilderEngine::modelUseSoftDeletes()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 4
nc 2
nop 0
dl 0
loc 8
rs 9.4285
c 0
b 0
f 0
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\HasOne;
8
use Illuminate\Database\Eloquent\Relations\HasMany;
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
     * Perform global search.
126
     *
127
     * @return void
128
     */
129
    public function filtering()
130
    {
131
        $this->query->where(
132
            function ($query) {
133
                $globalKeyword = $this->request->keyword();
134
                $queryBuilder  = $this->getQueryBuilder($query);
135
136
                foreach ($this->request->searchableColumnIndex() as $index) {
137
                    $columnName = $this->getColumnName($index);
138
                    if ($this->isBlacklisted($columnName)) {
139
                        continue;
140
                    }
141
142
                    // check if custom column filtering is applied
143
                    if (isset($this->columnDef['filter'][$columnName])) {
144
                        $columnDef = $this->columnDef['filter'][$columnName];
145
                        // check if global search should be applied for the specific column
146
                        $applyGlobalSearch = count($columnDef['parameters']) == 0 || end($columnDef['parameters']) !== false;
147
                        if (! $applyGlobalSearch) {
148
                            continue;
149
                        }
150
151 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...
152
                            $whereQuery = $queryBuilder->newQuery();
153
                            call_user_func_array($columnDef['method'], [$whereQuery, $globalKeyword]);
154
                            $queryBuilder->addNestedWhereQuery($whereQuery, 'or');
155
                        } else {
156
                            $this->compileColumnQuery(
157
                                $queryBuilder,
158
                                Helper::getOrMethod($columnDef['method']),
159
                                $columnDef['parameters'],
160
                                $columnName,
161
                                $globalKeyword
162
                            );
163
                        }
164 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...
165
                        if (count(explode('.', $columnName)) > 1) {
166
                            $eagerLoads     = $this->getEagerLoads();
167
                            $parts          = explode('.', $columnName);
168
                            $relationColumn = array_pop($parts);
169
                            $relation       = implode('.', $parts);
170
                            if (in_array($relation, $eagerLoads)) {
171
                                $this->compileRelationSearch(
172
                                    $queryBuilder,
173
                                    $relation,
174
                                    $relationColumn,
175
                                    $globalKeyword
176
                                );
177
                            } else {
178
                                $this->compileQuerySearch($queryBuilder, $columnName, $globalKeyword);
179
                            }
180
                        } else {
181
                            $this->compileQuerySearch($queryBuilder, $columnName, $globalKeyword);
182
                        }
183
                    }
184
185
                    $this->isFilterApplied = true;
186
                }
187
            }
188
        );
189
    }
190
191
    /**
192
     * Perform filter column on selected field.
193
     *
194
     * @param mixed $query
195
     * @param string|Closure $method
196
     * @param mixed $parameters
197
     * @param string $column
198
     * @param string $keyword
199
     */
200
    protected function compileColumnQuery($query, $method, $parameters, $column, $keyword)
201
    {
202
        if (method_exists($query, $method)
203
            && count($parameters) <= with(new \ReflectionMethod($query, $method))->getNumberOfParameters()
204
        ) {
205
            if (Str::contains(Str::lower($method), 'raw')
0 ignored issues
show
Bug introduced by
It seems like $method defined by parameter $method on line 200 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...
206
                || Str::contains(Str::lower($method), 'exists')
0 ignored issues
show
Bug introduced by
It seems like $method defined by parameter $method on line 200 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...
207
            ) {
208
                call_user_func_array(
209
                    [$query, $method],
210
                    $this->parameterize($parameters, $keyword)
211
                );
212
            } else {
213
                call_user_func_array(
214
                    [$query, $method],
215
                    $this->parameterize($column, $parameters, $keyword)
216
                );
217
            }
218
        }
219
    }
220
221
    /**
222
     * Build Query Builder Parameters.
223
     *
224
     * @return array
225
     */
226
    protected function parameterize()
227
    {
228
        $args       = func_get_args();
229
        $keyword    = count($args) > 2 ? $args[2] : $args[1];
230
        $parameters = Helper::buildParameters($args);
231
        $parameters = Helper::replacePatternWithKeyword($parameters, $keyword, '$1');
232
233
        return $parameters;
234
    }
235
236
    /**
237
     * Get eager loads keys if eloquent.
238
     *
239
     * @return array
240
     */
241
    protected function getEagerLoads()
242
    {
243
        if ($this->query_type == 'eloquent') {
244
            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...
245
        }
246
247
        return [];
248
    }
249
250
    /**
251
     * Add relation query on global search.
252
     *
253
     * @param mixed $query
254
     * @param string $relation
255
     * @param string $column
256
     * @param string $keyword
257
     */
258
    protected function compileRelationSearch($query, $relation, $column, $keyword)
259
    {
260
        $myQuery = clone $this->query;
261
        $myQuery->orWhereHas($relation, function ($builder) use ($column, $keyword, $query) {
0 ignored issues
show
Bug introduced by
The method orWhereHas 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...
262
            $builder->select($this->connection->raw('count(1)'));
263
            $this->compileQuerySearch($builder, $column, $keyword, '');
264
            $builder = "({$builder->toSql()}) >= 1";
265
266
            $query->orWhereRaw($builder, [$this->prepareKeyword($keyword)]);
267
        });
268
    }
269
270
    /**
271
     * Compile query builder where clause depending on configurations.
272
     *
273
     * @param mixed $query
274
     * @param string $column
275
     * @param string $keyword
276
     * @param string $relation
277
     */
278
    protected function compileQuerySearch($query, $column, $keyword, $relation = 'or')
279
    {
280
        $column = strstr($column, '(') ? $this->connection->raw($column) : $column;
281
        $column = $this->castColumn($column);
0 ignored issues
show
Bug introduced by
It seems like $column can also be of type object<Illuminate\Database\Query\Expression>; however, Yajra\Datatables\Engines...derEngine::castColumn() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
282
        $sql    = $column . ' LIKE ?';
283
284
        if ($this->isCaseInsensitive()) {
285
            $sql = 'LOWER(' . $column . ') LIKE ?';
286
        }
287
288
        $query->{$relation . 'WhereRaw'}($sql, [$this->prepareKeyword($keyword)]);
289
    }
290
291
    /**
292
     * Wrap a column and cast in pgsql.
293
     *
294
     * @param  string $column
295
     * @return string
296
     */
297
    public function castColumn($column)
298
    {
299
        $column = $this->wrap($column);
300
        if ($this->database === 'pgsql') {
301
            $column = 'CAST(' . $column . ' as TEXT)';
302
        } elseif ($this->database === 'firebird') {
303
            $column = 'CAST(' . $column . ' as VARCHAR(255))';
304
        }
305
306
        return $column;
307
    }
308
309
    /**
310
     * Prepare search keyword based on configurations.
311
     *
312
     * @param string $keyword
313
     * @return string
314
     */
315
    protected function prepareKeyword($keyword)
316
    {
317
        if ($this->isCaseInsensitive()) {
318
            $keyword = Str::lower($keyword);
319
        }
320
321
        if ($this->isWildcard()) {
322
            $keyword = $this->wildcardLikeString($keyword);
323
        }
324
325
        if ($this->isSmartSearch()) {
326
            $keyword = "%$keyword%";
327
        }
328
329
        return $keyword;
330
    }
331
332
    /**
333
     * Perform column search.
334
     *
335
     * @return void
336
     */
337
    public function columnSearch()
338
    {
339
        $columns = $this->request->get('columns', []);
340
341
        foreach ($columns as $index => $column) {
342
            if (! $this->request->isColumnSearchable($index)) {
343
                continue;
344
            }
345
346
            $column = $this->getColumnName($index);
347
348
            if (isset($this->columnDef['filter'][$column])) {
349
                $columnDef = $this->columnDef['filter'][$column];
350
                // get a raw keyword (without wildcards)
351
                $keyword = $this->getSearchKeyword($index, true);
352
                $builder = $this->getQueryBuilder();
353
354 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...
355
                    $whereQuery = $builder->newQuery();
356
                    call_user_func_array($columnDef['method'], [$whereQuery, $keyword]);
357
                    $builder->addNestedWhereQuery($whereQuery);
358
                } else {
359
                    $this->compileColumnQuery(
360
                        $builder,
361
                        $columnDef['method'],
362
                        $columnDef['parameters'],
363
                        $column,
364
                        $keyword
365
                    );
366
                }
367 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...
368
                if (count(explode('.', $column)) > 1) {
369
                    $eagerLoads     = $this->getEagerLoads();
370
                    $parts          = explode('.', $column);
371
                    $relationColumn = array_pop($parts);
372
                    $relation       = implode('.', $parts);
373
                    if (in_array($relation, $eagerLoads)) {
374
                        $column = $this->joinEagerLoadedColumn($relation, $relationColumn);
375
                    }
376
                }
377
378
                $keyword = $this->getSearchKeyword($index);
379
                $this->compileColumnSearch($index, $column, $keyword);
380
            }
381
382
            $this->isFilterApplied = true;
383
        }
384
    }
385
386
    /**
387
     * Get proper keyword to use for search.
388
     *
389
     * @param int $i
390
     * @param bool $raw
391
     * @return string
392
     */
393
    private function getSearchKeyword($i, $raw = false)
394
    {
395
        $keyword = $this->request->columnKeyword($i);
396
        if ($raw || $this->request->isRegex($i)) {
397
            return $keyword;
398
        }
399
400
        return $this->setupKeyword($keyword);
401
    }
402
    
403
    /**
404
     * Check if model use SoftDeletes trait
405
     *
406
     * @return boolean
407
     */
408
    private function modelUseSoftDeletes()
409
    {
410
        if ($this->query_type == 'eloquent') {
411
            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...
412
        }
413
        
414
        return false;
415
    }
416
    
417
    /**
418
     * Join eager loaded relation and get the related column name.
419
     *
420
     * @param string $relation
421
     * @param string $relationColumn
422
     * @return string
423
     */
424
    protected function joinEagerLoadedColumn($relation, $relationColumn)
425
    {
426
        $joins = [];
427
        foreach ((array) $this->getQueryBuilder()->joins as $key => $join) {
428
            $joins[] = $join->table;
429
        }
430
431
        $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...
432
        if ($model instanceof BelongsToMany) {
433
            $pivot   = $model->getTable();
434
            $pivotPK = $model->getForeignKey();
435
            $pivotFK = $model->getQualifiedParentKeyName();
436
437
            if (! in_array($pivot, $joins)) {
438
                $this->getQueryBuilder()->leftJoin($pivot, $pivotPK, '=', $pivotFK);
439
            }
440
441
            $related = $model->getRelated();
442
            $table   = $related->getTable();
443
            $tablePK = $related->getForeignKey();
444
            $tableFK = $related->getQualifiedKeyName();
445
446
            if (! in_array($table, $joins)) {
447
                $this->getQueryBuilder()->leftJoin($table, $pivot . '.' . $tablePK, '=', $tableFK);
448
            }
449
        } else {
450
            $table = $model->getRelated()->getTable();
451
            if ($model instanceof HasOne || $model instanceof HasMany) {
452
                $foreign = $model->getForeignKey();
453
                $other   = $model->getQualifiedParentKeyName();
454
            } else {
455
                $foreign = $model->getQualifiedForeignKey();
456
                $other   = $model->getQualifiedOtherKeyName();
457
            }
458
459
            if (! in_array($table, $joins)) {
460
                $this->getQueryBuilder()->leftJoin($table, $foreign, '=', $other);
461
            }
462
        }
463
464
        $column = $table . '.' . $relationColumn;
465
466
        return $column;
467
    }
468
469
    /**
470
     * Compile queries for column search.
471
     *
472
     * @param int $i
473
     * @param mixed $column
474
     * @param string $keyword
475
     */
476
    protected function compileColumnSearch($i, $column, $keyword)
477
    {
478
        if ($this->request->isRegex($i)) {
479
            $column = strstr($column, '(') ? $this->connection->raw($column) : $column;
480
            $this->regexColumnSearch($column, $keyword);
481
        } else {
482
            $this->compileQuerySearch($this->query, $column, $keyword, '');
483
        }
484
    }
485
486
    /**
487
     * Compile regex query column search.
488
     *
489
     * @param mixed $column
490
     * @param string $keyword
491
     */
492
    protected function regexColumnSearch($column, $keyword)
493
    {
494
        if ($this->isOracleSql()) {
495
            $sql = ! $this->isCaseInsensitive() ? 'REGEXP_LIKE( ' . $column . ' , ? )' : 'REGEXP_LIKE( LOWER(' . $column . ') , ?, \'i\' )';
496
            $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...
497
        } else {
498
            $sql = ! $this->isCaseInsensitive() ? $column . ' REGEXP ?' : 'LOWER(' . $column . ') REGEXP ?';
499
            $this->query->whereRaw($sql, [Str::lower($keyword)]);
500
        }
501
    }
502
503
    /**
504
     * Perform sorting of columns.
505
     *
506
     * @return void
507
     */
508
    public function ordering()
509
    {
510
        if ($this->orderCallback) {
511
            call_user_func($this->orderCallback, $this->getQueryBuilder());
512
513
            return;
514
        }
515
516
        foreach ($this->request->orderableColumns() as $orderable) {
517
            $column = $this->getColumnName($orderable['column'], true);
518
519
            if ($this->isBlacklisted($column)) {
520
                continue;
521
            }
522
523
            if (isset($this->columnDef['order'][$column])) {
524
                $method     = $this->columnDef['order'][$column]['method'];
525
                $parameters = $this->columnDef['order'][$column]['parameters'];
526
                $this->compileColumnQuery(
527
                    $this->getQueryBuilder(),
528
                    $method,
529
                    $parameters,
530
                    $column,
531
                    $orderable['direction']
532
                );
533
            } else {
534
                if (count(explode('.', $column)) > 1) {
535
                    $eagerLoads     = $this->getEagerLoads();
536
                    $parts          = explode('.', $column);
537
                    $relationColumn = array_pop($parts);
538
                    $relation       = implode('.', $parts);
539
540
                    if (in_array($relation, $eagerLoads)) {
541
                        $column = $this->joinEagerLoadedColumn($relation, $relationColumn);
542
                    }
543
                }
544
545
                $this->getQueryBuilder()->orderBy($column, $orderable['direction']);
546
            }
547
        }
548
    }
549
550
    /**
551
     * Perform pagination
552
     *
553
     * @return void
554
     */
555
    public function paging()
556
    {
557
        $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...
558
                    ->take((int) $this->request['length'] > 0 ? $this->request['length'] : 10);
559
    }
560
561
    /**
562
     * Get results
563
     *
564
     * @return array|static[]
565
     */
566
    public function results()
567
    {
568
        return $this->query->get();
569
    }
570
}
571