Completed
Pull Request — master (#29)
by Tim
02:19
created

QueryBuilderProvider::prepareForProcessing()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 5
rs 9.4285
cc 1
eloc 3
nc 1
nop 2
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
0 ignored issues
show
Unused Code introduced by
The call to ResponseData::__construct() has too many arguments starting with $dataCount.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
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
    private function createQueryForColumn(ColumnConfiguration $column, $searchValue)
186
    {
187
        $searchType = $column->getSearch();
188
189
        if ($searchType == DefaultSearchable::NONE()) {
190
            // Don't do anything, this is not a searchable field
191
            return $this->query;
192
        } elseif ($searchType == DefaultSearchable::NORMAL()) {
193
            $this->query->orWhere($column->getName(), 'LIKE', '%' . $searchValue . '%');
194
        } elseif ($searchType == DefaultSearchable::REGEX()) {
195
            $this->query->orWhere($column->getName(), 'REGEXP', $searchValue);
196
        } else {
197
            throw new DatatableException('An unsupported DefaultSearchable was provided.');
198
        }
199
200
        return $this->query;
201
    }
202
203
    /**
204
     * @param ColumnConfiguration[] $columnConfiguration
205
     * @param string $name
206
     * @return ColumnConfiguration
207
     */
208
    private function getColumnFromName($columnConfiguration, $name)
209
    {
210
        foreach ($columnConfiguration as $i => $col) {
211
            if ($col->getName() == $name)
212
                return $col;
213
        }
214
    }
215
216
    /**
217
     * When a global query is being performed (ie, a query against
218
     */
219
    private function compileColumnNames()
220
    {
221
        $columns = [];
222
        foreach($this->columnConfiguration as $column) {
223
            $columns[] = $column->getName();
224
        }
225
226
        return $columns;
227
    }
228
229
    /**
230
     * Will sort the query based on the given datatable query configuration.
231
     */
232
    private function sortQuery()
233
    {
234
        if ($this->queryConfiguration->hasOrderColumn()) {
235
            $orderColumns = $this->queryConfiguration->orderColumns();
236
237
            foreach($orderColumns as $order) {
238
                $this->query->orderBy($order->columnName(), $order->isAscending() ? 'asc' : 'desc');
239
            }
240
        }
241
    }
242
243
    /**
244
     * Will limit a query hased on the start and length given
245
     */
246
    private function limitQuery()
247
    {
248
        $this->query->skip($this->queryConfiguration->start());
249
        $this->query->limit($this->queryConfiguration->length());
250
    }
251
}