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 — CrudButton-fixees ( 8e21aa...0a7a26 )
by Pedro
36:42 queued 21:25
created

Query::getCountFromQuery()   B

Complexity

Conditions 9
Paths 49

Size

Total Lines 49
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 19
Bugs 6 Features 2
Metric Value
cc 9
eloc 19
c 19
b 6
f 2
nc 49
nop 1
dl 0
loc 49
rs 8.0555
1
<?php
2
3
namespace Backpack\CRUD\app\Library\CrudPanel\Traits;
4
5
use Illuminate\Database\Eloquent\Builder;
6
7
trait Query
8
{
9
    /** @var Builder */
10
    public $query;
11
12
    /** @var Builder */
13
    public $totalQuery;
14
15
    // ----------------
16
    // ADVANCED QUERIES
17
    // ----------------
18
19
    /**
20
     * Add another clause to the query (for ex, a WHERE clause).
21
     *
22
     * Examples:
23
     * $this->crud->addClause('active');
24
     * $this->crud->addClause('type', 'car');
25
     * $this->crud->addClause('where', 'name', '=', 'car');
26
     * $this->crud->addClause('whereName', 'car');
27
     * $this->crud->addClause('whereHas', 'posts', function($query) {
28
     *     $query->activePosts();
29
     * });
30
     *
31
     * @param  callable|string  $function
32
     * @return mixed
33
     */
34
    public function addClause($function)
35
    {
36
        if ($function instanceof \Closure) {
37
            $function($this->query);
38
39
            return $this->query;
40
        }
41
42
        return call_user_func_array([$this->query, $function], array_slice(func_get_args(), 1));
43
    }
44
45
    /**
46
     * This function is an alias of `addClause` but also adds the query as a constrain
47
     * in the `totalQuery` property.
48
     *
49
     * @param  \Closure|string  $function
50
     * @return self
51
     */
52
    public function addBaseClause($function)
53
    {
54
        if ($function instanceof \Closure) {
55
            $function($this->query);
56
            $function($this->totalQuery);
57
58
            return $this;
59
        }
60
        call_user_func_array([$this->query, $function], array_slice(func_get_args(), 1));
61
        call_user_func_array([$this->totalQuery, $function], array_slice(func_get_args(), 1));
62
63
        return $this;
64
    }
65
66
    /**
67
     * Use eager loading to reduce the number of queries on the table view.
68
     *
69
     * @param  array|string  $entities
70
     * @return \Illuminate\Database\Eloquent\Builder
71
     */
72
    public function with($entities)
73
    {
74
        return $this->query->with($entities);
75
    }
76
77
    /**
78
     * Order the results of the query in a certain way.
79
     *
80
     * @param  string  $field
81
     * @param  string  $order
82
     * @return \Illuminate\Database\Eloquent\Builder
83
     */
84
    public function orderBy($field, $order = 'asc')
85
    {
86
        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

86
        if ($this->/** @scrutinizer ignore-call */ getRequest()->has('order')) {
Loading history...
87
            return $this->query;
88
        }
89
90
        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...
91
    }
92
93
    /**
94
     * Order results of the query in a custom way.
95
     *
96
     * @param  array  $column  Column array with all attributes
97
     * @param  string  $column_direction  ASC or DESC
98
     * @return \Illuminate\Database\Eloquent\Builder
99
     */
100
    public function customOrderBy($column, $columnDirection = 'asc')
101
    {
102
        if (! isset($column['orderLogic'])) {
103
            return $this->query;
104
        }
105
106
        $orderLogic = $column['orderLogic'];
107
108
        if (is_callable($orderLogic)) {
109
            return $orderLogic($this->query, $column, $columnDirection);
110
        }
111
112
        return $this->query;
113
    }
114
115
    /**
116
     * Group the results of the query in a certain way.
117
     *
118
     * @param  string  $field
119
     * @return \Illuminate\Database\Eloquent\Builder
120
     */
121
    public function groupBy($field)
122
    {
123
        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...
124
    }
125
126
    /**
127
     * Limit the number of results in the query.
128
     *
129
     * @param  int  $number
130
     * @return \Illuminate\Database\Eloquent\Builder
131
     */
132
    public function limit($number)
133
    {
134
        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...
135
    }
136
137
    /**
138
     * Take a certain number of results from the query.
139
     *
140
     * @param  int  $number
141
     * @return \Illuminate\Database\Eloquent\Builder
142
     */
143
    public function take($number)
144
    {
145
        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...
146
    }
147
148
    /**
149
     * Start the result set from a certain number.
150
     *
151
     * @param  int  $number
152
     * @return \Illuminate\Database\Eloquent\Builder
153
     */
154
    public function skip($number)
155
    {
156
        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...
157
    }
158
159
    /**
160
     * Count the number of results.
161
     *
162
     * @return int
163
     */
164
    public function count()
165
    {
166
        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...
167
    }
168
169
    /**
170
     * Apply table prefix in the order clause if the query contains JOINS clauses.
171
     *
172
     * @param  string  $column_name
173
     * @param  string  $column_direction
174
     * @return \Illuminate\Database\Eloquent\Builder
175
     */
176
    public function orderByWithPrefix($column_name, $column_direction = 'ASC')
177
    {
178
        if ($this->query->getQuery()->joins !== null) {
179
            return $this->query->orderByRaw($this->model->getTableWithPrefix().'.'.$column_name.' '.$column_direction);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->query->ord... ' . $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...
180
        }
181
182
        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...
183
    }
184
185
    /**
186
     * Get the entries count from `totalQuery`.
187
     *
188
     * @return int
189
     */
190
    public function getTotalQueryCount()
191
    {
192
        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

192
        if (! $this->/** @scrutinizer ignore-call */ getOperationSetting('showEntryCount')) {
Loading history...
193
            return 0;
194
        }
195
196
        return  $this->getOperationSetting('totalEntryCount') ??
197
                $this->getCountFromQuery($this->totalQuery);
198
    }
199
200
    /**
201
     * Get the entries count from the `query`.
202
     *
203
     * @return int
204
     */
205
    public function getQueryCount()
206
    {
207
        return $this->getCountFromQuery($this->query);
208
    }
209
210
    /**
211
     * Return the filtered query count or skip the counting when the `totalQuery` is the same as the filtered one.
212
     *
213
     * @return int|null
214
     */
215
    public function getFilteredQueryCount()
216
    {
217
        // check if the filtered query is different from total query, in case they are the same, skip the count
218
        $filteredQuery = $this->query->toBase()->cloneWithout(['orders', 'limit', 'offset']);
219
220
        return $filteredQuery->toSql() !== $this->totalQuery->toSql() ? $this->getQueryCount() : null;
221
    }
222
223
    /**
224
     * Do a separate query to get the total number of entries, in an optimized way.
225
     *
226
     * @param  Builder  $query
227
     * @return int
228
     */
229
    private function getCountFromQuery(Builder $query)
230
    {
231
        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

231
        if (! $this->/** @scrutinizer ignore-call */ driverIsSql()) {
Loading history...
232
            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...
233
        }
234
235
        $crudQuery = $query->toBase()->clone();
236
237
        $modelTable = $this->model->getTable();
238
239
        // create an "outer" query, the one that is responsible to do the count of the "crud query".
240
        $outerQuery = $crudQuery->newQuery();
241
242
        // add the count query in the "outer" query.
243
        $outerQuery = $outerQuery->selectRaw('count(*) as total_rows');
244
245
        // Expression columns are hand-written by developers in ->selectRaw() and we can't exclude those statements reliably
246
        // so we just store them and re-use them in the sub-query too.
247
        $expressionColumns = [];
248
249
        foreach ($crudQuery->columns ?? [] as $column) {
250
            if (! is_string($column) && is_a($column, 'Illuminate\Database\Query\Expression')) {
251
                $expressionColumns[] = $column;
252
            }
253
        }
254
        // add the subquery from where the "outer query" will count the results.
255
        // this subquery is the "main crud query" without some properties:
256
        // - columns : we manually select the "minimum" columns possible from database.
257
        // - orders/limit/offset because we want the "full query count" where orders don't matter and limit/offset would break the total count
258
        $subQuery = $crudQuery->cloneWithout(['columns', 'orders', 'limit', 'offset']);
259
260
        // select minimum possible columns for the count
261
        $minimumColumns = ($crudQuery->groups || $crudQuery->havings) ? '*' : $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...
262
        $subQuery->select($minimumColumns);
263
264
        // in case there are raw expressions we need to add them too.
265
        foreach ($expressionColumns as $expression) {
266
            $subQuery->selectRaw($expression);
267
        }
268
269
        // re-set the previous query bindings
270
        //dump($crudQuery->getColumns(), get_class($crudQuery), get_class($subQuery));
271
        foreach ($crudQuery->getRawBindings() as $type => $binding) {
272
            $subQuery->setBindings($binding, $type);
273
        }
274
275
        $outerQuery = $outerQuery->fromSub($subQuery, str_replace('.', '_', $modelTable).'_aggregator');
276
277
        return $outerQuery->cursor()->first()->total_rows;
278
    }
279
}
280