QueryBuilderProvider   A
last analyzed

Complexity

Total Complexity 28

Size/Duplication

Total Lines 231
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 8

Importance

Changes 0
Metric Value
wmc 28
lcom 1
cbo 8
dl 0
loc 231
rs 10
c 0
b 0
f 0

12 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A prepareForProcessing() 0 11 1
B process() 0 25 3
A getTotalNumberOfRows() 0 4 1
A compileQuery() 0 8 3
A compileGlobalQuery() 0 6 2
A compileColumnQuery() 0 13 3
A createQueryForColumn() 0 17 4
A getColumnFromName() 0 13 3
A compileColumnNames() 0 9 2
A sortQuery() 0 10 4
A limitQuery() 0 5 1
1
<?php
2
3
namespace OpenSkill\Datatable\Providers;
4
5
use Illuminate\Support\Collection;
6
use Illuminate\Database\Eloquent\Builder;
7
use Illuminate\Database\Query\Builder as QueryBuilder;
8
use OpenSkill\Datatable\Columns\ColumnConfiguration;
9
use OpenSkill\Datatable\Columns\ColumnOrder;
10
use OpenSkill\Datatable\Columns\ColumnSearch;
11
use OpenSkill\Datatable\Columns\Searchable\DefaultSearchable;
12
use OpenSkill\Datatable\Data\ResponseData;
13
use OpenSkill\Datatable\DatatableException;
14
use OpenSkill\Datatable\Queries\QueryConfiguration;
15
16
/**
17
 * Class CollectionProvider
18
 * @package OpenSkill\Datatable\Providers
19
 *
20
 * Provider that is able to provide data based on a initial passed collection.
21
 */
22
class QueryBuilderProvider implements Provider
23
{
24
    /**
25
     * @var QueryBuilder The original query, as passed to us.
26
     */
27
    private $originalQuery;
28
29
    /**
30
     * @var QueryBuilder The query, before limits are applied
31
     */
32
    private $queryBeforeLimits;
33
34
    /**
35
     * @var QueryBuilder The underlying query
36
     */
37
    private $query;
38
39
    /**
40
     * @var QueryConfiguration
41
     */
42
    private $queryConfiguration;
43
44
    /**
45
     * @var array an array that will hold the search functions for each column
46
     */
47
    private $columnConfiguration = [];
48
49
    /**
50
     * CollectionProvider constructor.
51
     * @param QueryBuilder $query The query to base the built query on
52
     */
53
    public function __construct(QueryBuilder $query)
54
    {
55
        $this->originalQuery = $query;
56
        $this->query = clone $query;
57
    }
58
59
    /**
60
     * Here the DTQueryConfiguration is passed to prepare the provider for the processing of the request.
61
     * This will only be called when the DTProvider needs to handle the request.
62
     * It will never be called when the DTProvider does not need to handle the request.
63
     *
64
     * @param QueryConfiguration $queryConfiguration
65
     * @param ColumnConfiguration[] $columnConfiguration
66
     * @return mixed
67
     */
68
    public function prepareForProcessing(QueryConfiguration $queryConfiguration, array $columnConfiguration)
69
    {
70
        $this->queryConfiguration = $queryConfiguration;
71
        $this->columnConfiguration = $columnConfiguration;
72
73
        // compile the query first
74
        $this->compileQuery();
75
76
        // sort
77
        $this->sortQuery();
78
    }
79
80
    /**
81
     * This method should process all configurations and prepare the underlying data for the view. It will arrange the
82
     * data and provide the results in a DTData object.
83
     * It will be called after {@link #prepareForProcessing} has been called and needs to return the processed data in
84
     * a DTData object so the Composer can further handle the data.
85
     *
86
     * @return ResponseData The processed data
87
     *
88
     */
89
    public function process()
90
    {
91
        // check if the query configuration is set
92
        if (is_null($this->queryConfiguration) || empty($this->columnConfiguration)) {
93
            throw new \InvalidArgumentException("Provider was not configured. Did you call prepareForProcessing first?");
94
        }
95
96
        // limit
97
        $this->queryBeforeLimits = clone $this->query;
98
        $this->limitQuery();
99
100
        // # of items in filtered & ordered data set
101
        $dataCount = $this->queryBeforeLimits->count();
102
103
        // the data for the response
104
        $columns = $this->compileColumnNames();
105
        $response = new Collection($this->query->get($columns));
106
107
        // slice the result into the right size
108
        return new ResponseData(
109
            $response,
110
            $this->getTotalNumberOfRows(),
111
            $dataCount
112
        );
113
    }
114
115
    /**
116
     * Get the total number of rows for the original query.
117
     * @return int
118
     */
119
    private function getTotalNumberOfRows()
120
    {
121
        return $this->originalQuery->count();
122
    }
123
124
    /**
125
     * Will compile the collection into the final collection where operations like search and order can be applied.
126
     *
127
     * @return QueryBuilder
128
     * @throws DatatableException
129
     */
130
    private function compileQuery()
131
    {
132
        if ($this->queryConfiguration->isGlobalSearch()) {
133
            $this->compileGlobalQuery();
134
        } elseif ($this->queryConfiguration->isColumnSearch()) {
135
            $this->compileColumnQuery();
136
        }
137
    }
138
139
    /**
140
     * When a global (single) search has been done against data in the datatable.
141
     *
142
     * @return QueryBuilder
143
     * @throws DatatableException
144
     */
145
    private function compileGlobalQuery()
146
    {
147
        foreach ($this->columnConfiguration as $i => $col) {
148
            $this->createQueryForColumn($col, $this->queryConfiguration->searchValue());
149
        }
150
    }
151
152
    /**
153
     * When a global query is being performed (ie, a query against a single column)
154
     *
155
     * @return QueryBuilder
156
     * @throws DatatableException
157
     */
158
    private function compileColumnQuery()
159
    {
160
        $searchColumns = $this->queryConfiguration->searchColumns();
161
162
        foreach ($searchColumns as $i => $col) {
163
            $column = $this->getColumnFromName($col->columnName());
164
165
            if (!isset($column))
166
                continue;
167
168
            $this->createQueryForColumn($column, $col->searchValue());
169
        }
170
    }
171
172
    /**
173
     * Create the query w/ QueryBuilder
174
     * @param ColumnConfiguration $column
175
     * @param $searchValue
176
     * @return QueryBuilder
177
     * @throws DatatableException
178
     */
179
    private function createQueryForColumn(ColumnConfiguration $column, $searchValue)
180
    {
181
        $searchType = $column->getSearch();
182
183
        if ($searchType == DefaultSearchable::NONE()) {
184
            // Don't do anything, this is not a searchable field
185
            return $this->query;
186
        } elseif ($searchType == DefaultSearchable::NORMAL()) {
187
            $this->query->orWhere($column->getName(), 'LIKE', '%' . $searchValue . '%');
188
        } elseif ($searchType == DefaultSearchable::REGEX()) {
189
            $this->query->orWhere($column->getName(), 'REGEXP', $searchValue);
190
        } else {
191
            throw new DatatableException('An unsupported DefaultSearchable was provided.');
192
        }
193
194
        return $this->query;
195
    }
196
197
    /**
198
     * Get the requested column configuration from the name of a column
199
     * @param string $name
200
     * @return ColumnConfiguration
201
     * @throws DatatableException when a column is not found
202
     */
203
    private function getColumnFromName($name)
204
    {
205
        foreach ($this->columnConfiguration as $i => $col) {
206
            if ($col->getName() == $name) {
207
                return $col;
208
            }
209
        }
210
211
        // This exception should never happen. If it does, something is
212
        // wrong w/ the relationship between searchable columns and the
213
        // configuration.
214
        throw new DatatableException("A requested column was not found in the columnConfiguration.");
215
    }
216
217
    /**
218
     * Get a list of all the column names for the SELECT query.
219
     */
220
    private function compileColumnNames()
221
    {
222
        $columns = [];
223
        foreach($this->columnConfiguration as $column) {
224
            $columns[] = $column->getName();
225
        }
226
227
        return $columns;
228
    }
229
230
    /**
231
     * Will sort the query based on the given datatable query configuration.
232
     */
233
    private function sortQuery()
234
    {
235
        if ($this->queryConfiguration->hasOrderColumn()) {
236
            $orderColumns = $this->queryConfiguration->orderColumns();
237
238
            foreach($orderColumns as $order) {
239
                $this->query->orderBy($order->columnName(), $order->isDescending() ? 'desc' : 'asc');
240
            }
241
        }
242
    }
243
244
    /**
245
     * Will limit a query based on the start and length given
246
     */
247
    private function limitQuery()
248
    {
249
        $this->query->skip($this->queryConfiguration->start());
250
        $this->query->limit($this->queryConfiguration->length());
251
    }
252
}