Test Failed
Push — master ( 1f284f...9793d0 )
by Mathieu
05:27 queued 10s
created

Datatable   A

Complexity

Total Complexity 34

Size/Duplication

Total Lines 336
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 34
eloc 82
dl 0
loc 336
rs 9.68
c 0
b 0
f 0

15 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 15 2
A createGlobalFilters() 0 14 1
A result() 0 9 1
A count() 0 8 1
A processColumnSelect() 0 3 1
A limit() 0 6 1
B createWherePart() 0 12 7
A processColumnIdentifier() 0 3 2
A orderBy() 0 8 1
A createQueryResult() 0 9 2
A createFoundationQuery() 0 12 5
B get() 0 28 7
A setGlobalSearch() 0 5 1
A setNameIdentifier() 0 5 1
A getNameIdentifier() 0 3 1
1
<?php
2
3
namespace DoctrineDatatable;
4
5
use Doctrine\ORM\QueryBuilder;
6
use DoctrineDatatable\Exception\MinimumColumn;
7
8
/**
9
 * Class Datatable.
10
 *
11
 * @author Mathieu Petrini <[email protected]>
12
 */
13
class Datatable
14
{
15
    /**
16
     * @var QueryBuilder
17
     */
18
    private $query;
19
20
    /**
21
     * @var string
22
     */
23
    private $identifier;
24
25
    /**
26
     * @var Column[]
27
     */
28
    private $columns;
29
30
    /**
31
     * @var int
32
     */
33
    private $resultPerPage;
34
35
    /**
36
     * @var string
37
     */
38
    private $nameIdentifier;
39
40
    /**
41
     * @var bool
42
     */
43
    private $globalSearch;
44
45
    private const DEFAULT_NAME_IDENTIFIER = 'DT_RowID';
46
47
    public const RESULT_PER_PAGE = 30;
48
49
    /**
50
     * Datatable constructor.
51
     *
52
     * @author Mathieu Petrini <[email protected]>
53
     *
54
     * @param QueryBuilder $query
55
     * @param string       $identifier
56
     * @param array        $columns
57
     * @param int|null     $resultPerPage
58
     *
59
     * @throws MinimumColumn
60
     */
61
    public function __construct(
62
        QueryBuilder $query,
63
        string $identifier,
64
        array $columns,
65
        ?int $resultPerPage = self::RESULT_PER_PAGE
66
    ) {
67
        if (empty($columns)) {
68
            throw new MinimumColumn();
69
        }
70
        $this->query = $query;
71
        $this->identifier = $identifier;
72
        $this->columns = $columns;
73
        $this->resultPerPage = $resultPerPage ?? self::RESULT_PER_PAGE;
74
        $this->nameIdentifier = self::DEFAULT_NAME_IDENTIFIER;
75
        $this->globalSearch = false;
76
    }
77
78
    /**
79
     * PRIVATE METHODS.
80
     */
81
82
    /**
83
     * @author Mathieu Petrini <[email protected]>
84
     *
85
     * @param array $filters
86
     *
87
     * @return array
88
     */
89
    private function createGlobalFilters(array $filters): array
90
    {
91
        $temp = array(
92
            'columns' => array(),
93
        );
94
        array_map(function () use ($filters, &$temp) {
95
            $temp['columns'][] = array(
96
                'search' => array(
97
                    'value' => $filters['search'][Column::GLOBAL_ALIAS],
98
                ),
99
            );
100
        }, $this->columns);
101
102
        return $temp;
103
    }
104
105
    /**
106
     * @author Mathieu Petrini <[email protected]>
107
     *
108
     * @param QueryBuilder $query
109
     * @param array        $filters
110
     *
111
     * @return string
112
     *
113
     * @throws Exception\ResolveColumnNotHandle
114
     * @throws Exception\UnfilterableColumn
115
     * @throws Exception\WhereColumnNotHandle
116
     */
117
    private function createWherePart(QueryBuilder &$query, array $filters): string
118
    {
119
        $temp = '';
120
121
        foreach (isset($filters['columns']) ? $filters['columns'] : array() as $index => $filter) {
122
            if (isset($this->columns[$index]) && !empty($filter['search']['value'])) {
123
                $temp .= (!empty($temp) ? ' '.($this->globalSearch ? 'OR' : 'AND').' ' : '').
124
                    '('.$this->columns[$index]->where($query, $filter['search']['value']).')';
125
            }
126
        }
127
128
        return $temp;
129
    }
130
131
    /**
132
     * @author Mathieu Petrini <[email protected]>
133
     *
134
     * @param QueryBuilder $query
135
     * @param array        $filters
136
     *
137
     * @return QueryBuilder
138
     *
139
     * @throws Exception\ResolveColumnNotHandle
140
     * @throws Exception\UnfilterableColumn
141
     * @throws Exception\WhereColumnNotHandle
142
     */
143
    private function createFoundationQuery(QueryBuilder &$query, array $filters): QueryBuilder
144
    {
145
        // If global search we erase all specific where and only keep the unified filter
146
        if ($this->globalSearch && isset($filters['search']) && !empty($filters['search'][Column::GLOBAL_ALIAS])) {
147
            $filters = $this->createGlobalFilters($filters);
148
        }
149
150
        $temp = $this->createWherePart($query, $filters);
151
152
        return !empty($temp) ?
153
            $query->andWhere($temp) :
154
            $query;
155
    }
156
157
    /**
158
     * @return QueryBuilder
159
     */
160
    private function createQueryResult(): QueryBuilder
161
    {
162
        $query = clone $this->query;
163
        $query->select($this->processColumnIdentifier($query));
164
        foreach ($this->columns as $column) {
165
            $this->processColumnSelect($query, $column);
166
        }
167
168
        return $query;
169
    }
170
171
    /**
172
     * @author Mathieu Petrini <[email protected]>
173
     *
174
     * @param QueryBuilder $query
175
     * @param bool         $withAlias (optional) (default=true)
176
     *
177
     * @return string
178
     */
179
    private function processColumnIdentifier(QueryBuilder &$query, bool $withAlias = true): string
180
    {
181
        return $query->getRootAliases()[0].'.'.$this->identifier.($withAlias ? ' AS '.$this->nameIdentifier : '');
182
    }
183
184
    /**
185
     * @author Mathieu Petrini <[email protected]>
186
     *
187
     * @param QueryBuilder $query
188
     * @param Column       $column
189
     */
190
    private function processColumnSelect(QueryBuilder &$query, Column $column): void
191
    {
192
        $query->addSelect($column->getName().' AS '.$column->getAlias());
193
    }
194
195
    /**
196
     * @author Mathieu Petrini <[email protected]>
197
     *
198
     * @param QueryBuilder $query
199
     * @param int          $index
200
     * @param string       $direction
201
     *
202
     * @return Datatable
203
     */
204
    private function orderBy(QueryBuilder &$query, int $index, string $direction): self
205
    {
206
        $query->orderBy(
207
            \array_slice($this->columns, $index, 1)[0]->getAlias(),
208
            $direction
209
        );
210
211
        return $this;
212
    }
213
214
    /**
215
     * @param QueryBuilder $query
216
     * @param int          $start
217
     * @param int|null     $length
218
     *
219
     * @return Datatable
220
     */
221
    private function limit(QueryBuilder &$query, int $start, int $length = null): self
222
    {
223
        $query->setFirstResult($start)
224
            ->setMaxResults($length ?? $this->resultPerPage);
225
226
        return $this;
227
    }
228
229
    /**
230
     * @author Mathieu Petrini <[email protected]>
231
     *
232
     * @param QueryBuilder $query
233
     * @param int          $index
234
     * @param string       $direction
235
     *
236
     * @return array
237
     */
238
    private function result(
239
        QueryBuilder &$query,
240
        int $index,
241
        string $direction
242
    ): array {
243
        $this->orderBy($query, $index, $direction);
244
245
        return $query->getQuery()
246
            ->getResult();
247
    }
248
249
    /**
250
     * @author Mathieu Petrini <[email protected]>
251
     *
252
     * @param QueryBuilder $query
253
     *
254
     * @return int
255
     *
256
     * @throws \Doctrine\ORM\NonUniqueResultException
257
     */
258
    private function count(QueryBuilder $query): int
259
    {
260
        $query = clone $query;
261
262
        return (int) ($query->select('COUNT(DISTINCT '.$this->processColumnIdentifier($query, false).')')
263
            ->resetDQLPart('orderBy')
264
            ->getQuery()
265
            ->getSingleScalarResult());
266
    }
267
268
    /**
269
     * PUBLIC METHODS.
270
     */
271
272
    /**
273
     * @author Mathieu Petrini <[email protected]>
274
     *
275
     * @param array    $filters
276
     * @param int|null $length
277
     *
278
     * @return array
279
     *
280
     * @throws Exception\ResolveColumnNotHandle
281
     * @throws Exception\UnfilterableColumn
282
     * @throws Exception\WhereColumnNotHandle
283
     * @throws \Doctrine\ORM\NonUniqueResultException
284
     */
285
    public function get(
286
        array $filters,
287
        int $length = null
288
    ): array {
289
        $query = $this->createQueryResult();
290
        $this->createFoundationQuery($query, $filters);
291
292
        $data = $this->limit(
293
            $query,
294
            isset($filters['start']) ? $filters['start'] : 0,
295
            $length ?? (isset($filters['length']) ? $filters['length'] : $this->resultPerPage)
296
        )->result(
297
            $query,
298
            isset($filters['order']) && isset($filters['order'][0]) ?
299
                $filters['order'][0]['column'] :
300
                0,
301
            isset($filters['order']) && isset($filters['order'][0]) ?
302
                $filters['order'][0]['dir'] :
303
                'ASC'
304
        );
305
306
        $ret = array(
307
            'recordsTotal' => $this->count($query),
308
            'recordsFiltered' => \count($data),
309
            'data' => $data,
310
        );
311
312
        return $ret;
313
    }
314
315
    /**
316
     * GETTERS / SETTERS.
317
     */
318
319
    /**
320
     * @return string
321
     */
322
    public function getNameIdentifier(): string
323
    {
324
        return $this->nameIdentifier;
325
    }
326
327
    /**
328
     * @param string $nameIdentifier
329
     *
330
     * @return Datatable
331
     */
332
    public function setNameIdentifier(string $nameIdentifier): self
333
    {
334
        $this->nameIdentifier = $nameIdentifier;
335
336
        return $this;
337
    }
338
339
    /**
340
     * @param bool $globalSearch
341
     *
342
     * @return Datatable
343
     */
344
    public function setGlobalSearch(bool $globalSearch): self
345
    {
346
        $this->globalSearch = $globalSearch;
347
348
        return $this;
349
    }
350
}
351