Completed
Push — master ( d97b85...f43d1a )
by Timo
07:11
created

DataTable::addComponent()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 12
ccs 6
cts 6
cp 1
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 6
nc 2
nop 2
crap 2
1
<?php
2
3
namespace hamburgscleanest\DataTables\Models;
4
5
use hamburgscleanest\DataTables\Exceptions\ColumnNotFoundException;
6
use hamburgscleanest\DataTables\Exceptions\MultipleComponentAssertionException;
7
use hamburgscleanest\DataTables\Exceptions\NotAnActiveRecordException;
8
use hamburgscleanest\DataTables\Exceptions\UnknownBaseModelException;
9
use hamburgscleanest\DataTables\Facades\TableRenderer;
10
use hamburgscleanest\DataTables\Interfaces\ColumnFormatter;
11
use hamburgscleanest\DataTables\Interfaces\HeaderFormatter;
12
use hamburgscleanest\DataTables\Models\Cache\Cache;
13
use hamburgscleanest\DataTables\Models\Cache\NoCache;
14
use hamburgscleanest\DataTables\Models\Column\Column;
15
use hamburgscleanest\DataTables\Models\Column\ColumnCollection;
16
use Illuminate\Database\Eloquent\Builder;
17
use Illuminate\Database\Eloquent\Model;
18
use Illuminate\Support\Collection;
19
20
/**
21
 * Class DataTable
22
 * @package hamburgscleanest\DataTables\Models
23
 */
24
class DataTable {
25
26
    /** @var Builder */
27
    private $_queryBuilder;
28
29
    /** @var array */
30
    private $_headerFormatters = [];
31
32
    /** @var array */
33
    private $_components = [];
34
35
    /** @var string */
36
    private $_classes;
37
38
    /** @var ColumnCollection */
39
    private $_columns;
40
41
    /** @var Model */
42
    private $_model;
43
44
    /** @var array */
45
    private $_relations = [];
46
47
    /** @var string */
48
    private $_noDataHtml = '<div>no data</div>';
49
50
    /** @var Cache */
51
    private $_cache;
52
53
    /**
54
     * Set the base model whose data is displayed in the table.
55
     *
56
     * @param string $modelName
57
     * @param array $columns
58
     * @param Cache|null $cache
59
     * @return DataTable
60
     * @throws NotAnActiveRecordException
61
     */
62 72
    public function model(string $modelName, array $columns = [], Cache $cache = null) : DataTable
63
    {
64 72
        if (!\is_subclass_of($modelName, Model::class))
65
        {
66 2
            throw new NotAnActiveRecordException($modelName);
67
        }
68
69 70
        $this->_model = new $modelName;
70 70
        $this->_queryBuilder = $this->_model->newQuery();
71 70
        $this->_columns = new ColumnCollection($columns, $this->_model);
72 70
        $this->_cache = $cache ?? new NoCache();
73
74 70
        return $this;
75
    }
76
77
    /**
78
     * @param Cache $cache
79
     * @return DataTable
80
     */
81 1
    public function cache(Cache $cache) : DataTable
82
    {
83 1
        $this->_cache = $cache;
84
85 1
        return $this;
86
    }
87
88
    /**
89
     * @return ColumnCollection
90
     */
91 5
    public function getColumns() : ColumnCollection
92
    {
93 5
        return $this->_columns;
94
    }
95
96
    /**
97
     * @return Builder
98
     */
99 51
    public function query() : Builder
100
    {
101 51
        return $this->_queryBuilder;
102
    }
103
104
    /**
105
     * Displayed columns
106
     *
107
     * @param array $columns
108
     * @return $this
109
     * @throws UnknownBaseModelException
110
     */
111 3
    public function columns(array $columns) : DataTable
112
    {
113 3
        if ($this->_columns === null)
114
        {
115 1
            throw new UnknownBaseModelException();
116
        }
117
118 2
        $this->_columns->push($columns);
119
120 2
        return $this;
121
    }
122
123
    /**
124
     * Add a component to the data table.
125
     * For example a "Paginator" or a "Sorter".
126
     *
127
     * @param DataComponent $component
128
     *
129
     * @param null|string $name
130
     * @return DataTable
131
     * @throws MultipleComponentAssertionException
132
     */
133 33
    public function addComponent(DataComponent $component, ? string $name = null) : DataTable
134
    {
135 33
        $componentName = $this->_getComponentName($component, $name);
136 33
        if ($this->componentExists($componentName))
137
        {
138 1
            throw new MultipleComponentAssertionException();
139
        }
140
141 33
        $this->_components[$componentName] = $component->init($this);
142
143 33
        return $this;
144
    }
145
146
    /**
147
     * @param DataComponent $component
148
     * @param null|string $name
149
     * @return string
150
     */
151 33
    private function _getComponentName(DataComponent $component, string $name = null) : string
152
    {
153 33
        if ($name !== null)
154
        {
155 2
            return \str_replace(' ', '', \mb_strtolower($name));
156
        }
157
158 31
        return $component->getName();
159
    }
160
161
    /**
162
     * Check whether a component exists for the given data table.
163
     * @param string $componentName
164
     * @return bool
165
     */
166 33
    public function componentExists(string $componentName) : bool
167
    {
168 33
        return \array_key_exists($componentName, $this->_components);
169
    }
170
171
    /**
172
     * Add a formatter for the column headers.
173
     *
174
     * @param HeaderFormatter $headerFormatter
175
     * @return DataTable
176
     */
177 12
    public function formatHeaders(HeaderFormatter $headerFormatter) : DataTable
178
    {
179 12
        $this->_headerFormatters[] = $headerFormatter;
180
181 12
        return $this;
182
    }
183
184
    /**
185
     * Add a formatter for a column.
186
     *
187
     * @param string $columnName
188
     * @param ColumnFormatter $columnFormatter
189
     * @return DataTable
190
     * @throws ColumnNotFoundException
191
     */
192 11
    public function formatColumn(string $columnName, ColumnFormatter $columnFormatter) : DataTable
193
    {
194
        /** @var Column $column */
195
        $column = $this->_columns->first(function($column) use ($columnName) {
196
            /** @var Column $column */
197 11
            return $column->getName() === $columnName;
198 11
        });
199
200 11
        if ($column === null)
201
        {
202 1
            throw new ColumnNotFoundException($columnName);
203
        }
204
205 10
        $column->setFormatter($columnFormatter);
206
207 10
        return $this;
208
    }
209
210
    /**
211
     * Add classes to the table.
212
     *
213
     * @param string $classes
214
     *
215
     * @return $this
216
     */
217 1
    public function classes(string $classes) : DataTable
218
    {
219 1
        $this->_classes = $classes;
220
221 1
        return $this;
222
    }
223
224
    /**
225
     * Add a relation to the table.
226
     *
227
     * @param array $relations
228
     * @return DataTable
229
     */
230 4
    public function with(array $relations) : DataTable
231
    {
232 4
        $this->_relations += Relationship::createFromArray($this->_model, $relations);
233
234 4
        return $this;
235
    }
236
237
    /**
238
     * Set the HTML which should be displayed when the dataset is empty.
239
     *
240
     * @param string $html
241
     * @return DataTable
242
     */
243 1
    public function noDataHtml(string $html) : DataTable
244
    {
245 1
        $this->_noDataHtml = $html;
246
247 1
        return $this;
248
    }
249
250
    /**
251
     * Set a view which should be displayed when the dataset is empty.
252
     *
253
     * @param string $viewName
254
     * @return DataTable
255
     * @throws \Throwable
256
     */
257 1
    public function noDataView(string $viewName) : DataTable
258
    {
259 1
        $this->_noDataHtml = \view($viewName)->render();
260
261 1
        return $this;
262
    }
263
264
    /**
265
     * Renders the table.
266
     *
267
     * @return string
268
     * @throws UnknownBaseModelException
269
     */
270 51
    public function render() : string
271
    {
272 51
        if ($this->_queryBuilder === null)
273
        {
274
            throw new UnknownBaseModelException();
275
        }
276
277
        /** @var Collection $data */
278
        $data = $this->_cache->retrieve(function() {
279 51
            return $this->_getData();
280 51
        });
281
282 51
        if ($data->count() === 0)
283
        {
284 5
            return $this->_noDataHtml;
285
        }
286
287 46
        return TableRenderer::open($this->_classes) .
288 46
               TableRenderer::renderHeaders($this->_columns->getHeaders(), $this->_headerFormatters) .
289 46
               TableRenderer::renderBody($data, $this->_columns->toArray()) .
290 46
               TableRenderer::close();
291
    }
292
293
    /**
294
     * Get data which should be displayed in the table.
295
     *
296
     * @return Collection
297
     */
298 51
    private function _getData() : Collection
299
    {
300 51
        $this->_addRelations();
301
302
        /** @var DataComponent $component */
303 51
        foreach ($this->_components as $component)
304
        {
305 18
            $component->transformData();
306
        }
307
308 51
        return $this->_setSelection()->_queryBuilder->get();
309
    }
310
311 51
    private function _addRelations() : void
312
    {
313 51
        if (\count($this->_relations) === 0)
314
        {
315 47
            return;
316
        }
317
318
        /** @var Relationship $relation */
319 4
        foreach ($this->_relations as $relation)
320
        {
321 4
            $relation->addJoin($this->_queryBuilder);
322
        }
323
324 4
        $this->_queryBuilder->getQuery()->groupBy($this->_model->getTable() . '.' . $this->_model->getKeyName());
325 4
    }
326
327
    /**
328
     * @return DataTable
329
     */
330 51
    private function _setSelection() : DataTable
331
    {
332 51
        $query = $this->_queryBuilder->getQuery();
333
334 51
        $columns = $this->_columns->getUnmutatedColumns();
335 51
        if (!empty($columns))
336
        {
337 45
            $query->selectRaw(
338 45
                \implode(',',
339 45
                    \array_map(function($column) {
340
                        /** @var Column $column */
341 45
                        return $column->getIdentifier();
342 45
                    }, $columns)
343
                )
344
            );
345
        }
346
347 51
        return $this;
348
    }
349
350
    /**
351
     * @param $name
352
     * @return mixed
353
     */
354 2
    public function __get($name)
355
    {
356 2
        if (\array_key_exists($name, $this->_components))
357
        {
358 2
            return $this->_components[$name];
359
        }
360
361
        return $this->{$name};
362
    }
363
}