Test Failed
Push — master ( 9793d0...ce5c6f )
by Mathieu
04:47 queued 11s
created

Datatable::get()   A

Complexity

Conditions 3
Paths 1

Size

Total Lines 19
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 13
dl 0
loc 19
rs 9.8333
c 0
b 0
f 0
cc 3
nc 1
nop 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 string
17
     */
18
    private $identifier;
19
20
    /**
21
     * @var Column[]
22
     */
23
    private $columns;
24
25
    /**
26
     * @var int
27
     */
28
    private $resultPerPage;
29
30
    /**
31
     * @var string
32
     */
33
    private $nameIdentifier;
34
35
    /**
36
     * @var bool
37
     */
38
    private $globalSearch;
39
40
    /**
41
     * @var QueryBuilder
42
     */
43
    protected $query;
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 ($filters['columns'] 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 = isset($filters['columns']) ?
151
            $this->createWherePart($query, $filters) :
152
            '';
153
154
        return !empty($temp) ?
155
            $query->andWhere($temp) :
156
            $query;
157
    }
158
159
    /**
160
     * @return QueryBuilder
161
     */
162
    private function createQueryResult(): QueryBuilder
163
    {
164
        $query = clone $this->query;
165
        $query->select($this->processColumnIdentifier($query));
166
        foreach ($this->columns as $column) {
167
            $this->processColumnSelect($query, $column);
168
        }
169
170
        return $query;
171
    }
172
173
    /**
174
     * @author Mathieu Petrini <[email protected]>
175
     *
176
     * @param QueryBuilder $query
177
     * @param bool         $withAlias (optional) (default=true)
178
     *
179
     * @return string
180
     */
181
    private function processColumnIdentifier(QueryBuilder &$query, bool $withAlias = true): string
182
    {
183
        return $query->getRootAliases()[0].'.'.$this->identifier.($withAlias ? ' AS '.$this->nameIdentifier : '');
184
    }
185
186
    /**
187
     * @author Mathieu Petrini <[email protected]>
188
     *
189
     * @param QueryBuilder $query
190
     * @param Column       $column
191
     */
192
    private function processColumnSelect(QueryBuilder &$query, Column $column): void
193
    {
194
        $query->addSelect($column->getName().' AS '.$column->getAlias());
195
    }
196
197
    /**
198
     * @author Mathieu Petrini <[email protected]>
199
     *
200
     * @param QueryBuilder $query
201
     * @param int          $index
202
     * @param string       $direction
203
     *
204
     * @return Datatable
205
     */
206
    private function orderBy(QueryBuilder &$query, int $index, string $direction): self
207
    {
208
        $query->orderBy(
209
            \array_slice($this->columns, $index, 1)[0]->getAlias(),
210
            $direction
211
        );
212
213
        return $this;
214
    }
215
216
    /**
217
     * @param QueryBuilder $query
218
     * @param array        $filters
219
     *
220
     * @return Datatable
221
     */
222
    private function limit(QueryBuilder &$query, array $filters): self
223
    {
224
        $query->setFirstResult($filters['start'] ?? 0)
225
            ->setMaxResults($filters['length'] ?? $this->resultPerPage);
226
227
        return $this;
228
    }
229
230
    /**
231
     * @author Mathieu Petrini <[email protected]>
232
     *
233
     * @param QueryBuilder $query
234
     * @param int          $index
235
     * @param string       $direction
236
     *
237
     * @return array
238
     */
239
    private function result(
240
        QueryBuilder &$query,
241
        int $index,
242
        string $direction
243
    ): array {
244
        $this->orderBy($query, $index, $direction);
245
246
        return $query->getQuery()
247
            ->getResult();
248
    }
249
250
    /**
251
     * @author Mathieu Petrini <[email protected]>
252
     *
253
     * @param QueryBuilder $query
254
     *
255
     * @return int
256
     *
257
     * @throws \Doctrine\ORM\NonUniqueResultException
258
     */
259
    private function count(QueryBuilder $query): int
260
    {
261
        $query = clone $query;
262
263
        return (int) ($query->select('COUNT(DISTINCT '.$this->processColumnIdentifier($query, false).')')
264
            ->resetDQLPart('orderBy')
265
            ->getQuery()
266
            ->getSingleScalarResult());
267
    }
268
269
    /**
270
     * PUBLIC METHODS.
271
     */
272
273
    /**
274
     * @author Mathieu Petrini <[email protected]>
275
     *
276
     * @param array $filters
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(array $filters): array
286
    {
287
        $query = $this->createQueryResult();
288
        $this->createFoundationQuery($query, $filters);
289
290
        $data = $this->limit($query, $filters)->result(
291
            $query,
292
            isset($filters['order']) ?
293
                $filters['order'][0]['column'] :
294
                0,
295
            isset($filters['order']) ?
296
                $filters['order'][0]['dir'] :
297
                'ASC'
298
        );
299
300
        return array(
301
            'recordsTotal' => $this->count($query),
302
            'recordsFiltered' => \count($data),
303
            'data' => $data,
304
        );
305
    }
306
307
    /**
308
     * GETTERS / SETTERS.
309
     */
310
311
    /**
312
     * @return string
313
     */
314
    public function getNameIdentifier(): string
315
    {
316
        return $this->nameIdentifier;
317
    }
318
319
    /**
320
     * @param string $nameIdentifier
321
     *
322
     * @return Datatable
323
     */
324
    public function setNameIdentifier(string $nameIdentifier): self
325
    {
326
        $this->nameIdentifier = $nameIdentifier;
327
328
        return $this;
329
    }
330
331
    /**
332
     * @param bool $globalSearch
333
     *
334
     * @return Datatable
335
     */
336
    public function setGlobalSearch(bool $globalSearch): self
337
    {
338
        $this->globalSearch = $globalSearch;
339
340
        return $this;
341
    }
342
}
343