Completed
Push — master ( 233141...899014 )
by Arjay
03:30
created

QueryBuilderEngine   D

Complexity

Total Complexity 80

Size/Duplication

Total Lines 564
Duplicated Lines 10.64 %

Coupling/Cohesion

Components 1
Dependencies 11

Importance

Changes 0
Metric Value
dl 60
loc 564
rs 4.5142
c 0
b 0
f 0
wmc 80
lcom 1
cbo 11

24 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A init() 0 12 2
A filter() 0 6 1
A make() 0 4 1
A totalCount() 0 4 2
A count() 0 18 4
A wrap() 0 4 1
A modelUseSoftDeletes() 0 8 2
C filtering() 33 61 9
B compileColumnQuery() 0 20 5
A parameterize() 0 9 2
A getEagerLoads() 0 8 2
A compileRelationSearch() 0 16 2
A compileQuerySearch() 0 12 3
A castColumn() 0 11 3
A prepareKeyword() 0 16 4
C columnSearch() 27 48 7
A getSearchKeyword() 0 9 3
C joinEagerLoadedColumn() 0 44 7
A compileColumnSearch() 0 9 3
A regexColumnSearch() 0 10 4
C ordering() 0 49 9
A paging() 0 5 2
A results() 0 4 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like QueryBuilderEngine often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use QueryBuilderEngine, and based on these observations, apply Extract Interface, too.

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