Scrutinizer GitHub App not installed

We could not synchronize checks via GitHub's checks API since Scrutinizer's GitHub App is not installed for this repository.

Install GitHub App

Passed
Push — add-setQuery()-method ( aa34f1 )
by Pedro
15:17
created

Query::take()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Backpack\CRUD\app\Library\CrudPanel\Traits;
4
5
use Illuminate\Contracts\Database\Query\Builder as QueryBuilder;
6
use Illuminate\Database\Eloquent\Builder;
7
8
trait Query
9
{
10
    /** @var Builder */
11
    public $query;
12
13
    /** @var Builder */
14
    public $totalQuery;
15
16
    // ----------------
17
    // ADVANCED QUERIES
18
    // ----------------
19
20
    /**
21
     * Add another clause to the query (for ex, a WHERE clause).
22
     *
23
     * Examples:
24
     * $this->crud->addClause('active');
25
     * $this->crud->addClause('type', 'car');
26
     * $this->crud->addClause('where', 'name', '=', 'car');
27
     * $this->crud->addClause('whereName', 'car');
28
     * $this->crud->addClause('whereHas', 'posts', function($query) {
29
     *     $query->activePosts();
30
     * });
31
     *
32
     * @param  callable|string  $function
33
     * @return mixed
34
     */
35
    public function addClause($function)
36
    {
37
        if ($function instanceof \Closure) {
38
            $function($this->query);
39
40
            return $this->query;
41
        }
42
43
        return call_user_func_array([$this->query, $function], array_slice(func_get_args(), 1));
44
    }
45
46
    /**
47
     * This function is an alias of `addClause` but also adds the query as a constrain
48
     * in the `totalQuery` property.
49
     *
50
     * @param  \Closure|string  $function
51
     * @return self
52
     */
53
    public function addBaseClause($function)
54
    {
55
        if ($function instanceof \Closure) {
56
            $function($this->query);
57
            $function($this->totalQuery);
58
59
            return $this;
60
        }
61
        call_user_func_array([$this->query, $function], array_slice(func_get_args(), 1));
62
        call_user_func_array([$this->totalQuery, $function], array_slice(func_get_args(), 1));
63
64
        return $this;
65
    }
66
67
    public function setQuery(QueryBuilder $query)
68
    {
69
        $this->query = $query;
0 ignored issues
show
Documentation Bug introduced by
$query is of type Illuminate\Contracts\Database\Query\Builder, but the property $query was declared to be of type Illuminate\Database\Eloquent\Builder. Are you sure that you always receive this specific sub-class here, or does it make sense to add an instanceof check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a given class or a super-class is assigned to a property that is type hinted more strictly.

Either this assignment is in error or an instanceof check should be added for that assignment.

class Alien {}

class Dalek extends Alien {}

class Plot
{
    /** @var  Dalek */
    public $villain;
}

$alien = new Alien();
$plot = new Plot();
if ($alien instanceof Dalek) {
    $plot->villain = $alien;
}
Loading history...
70
71
        return $this;
72
    }
73
74
    /**
75
     * Use eager loading to reduce the number of queries on the table view.
76
     *
77
     * @param  array|string  $entities
78
     * @return \Illuminate\Database\Eloquent\Builder
79
     */
80
    public function with($entities)
81
    {
82
        return $this->query->with($entities);
83
    }
84
85
    /**
86
     * Order the results of the query in a certain way.
87
     *
88
     * @param  string  $field
89
     * @param  string  $order
90
     * @return \Illuminate\Database\Eloquent\Builder
91
     */
92
    public function orderBy($field, $order = 'asc')
93
    {
94
        if ($this->getRequest()->has('order')) {
0 ignored issues
show
Bug introduced by
It seems like getRequest() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

94
        if ($this->/** @scrutinizer ignore-call */ getRequest()->has('order')) {
Loading history...
95
            return $this->query;
96
        }
97
98
        return $this->query->orderBy($field, $order);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->query->orderBy($field, $order) also could return the type Illuminate\Database\Query\Builder which is incompatible with the documented return type Illuminate\Database\Eloquent\Builder.
Loading history...
99
    }
100
101
    /**
102
     * Order results of the query in a custom way.
103
     *
104
     * @param  array  $column  Column array with all attributes
105
     * @param  string  $column_direction  ASC or DESC
106
     * @return \Illuminate\Database\Eloquent\Builder
107
     */
108
    public function customOrderBy($column, $columnDirection = 'asc')
109
    {
110
        if (! isset($column['orderLogic'])) {
111
            return $this->query;
112
        }
113
114
        $orderLogic = $column['orderLogic'];
115
116
        if ($orderLogic instanceof \Closure) {
117
            return $orderLogic($this->query, $column, $columnDirection);
118
        }
119
120
        return $this->query;
121
    }
122
123
    /**
124
     * Group the results of the query in a certain way.
125
     *
126
     * @param  string  $field
127
     * @return \Illuminate\Database\Eloquent\Builder
128
     */
129
    public function groupBy($field)
130
    {
131
        return $this->query->groupBy($field);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->query->groupBy($field) also could return the type Illuminate\Database\Query\Builder which is incompatible with the documented return type Illuminate\Database\Eloquent\Builder.
Loading history...
132
    }
133
134
    /**
135
     * Limit the number of results in the query.
136
     *
137
     * @param  int  $number
138
     * @return \Illuminate\Database\Eloquent\Builder
139
     */
140
    public function limit($number)
141
    {
142
        return $this->query->limit($number);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->query->limit($number) also could return the type Illuminate\Database\Query\Builder which is incompatible with the documented return type Illuminate\Database\Eloquent\Builder.
Loading history...
143
    }
144
145
    /**
146
     * Take a certain number of results from the query.
147
     *
148
     * @param  int  $number
149
     * @return \Illuminate\Database\Eloquent\Builder
150
     */
151
    public function take($number)
152
    {
153
        return $this->query->take($number);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->query->take($number) also could return the type Illuminate\Database\Query\Builder which is incompatible with the documented return type Illuminate\Database\Eloquent\Builder.
Loading history...
154
    }
155
156
    /**
157
     * Start the result set from a certain number.
158
     *
159
     * @param  int  $number
160
     * @return \Illuminate\Database\Eloquent\Builder
161
     */
162
    public function skip($number)
163
    {
164
        return $this->query->skip($number);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->query->skip($number) also could return the type Illuminate\Database\Query\Builder which is incompatible with the documented return type Illuminate\Database\Eloquent\Builder.
Loading history...
165
    }
166
167
    /**
168
     * Count the number of results.
169
     *
170
     * @return int
171
     */
172
    public function count()
173
    {
174
        return $this->query->count();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->query->count() also could return the type Illuminate\Database\Eloquent\Builder which is incompatible with the documented return type integer.
Loading history...
175
    }
176
177
    /**
178
     * Apply table prefix in the order clause if the query contains JOINS clauses.
179
     *
180
     * @param  string  $column_name
181
     * @param  string  $column_direction
182
     * @return \Illuminate\Database\Eloquent\Builder
183
     */
184
    public function orderByWithPrefix($column_name, $column_direction = 'asc')
185
    {
186
        $column_direction = strtolower($column_direction);
187
188
        if ($this->query->getQuery()->joins !== null) {
189
            return $this->query->orderBy("{$this->model->getTableWithPrefix()}.{$column_name}", $column_direction);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->query->ord...ame, $column_direction) also could return the type Illuminate\Database\Query\Builder which is incompatible with the documented return type Illuminate\Database\Eloquent\Builder.
Loading history...
190
        }
191
192
        return $this->query->orderBy($column_name, $column_direction);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->query->ord...ame, $column_direction) also could return the type Illuminate\Database\Query\Builder which is incompatible with the documented return type Illuminate\Database\Eloquent\Builder.
Loading history...
193
    }
194
195
    /**
196
     * Get the entries count from `totalQuery`.
197
     *
198
     * @return int
199
     */
200
    public function getTotalQueryCount()
201
    {
202
        if (! $this->getOperationSetting('showEntryCount')) {
0 ignored issues
show
Bug introduced by
It seems like getOperationSetting() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

202
        if (! $this->/** @scrutinizer ignore-call */ getOperationSetting('showEntryCount')) {
Loading history...
203
            return 0;
204
        }
205
206
        return  $this->getOperationSetting('totalEntryCount') ??
207
                $this->getCountFromQuery($this->totalQuery);
208
    }
209
210
    /**
211
     * Get the entries count from the `query`.
212
     *
213
     * @return int
214
     */
215
    public function getQueryCount()
216
    {
217
        return $this->getCountFromQuery($this->query);
218
    }
219
220
    /**
221
     * Return the filtered query count or skip the counting when the `totalQuery` is the same as the filtered one.
222
     *
223
     * @return int|null
224
     */
225
    public function getFilteredQueryCount()
226
    {
227
        // check if the filtered query is different from total query, in case they are the same, skip the count
228
        $filteredQuery = $this->query->toBase()->cloneWithout(['orders', 'limit', 'offset']);
229
230
        return $filteredQuery->toSql() !== $this->totalQuery->toSql() ? $this->getQueryCount() : null;
231
    }
232
233
    /**
234
     * Do a separate query to get the total number of entries, in an optimized way.
235
     *
236
     * @param  Builder  $query
237
     * @return int
238
     */
239
    private function getCountFromQuery(Builder $query)
240
    {
241
        if (! $this->driverIsSql()) {
0 ignored issues
show
Bug introduced by
It seems like driverIsSql() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

241
        if (! $this->/** @scrutinizer ignore-call */ driverIsSql()) {
Loading history...
242
            return $query->count();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $query->count() also could return the type Illuminate\Database\Eloquent\Builder which is incompatible with the documented return type integer.
Loading history...
243
        }
244
245
        $crudQuery = $query->toBase()->clone();
246
247
        $modelTable = $this->model->getTable();
248
249
        // create an "outer" query, the one that is responsible to do the count of the "crud query".
250
        $outerQuery = $crudQuery->newQuery();
251
252
        // add the count query in the "outer" query.
253
        $outerQuery = $outerQuery->selectRaw('count(*) as total_rows');
254
255
        // Expression columns are hand-written by developers in ->selectRaw() and we can't exclude those statements reliably
256
        // so we just store them and re-use them in the sub-query too.
257
        $expressionColumns = [];
258
259
        foreach ($crudQuery->columns ?? [] as $column) {
260
            if (! is_string($column) && is_a($column, 'Illuminate\Database\Query\Expression')) {
261
                $expressionColumns[] = $column;
262
            }
263
        }
264
        // add the subquery from where the "outer query" will count the results.
265
        // this subquery is the "main crud query" without some properties:
266
        // - columns : we manually select the "minimum" columns possible from database.
267
        // - orders/limit/offset because we want the "full query count" where orders don't matter and limit/offset would break the total count
268
        $subQuery = $crudQuery->cloneWithout(['columns', 'orders', 'limit', 'offset']);
269
270
        // select minimum possible columns for the count
271
        $minimumColumns = ($crudQuery->groups || $crudQuery->havings) ? $modelTable.'.*' : $modelTable.'.'.$this->model->getKeyName();
0 ignored issues
show
Bug Best Practice introduced by
The expression $crudQuery->groups of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
Bug Best Practice introduced by
The expression $crudQuery->havings of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
272
        $subQuery->select($minimumColumns);
273
274
        // in case there are raw expressions we need to add them too.
275
        foreach ($expressionColumns as $expression) {
276
            $subQuery->selectRaw($expression);
277
        }
278
279
        // re-set the previous query bindings
280
        //dump($crudQuery->getColumns(), get_class($crudQuery), get_class($subQuery));
281
        foreach ($crudQuery->getRawBindings() as $type => $binding) {
282
            $subQuery->setBindings($binding, $type);
283
        }
284
285
        $outerQuery = $outerQuery->fromSub($subQuery, str_replace('.', '_', $modelTable).'_aggregator');
286
287
        return $outerQuery->cursor()->first()->total_rows;
288
    }
289
}
290