Passed
Push — master ( 62264f...226050 )
by Petr
08:16
created

Table::getOrderOrNull()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
eloc 1
dl 0
loc 3
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
crap 1
1
<?php
2
3
namespace kalanis\kw_table\core;
4
5
6
use kalanis\kw_connect\core\ConnectException;
7
use kalanis\kw_connect\core\Interfaces\IIterableConnector;
8
use kalanis\kw_paging\Interfaces\IOutput;
9
use kalanis\kw_table\core\Interfaces\Form\IField;
10
use kalanis\kw_table\core\Interfaces\Form\IFilterForm;
11
use kalanis\kw_table\core\Interfaces\Table\IColumn;
12
use kalanis\kw_table\core\Interfaces\Table\IRule;
13
14
15
/**
16
 * Class Table
17
 * @package kalanis\kw_table\core
18
 * Main table render
19
 */
20
class Table
21
{
22
    const PAGER_LIMIT_DEFAULT = 30;
23
24
    /** @var IIterableConnector|null */
25
    protected $dataSetConnector = null;
26
27
    /** @var IColumn[] */
28
    protected $columns = [];
29
30
    /** @var Table\Rows\ARow[] */
31
    protected $callRows = [];
32
33
    /** @var string[] */
34
    protected $classes = ['table', 'table-bordered', 'table-striped', 'table-hover', 'table-condensed', 'bootstrap-datatable', 'listtable'];
35
36
    /** @var IOutput|null */
37
    protected $pager = null;
38
39
    /** @var Table\Order|null */
40
    protected $order = null;
41
42
    /** @var Table\Filter|null */
43
    protected $headerFilter = null;
44
45
    /** @var Table\Filter|null */
46
    protected $footerFilter = null;
47
48
    /** @var Table\AOutput|null */
49
    protected $output = null;
50
51
    /** @var Table\Internal\Row[]|Table[] */
52
    protected $tableData = [];
53
54
    /** @var array<string, string> */
55
    protected $defaultHeaderFilterFieldAttributes = [];
56
57
    /** @var array<string, string>  */
58
    protected $defaultFooterFilterFieldAttributes = [];
59
60
    /** @var bool */
61
    protected $showPagerOnHead = false;
62
63
    /** @var bool */
64
    protected $showPagerOnFoot = true;
65
66
    /**
67
     * @param IIterableConnector|null $dataSetConnector
68
     */
69 31
    public function __construct(IIterableConnector $dataSetConnector = null)
70
    {
71 31
        if (!is_null($dataSetConnector)) {
72 5
            $this->addDataSetConnector($dataSetConnector);
73
        }
74 31
    }
75
76
    /**
77
     * Add external function to row for processing data
78
     * @param string $function
79
     * @param string[] $arguments
80
     * @return mixed|null|void
81
     */
82 1
    public function __call($function, $arguments)
83
    {
84 1
        if (preg_match('/^row(.*)$/', $function, $matches)) {
85 1
            $this->callRows[] = new Table\Rows\FunctionRow($matches[1], $arguments);
86
        }
87 1
    }
88
89
    /**
90
     * Add style class to whole row depending on the rule
91
     * @param string $class
92
     * @param IRule  $rule
93
     * @param string $cell
94
     */
95 1
    public function rowClass(string $class, IRule $rule, $cell): void
96
    {
97 1
        $this->callRows[] = new Table\Rows\ClassRow($class, $rule, $cell);
98 1
    }
99
100 11
    public function addOrder(Table\Order $order): self
101
    {
102 11
        $this->order = $order;
103 11
        return $this;
104
    }
105
106 5
    public function addHeaderFilter(IFilterForm $formConnector): self
107
    {
108 5
        $this->headerFilter = new Table\Filter($formConnector);
109 5
        return $this;
110
    }
111
112 1
    public function addFooterFilter(IFilterForm $formConnector): self
113
    {
114 1
        $this->footerFilter = new Table\Filter($formConnector);
115 1
        return $this;
116
    }
117
118
    /**
119
     * @param array<string, string> $attributes
120
     * @return $this
121
     */
122 1
    public function setDefaultHeaderFilterFieldAttributes(array $attributes): self
123
    {
124 1
        $this->defaultHeaderFilterFieldAttributes = $attributes;
125 1
        return $this;
126
    }
127
128
    /**
129
     * @param array<string, string> $attributes
130
     * @return $this
131
     */
132 1
    public function setDefaultFooterFilterFieldAttributes(array $attributes): self
133
    {
134 1
        $this->defaultFooterFilterFieldAttributes = $attributes;
135 1
        return $this;
136
    }
137
138 7
    public function addPager(IOutput $pager): self
139
    {
140 7
        $this->pager = $pager;
141 7
        return $this;
142
    }
143
144
    /**
145
     * Basic order
146
     * @param string $columnName
147
     * @param string $order
148
     * @throws TableException
149
     * @return $this
150
     */
151 9
    public function addOrdering(string $columnName, string $order = Table\Order::ORDER_ASC): self
152
    {
153 9
        $this->getOrder()->addOrdering($columnName, $order);
154 9
        return $this;
155
    }
156
157
    /**
158
     * More important order when some is already set
159
     * @param string $columnName
160
     * @param string $order
161
     * @throws TableException
162
     * @return $this
163
     */
164 1
    public function addPrimaryOrdering(string $columnName, string $order = Table\Order::ORDER_ASC): self
165
    {
166 1
        $this->getOrder()->addPrependOrdering($columnName, $order);
167 1
        return $this;
168
    }
169
170
    /**
171
     * @throws TableException
172
     * @return IOutput
173
     */
174 18
    public function getPager(): IOutput
175
    {
176 18
        if (empty($this->pager)) {
177 11
            throw new TableException('Need to set paging library first!');
178
        }
179 7
        return $this->pager;
180
    }
181
182
    /**
183
     * @return IOutput|null
184
     */
185 1
    public function getPagerOrNull(): ?IOutput
186
    {
187 1
        return $this->pager;
188
    }
189
190
    /**
191
     * @throws TableException
192
     * @return Table\Order
193
     */
194 15
    public function getOrder(): Table\Order
195
    {
196 15
        if (empty($this->order)) {
197 4
            throw new TableException('Need to set order library first!');
198
        }
199 11
        return $this->order;
200
    }
201
202
    /**
203
     * @return Table\Order|null
204
     */
205 10
    public function getOrderOrNull(): ?Table\Order
206
    {
207 10
        return $this->order;
208
    }
209
210 9
    public function getHeaderFilter(): ?Table\Filter
211
    {
212 9
        return $this->headerFilter;
213
    }
214
215 2
    public function getFooterFilter(): ?Table\Filter
216
    {
217 2
        return $this->footerFilter;
218
    }
219
220 2
    public function getFormName(): string
221
    {
222 2
        return $this->headerFilter ? $this->headerFilter->getFormName() : ( $this->footerFilter ? $this->footerFilter->getFormName() : '' );
223
    }
224
225 17
    public function setOutput(Table\AOutput $output): void
226
    {
227 17
        $this->output = $output;
228 17
    }
229
230 2
    public function getOutput(): ?Table\AOutput
231
    {
232 2
        return $this->output;
233
    }
234
235
    /**
236
     * Change data source
237
     * @param IIterableConnector $dataSetConnector
238
     * @return $this
239
     */
240 27
    public function addDataSetConnector(IIterableConnector $dataSetConnector): self
241
    {
242 27
        $this->dataSetConnector = $dataSetConnector;
243 27
        return $this;
244
    }
245
246
    /**
247
     * @throws TableException
248
     * @return IIterableConnector
249
     */
250 24
    public function getDataSetConnector(): IIterableConnector
251
    {
252 24
        if (empty($this->dataSetConnector)) {
253 1
            throw new TableException('Need to set dataset connector library first!');
254
        }
255 23
        return $this->dataSetConnector;
256
    }
257
258
    /**
259
     * Returns column to another update
260
     * @param int $position
261
     * @return IColumn|null
262
     */
263 1
    public function getColumn(int $position): ?IColumn
264
    {
265 1
        return $this->columns[$position] ?? null ;
266
    }
267
268
    /**
269
     * Return columns
270
     * @return Table\Internal\Row[]|Table[]
271
     */
272 17
    public function &getTableData(): array
273
    {
274 17
        return $this->tableData;
275
    }
276
277
    /**
278
     * @return IColumn[]
279
     */
280 17
    public function &getColumns(): array
281
    {
282 17
        return $this->columns;
283
    }
284
285
    /**
286
     * Return classes used for styles
287
     * @return string[]
288
     */
289 1
    public function &getClasses(): array
290
    {
291 1
        return $this->classes;
292
    }
293
294 1
    public function getClassesInString(): string
295
    {
296 1
        if (!empty($this->classes)) {
297 1
            return implode(' ', $this->classes);
298
        } else {
299 1
            return '';
300
        }
301
    }
302
303
    /**
304
     * @param string  $headerText
305
     * @param IColumn $column
306
     * @param IField|null $headerFilterField
307
     * @param IField|null $footerFilterField
308
     * @throws TableException
309
     * @return $this
310
     */
311 10
    public function addOrderedColumn(string $headerText, IColumn $column, ?IField $headerFilterField = null, ?IField $footerFilterField = null): self
312
    {
313 10
        if ($column->canOrder()) {
314 10
            $this->getOrder()->addColumn($column);
315
        }
316
317 9
        $this->addColumn($headerText, $column, $headerFilterField, $footerFilterField);
318
319 9
        return $this;
320
    }
321
322
    /**
323
     * @param string $headerText
324
     * @param IColumn $column
325
     * @param IField|null $headerFilterField
326
     * @param IField|null $footerFilterField
327
     * @throws TableException
328
     * @return $this
329
     */
330 26
    public function addColumn(string $headerText, IColumn $column, ?IField $headerFilterField = null, ?IField $footerFilterField = null): self
331
    {
332 26
        $column->setHeaderText($headerText);
333
334 26
        if (isset($headerFilterField)) {
335 6
            $headerFilterField->setAttributes($this->defaultHeaderFilterFieldAttributes);
336 6
            $column->setHeaderFiltering($headerFilterField);
337
        }
338
339 26
        if ($column->hasHeaderFilterField() && $this->headerFilter) {
340 5
            $this->headerFilter->addHeaderColumn($column);
341
        }
342
343 26
        if (isset($footerFilterField)) {
344 2
            $footerFilterField->setAttributes($this->defaultFooterFilterFieldAttributes);
345 2
            $column->setFooterFiltering($footerFilterField);
346
        }
347
348 26
        if ($column->hasFooterFilterField() && $this->footerFilter) {
349 1
            $this->footerFilter->addFooterColumn($column);
350
        }
351
352 26
        $this->columns[] = $column;
353
354 26
        return $this;
355
    }
356
357
    /**
358
     * Add Css class to the table
359
     * @param string $class
360
     */
361 1
    public function addClass(string $class): void
362
    {
363 1
        $this->classes[] = $class;
364 1
    }
365
366
    /**
367
     * Remover Css class from the table
368
     * @param string $class
369
     */
370 1
    public function removeClass($class): void
371
    {
372 1
        if (($key = array_search($class, $this->classes)) !== false) {
373 1
            unset($this->classes[$key]);
374
        }
375 1
    }
376
377
    /**
378
     * Render complete table - just helper method
379
     * @throws ConnectException
380
     * @throws TableException
381
     * @return string
382
     */
383 20
    public function render(): string
384
    {
385 20
        $this->translateData();
386 18
        if (!$this->output) {
387 1
            throw new TableException('Need to set output first!');
388
        }
389 17
        return $this->output->render();
390
    }
391
392
    /**
393
     * Update columns to readable format
394
     * @throws ConnectException
395
     * @throws TableException
396
     */
397 25
    public function translateData(): void
398
    {
399 25
        if (is_null($this->dataSetConnector)) {
400 1
            throw new TableException('Cannot create table from empty dataset');
401
        }
402
403 24
        if (empty($this->columns)) {
404 1
            throw new TableException('You need to define at least one column');
405
        }
406
407 23
        $this->applyFilter();
408 23
        $this->applyOrder();
409 23
        $this->applyPager();
410
411 23
        $this->getDataSetConnector()->fetchData();
412
413 23
        foreach ($this->getDataSetConnector() as $source) {
414 21
            $rowData = new Table\Internal\Row();
415 21
            $rowData->setSource($source);
416
417 21
            foreach ($this->callRows as $call) {
418 1
                call_user_func_array([$rowData, $call->getFunctionName()], $call->getFunctionArgs());
419
            }
420
421 21
            foreach ($this->columns as $column) {
422 21
                $col = clone $column;
423 21
                $rowData->addColumn($col);
424
            }
425
426 21
            $this->tableData[] = $rowData;
427
        }
428 23
    }
429
430
    /**
431
     * @throws ConnectException
432
     * @throws TableException
433
     * @return $this
434
     */
435 23
    protected function applyFilter(): self
436
    {
437 23
        if (empty($this->headerFilter)) {
438 18
            return $this;
439
        }
440
441 5
        $this->headerFilter->process();
442
443 5
        foreach ($this->columns as $column) {
444 5
            if ($this->headerFilter->hasValue($column)) {
445
446 5
                $filterField = $column->getHeaderFilterField();
447 5
                if ($filterField) {
448 5
                    $filterField->setDataSourceConnector($this->getDataSetConnector());
449 5
                    $this->getDataSetConnector()->setFiltering(
450 5
                        $column->getSourceName(),
451 5
                        $filterField->getFilterAction(),
452 5
                        $this->headerFilter->getValue($column)
453
                    );
454
                }
455
            }
456
        }
457 5
        return $this;
458
    }
459
460
    /**
461
     * @throws ConnectException
462
     * @throws TableException
463
     * @return $this
464
     */
465 23
    protected function applyOrder(): self
466
    {
467 23
        if (empty($this->order)) {
468 12
            return $this;
469
        }
470
471 11
        $this->getOrder()->process();
472 11
        foreach ($this->getOrder()->getOrdering() as $attributes) {
473
            /** @var Table\Internal\Attributes $attributes */
474 11
            $this->getDataSetConnector()->setOrdering($attributes->getColumnName(), $attributes->getProperty());
475
        }
476 11
        return $this;
477
    }
478
479
    /**
480
     * @throws ConnectException
481
     * @throws TableException
482
     * @return $this
483
     */
484 23
    protected function applyPager(): self
485
    {
486 23
        if (empty($this->pager)) {
487 16
            return $this;
488
        }
489
490 7
        if (empty($this->getPager()->getPager()->getMaxResults())) {
491 7
            $this->getPager()->getPager()->setMaxResults($this->getDataSetConnector()->getTotalCount());
492
        }
493 7
        $this->getDataSetConnector()->setPagination(
494 7
            $this->getPager()->getPager()->getOffset(),
495 7
            $this->getPager()->getPager()->getLimit()
496
        );
497 7
        return $this;
498
    }
499
500 3
    public function rowCount(): int
501
    {
502 3
        return count($this->tableData);
503
    }
504
505 3
    public function colCount(): int
506
    {
507 3
        return count($this->columns);
508
    }
509
510 1
    public function showPagerOnHead(): bool
511
    {
512 1
        return $this->showPagerOnHead;
513
    }
514
515 1
    public function showPagerOnFoot(): bool
516
    {
517 1
        return $this->showPagerOnFoot;
518
    }
519
}
520