Completed
Push — master ( 39f28d...bd6e2c )
by Arjay
02:18
created

QueryBuilderEngine::count()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 13
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 1 Features 0
Metric Value
dl 0
loc 13
c 2
b 1
f 0
rs 9.4286
cc 2
eloc 7
nc 2
nop 0
1
<?php
2
3
namespace yajra\Datatables\Engines;
4
5
/**
6
 * Laravel Datatables Query Builder Engine
7
 *
8
 * @package  Laravel
9
 * @category Package
10
 * @author   Arjay Angeles <[email protected]>
11
 */
12
13
use Closure;
14
use Illuminate\Database\Query\Builder;
15
use Illuminate\Support\Str;
16
use yajra\Datatables\Contracts\DataTableEngine;
17
use yajra\Datatables\Helper;
18
use yajra\Datatables\Request;
19
20
class QueryBuilderEngine extends BaseEngine implements DataTableEngine
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
     * @inheritdoc
54
     */
55
    public function filter(Closure $callback)
56
    {
57
        $this->overrideGlobalSearch($callback, $this->query);
58
59
        return $this;
60
    }
61
62
    /**
63
     * @inheritdoc
64
     */
65
    public function make($mDataSupport = false, $orderFirst = false)
66
    {
67
        return parent::make($mDataSupport, $orderFirst);
68
    }
69
70
    /**
71
     * Counts current query.
72
     *
73
     * @return int
74
     */
75
    public function count()
76
    {
77
        $myQuery = clone $this->query;
78
        // if its a normal query ( no union, having and distinct word )
79
        // replace the select with static text to improve performance
80
        if ( ! Str::contains(Str::lower($myQuery->toSql()), ['union', 'having', 'distinct'])) {
81
            $row_count = $this->connection->getQueryGrammar()->wrap('row_count');
82
            $myQuery->select($this->connection->raw("'1' as {$row_count}"));
83
        }
84
85
        return $this->connection->table($this->connection->raw('(' . $myQuery->toSql() . ') count_row_table'))
86
                                ->setBindings($myQuery->getBindings())->count();
87
    }
88
89
    /**
90
     * @inheritdoc
91
     */
92
    public function filtering()
93
    {
94
        $this->query->where(
95
            function ($query) {
96
                $keyword = $this->setupKeyword($this->request->keyword());
97
                foreach ($this->request->searchableColumnIndex() as $index) {
98
                    $columnName = $this->setupColumnName($index);
99
100
                    if (isset($this->columnDef['filter'][$columnName])) {
101
                        $method     = Helper::getOrMethod($this->columnDef['filter'][$columnName]['method']);
102
                        $parameters = $this->columnDef['filter'][$columnName]['parameters'];
103
                        $this->compileColumnQuery(
104
                            $this->getQueryBuilder($query), $method, $parameters, $columnName, $keyword
105
                        );
106
                    } else {
107
                        $this->compileGlobalSearch($this->getQueryBuilder($query), $columnName, $keyword);
108
                    }
109
110
                    $this->isFilterApplied = true;
111
                }
112
            }
113
        );
114
    }
115
116
    /**
117
     * Perform filter column on selected field.
118
     *
119
     * @param mixed $query
120
     * @param string $method
121
     * @param mixed $parameters
122
     * @param string $column
123
     * @param string $keyword
124
     */
125
    protected function compileColumnQuery($query, $method, $parameters, $column, $keyword)
126
    {
127
        if (method_exists($query, $method)
128
            && count($parameters) <= with(new \ReflectionMethod($query, $method))->getNumberOfParameters()
129
        ) {
130
            if (Str::contains(Str::lower($method), 'raw')
131
                || Str::contains(Str::lower($method), 'exists')
132
            ) {
133
                call_user_func_array(
134
                    [$query, $method],
135
                    $this->parameterize($parameters, $keyword)
136
                );
137
            } else {
138
                call_user_func_array(
139
                    [$query, $method],
140
                    $this->parameterize($column, $parameters, $keyword)
141
                );
142
            }
143
        }
144
    }
145
146
    /**
147
     * Build Query Builder Parameters.
148
     *
149
     * @return array
150
     */
151
    protected function parameterize()
152
    {
153
        $args       = func_get_args();
154
        $keyword    = count($args) > 2 ? $args[2] : $args[1];
155
        $parameters = Helper::buildParameters($args);
156
        $parameters = Helper::replacePatternWithKeyword($parameters, $keyword, '$1');
157
158
        return $parameters;
159
    }
160
161
    /**
162
     * Add a query on global search.
163
     *
164
     * @param mixed $query
165
     * @param string $column
166
     * @param string $keyword
167
     */
168
    protected function compileGlobalSearch($query, $column, $keyword)
169
    {
170
        $column = $this->castColumn($column);
171
        $sql    = $column . ' LIKE ?';
172
        if ($this->isCaseInsensitive()) {
173
            $sql     = 'LOWER(' . $column . ') LIKE ?';
174
            $keyword = Str::lower($keyword);
175
        }
176
177
        $query->orWhereRaw($sql, [$keyword]);
178
    }
179
180
    /**
181
     * Wrap a column and cast in pgsql
182
     *
183
     * @param  string $column
184
     * @return string
185
     */
186
    public function castColumn($column)
187
    {
188
        $column = $this->connection->getQueryGrammar()->wrap($column);
189
        if ($this->database === 'pgsql') {
190
            $column = 'CAST(' . $column . ' as TEXT)';
191
        }
192
193
        return $column;
194
    }
195
196
    /**
197
     * @inheritdoc
198
     */
199
    public function columnSearch()
200
    {
201
        $columns = $this->request->get('columns');
202
        for ($i = 0, $c = count($columns); $i < $c; $i++) {
203
            if ($this->request->isColumnSearchable($i)) {
204
                $column = $this->setupColumnName($i);
205
                if ($this->request->isRegex($i)) {
206
                    $keyword = $this->request->columnKeyword($i);
207
                } else {
208
                    $keyword = $this->setupKeyword($this->request->columnKeyword($i));
209
                }
210
211
                if (isset($this->columnDef['filter'][$column])) {
212
                    $method     = $this->columnDef['filter'][$column]['method'];
213
                    $parameters = $this->columnDef['filter'][$column]['parameters'];
214
                    $this->compileColumnQuery($this->getQueryBuilder(), $method, $parameters, $column, $keyword);
215
                } else {
216
                    $column = $this->castColumn($column);
217
                    if ($this->isCaseInsensitive()) {
218
                        if ($this->request->isRegex($i)) {
219
                            $this->query->whereRaw('LOWER(' . $column . ') REGEXP ?', [Str::lower($keyword)]);
220
                        } else {
221
                            $this->query->whereRaw('LOWER(' . $column . ') LIKE ?', [Str::lower($keyword)]);
222
                        }
223
                    } else {
224
                        $col = strstr($column, '(') ? $this->connection->raw($column) : $column;
225
                        if ($this->request->isRegex($i)) {
226
                            $this->query->whereRaw($col . ' REGEXP ?', [$keyword]);
227
                        } else {
228
                            $this->query->whereRaw($col . ' LIKE ?', [$keyword]);
229
                        }
230
                    }
231
                }
232
233
                $this->isFilterApplied = true;
234
            }
235
        }
236
    }
237
238
    /**
239
     * @inheritdoc
240
     */
241
    public function ordering()
242
    {
243
        foreach ($this->request->orderableColumns() as $orderable) {
244
            $column = $this->setupColumnName($orderable['column']);
245
            if (isset($this->columnDef['order'][$column])) {
246
                $method     = $this->columnDef['order'][$column]['method'];
247
                $parameters = $this->columnDef['order'][$column]['parameters'];
248
                $this->compileColumnQuery(
249
                    $this->getQueryBuilder(), $method, $parameters, $column, $orderable['direction']
250
                );
251
            } else {
252
                /**
253
                 * If we perform a select("*"), the ORDER BY clause will look like this:
254
                 * ORDER BY * ASC
255
                 * which causes a query exception
256
                 * The temporary fix is modify `*` column to `id` column
257
                 */
258
                if ($column === '*') $column = 'id';
259
                $this->getQueryBuilder()->orderBy($column, $orderable['direction']);
260
            }
261
        }
262
    }
263
264
    /**
265
     * @inheritdoc
266
     */
267
    public function paging()
268
    {
269
        $this->query->skip($this->request['start'])
270
                    ->take((int) $this->request['length'] > 0 ? $this->request['length'] : 10);
271
    }
272
273
    /**
274
     * Get results
275
     *
276
     * @return array|static[]
277
     */
278
    public function results()
279
    {
280
        return $this->query->get();
281
    }
282
283
}
284