Completed
Push — master ( d49c92...18b88e )
by Tim
02:28
created

QueryBuilderProvider::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 5
rs 9.4285
cc 1
eloc 3
nc 1
nop 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 callable the default global function to check if a row should be included
46
     */
47
    private $defaultGlobalSearchFunction;
0 ignored issues
show
Unused Code introduced by
The property $defaultGlobalSearchFunction is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
48
49
    /**
50
     * @var callable the default global order function
51
     */
52
    private $defaultGlobalOrderFunction;
0 ignored issues
show
Unused Code introduced by
The property $defaultGlobalOrderFunction is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
53
54
    /**
55
     * @var array an array of callables with local search functions to check if the row should be included
56
     */
57
    private $columnSearchFunction = [];
0 ignored issues
show
Unused Code introduced by
The property $columnSearchFunction is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
58
59
    /**
60
     * @var array an array that will hold the search functions for each column
61
     */
62
    private $columnConfiguration = [];
63
64
    /**
65
     * CollectionProvider constructor.
66
     * @param QueryBuilder $query The query to base the built query on
67
     */
68
    public function __construct(QueryBuilder $query)
69
    {
70
        $this->originalQuery = $query;
71
        $this->query = clone $query;
72
    }
73
74
    /**
75
     * Here the DTQueryConfiguration is passed to prepare the provider for the processing of the request.
76
     * This will only be called when the DTProvider needs to handle the request.
77
     * It will never be called when the DTProvider does not need to handle the request.
78
     *
79
     * @param QueryConfiguration $queryConfiguration
80
     * @param ColumnConfiguration[] $columnConfiguration
81
     * @return mixed
82
     */
83
    public function prepareForProcessing(QueryConfiguration $queryConfiguration, array $columnConfiguration)
84
    {
85
        $this->queryConfiguration = $queryConfiguration;
86
        $this->columnConfiguration = $columnConfiguration;
87
    }
88
89
    /**
90
     * This method should process all configurations and prepare the underlying data for the view. It will arrange the
91
     * data and provide the results in a DTData object.
92
     * It will be called after {@link #prepareForProcessing} has been called and needs to return the processed data in
93
     * a DTData object so the Composer can further handle the data.
94
     *
95
     * @return ResponseData The processed data
96
     *
97
     */
98
    public function process()
99
    {
100
        // check if the query configuration is set
101
        if (is_null($this->queryConfiguration) || empty($this->columnConfiguration)) {
102
            throw new \InvalidArgumentException("Provider was not configured. Did you call prepareForProcessing first?");
103
        }
104
105
        // compile the query first
106
        $this->compileQuery($this->columnConfiguration);
107
108
        // sort
109
        $this->sortQuery();
110
111
        // limit
112
        $this->queryBeforeLimits = clone $this->query;
113
        $this->limitQuery();
114
115
        // original # of items
116
        $filteredItems = $this->originalQuery->count();
117
118
        // # of items in filtered & ordered dataset
119
        $dataCount = $this->queryBeforeLimits->count();
120
121
        // the data for the response
122
        $columns = $this->compileColumnNames();
123
        $response = new Collection($this->query->get($columns));
124
125
        // slice the result into the right size
126
        return new ResponseData(
127
            $response,
128
            $filteredItems,
129
            $dataCount
130
        );
131
    }
132
133
    /**
134
     * Will compile the collection into the final collection where operations like search and order can be applied.
135
     *
136
     * @param QueryBuilder $query
0 ignored issues
show
Bug introduced by
There is no parameter named $query. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
137
     * @param ColumnConfiguration[] $columnConfiguration
138
     * @return QueryBuilder
139
     * @throws DatatableException
140
     */
141
    private function compileQuery(array $columnConfiguration)
142
    {
143
        if ($this->queryConfiguration->isGlobalSearch()) {
144
            $this->compileGlobalQuery($columnConfiguration);
145
        } elseif ($this->queryConfiguration->isColumnSearch()) {
146
            $this->compileColumnQuery($columnConfiguration);
147
        }
148
    }
149
150
    /**
151
     * When a global (single) search has been done against data in the datatable.
152
     *
153
     * @param array $columnConfiguration
154
     * @return QueryBuilder
155
     * @throws DatatableException
156
     */
157
    private function compileGlobalQuery(array $columnConfiguration)
158
    {
159
        foreach ($columnConfiguration as $i => $col) {
160
            $this->createQueryForColumn($col, $this->queryConfiguration->searchValue());
161
        }
162
    }
163
164
    /**
165
     * When a global query is being performed (ie, a query against a single column)
166
     *
167
     * @param ColumnConfiguration[] $columnConfiguration
168
     * @return QueryBuilder
169
     * @throws DatatableException
170
     */
171
    private function compileColumnQuery(array $columnConfiguration)
172
    {
173
        $searchColumns = $this->queryConfiguration->searchColumns();
174
175
        foreach ($searchColumns as $i => $col) {
176
            $column = $this->getColumnFromName($columnConfiguration, $col->columnName());
177
178
            if (!isset($column))
179
                continue;
180
181
            $this->createQueryForColumn($column, $col->searchValue());
182
        }
183
    }
184
185
    /**
186
     * Create the query w/ QueryBuilder
187
     * @param ColumnConfiguration $column
188
     * @param $searchValue
189
     * @return QueryBuilder
190
     * @throws DatatableException
191
     */
192
    private function createQueryForColumn(ColumnConfiguration $column, $searchValue)
193
    {
194
        $searchType = $column->getSearch();
195
196
        if ($searchType == DefaultSearchable::NONE()) {
197
            // Don't do anything, this is not a searchable field
198
            return $this->query;
199
        } elseif ($searchType == DefaultSearchable::NORMAL()) {
200
            $this->query->orWhere($column->getName(), 'LIKE', '%' . $searchValue . '%');
201
        } elseif ($searchType == DefaultSearchable::REGEX()) {
202
            $this->query->orWhere($column->getName(), 'REGEXP', $searchValue);
203
        } else {
204
            throw new DatatableException('An unsupported DefaultSearchable was provided.');
205
        }
206
207
        return $this->query;
208
    }
209
210
    /**
211
     * @param ColumnConfiguration[] $columnConfiguration
212
     * @param string $name
213
     * @return ColumnConfiguration
214
     */
215
    private function getColumnFromName($columnConfiguration, $name)
216
    {
217
        foreach ($columnConfiguration as $i => $col) {
218
            if ($col->getName() == $name)
219
                return $col;
220
        }
221
    }
222
223
    /**
224
     * When a global query is being performed (ie, a query against
225
     */
226
    private function compileColumnNames()
227
    {
228
        $columns = [];
229
        foreach($this->columnConfiguration as $column) {
230
            $columns[] = $column->getName();
231
        }
232
233
        return $columns;
234
    }
235
236
    /**
237
     * Will sort the query based on the given datatable query configuration.
238
     */
239
    private function sortQuery()
240
    {
241
        if ($this->queryConfiguration->hasOrderColumn()) {
242
            $orderColumns = $this->queryConfiguration->orderColumns();
243
244
            foreach($orderColumns as $order) {
245
                $this->query->orderBy($order->columnName(), $order->isAscending() ? 'asc' : 'desc');
246
            }
247
        }
248
    }
249
250
    /**
251
     * Will limit a query hased on the start and length given
252
     */
253
    private function limitQuery()
254
    {
255
        $this->query->skip($this->queryConfiguration->start());
256
        $this->query->limit($this->queryConfiguration->length());
257
    }
258
}