Completed
Pull Request — master (#16)
by Timo
06:00
created

DataTable::_setColumnFormatter()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 10
ccs 5
cts 5
cp 1
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 5
nc 2
nop 2
crap 2
1
<?php
2
3
namespace hamburgscleanest\DataTables\Models;
4
5
use hamburgscleanest\DataTables\Facades\TableRenderer;
6
use hamburgscleanest\DataTables\Interfaces\ColumnFormatter;
7
use hamburgscleanest\DataTables\Interfaces\HeaderFormatter;
8
use Illuminate\Database\Eloquent\Builder;
9
use Illuminate\Database\Eloquent\Model;
10
use Illuminate\Support\Collection;
11
use Illuminate\Support\Facades\Schema;
12
use RuntimeException;
13
14
/**
15
 * Class DataTable
16
 * @package hamburgscleanest\DataTables\Models
17
 */
18
class DataTable {
19
20
    /** @var Builder */
21
    private $_queryBuilder;
22
23
    /** @var array */
24
    private $_headerFormatters = [];
25
26
    /** @var array */
27
    private $_components = [];
28
29
    /** @var string */
30
    private $_classes;
31
32
    /** @var array */
33
    private $_columns = [];
34
35
    /** @var Model */
36
    private $_model;
37
38
    /** @var array */
39
    private $_relations = [];
40
41
    /** @var string */
42
    private $_noDataHtml = '<div>no data</div>';
43
44
    /**
45
     * Set the base model whose data is displayed in the table.
46
     *
47
     * @param string $modelName
48
     * @param array $columns
49
     * @return $this
50
     * @throws \RuntimeException
51
     */
52 54
    public function model(string $modelName, array $columns = []) : DataTable
53
    {
54 54
        if (!\is_subclass_of($modelName, Model::class))
55
        {
56 2
            throw new RuntimeException('Class "' . $modelName . '" is not an active record!');
57
        }
58
59 52
        $this->_model = new $modelName;
60 52
        $this->_queryBuilder = $this->_model->newQuery();
61 52
        $this->_columns = $this->_fetchColumns($columns);
62
63 52
        return $this;
64
    }
65
66
    /**
67
     * Returns an array of Column objects which may be bound to a formatter.
68
     *
69
     * @param array $columns
70
     * @return array
71
     */
72 53
    private function _fetchColumns(array $columns) : array
73
    {
74 53
        $columnModels = [];
75 53
        foreach ($columns as $column => $formatter)
76
        {
77 47
            [$column, $formatter] = $this->_setColumnFormatter($column, $formatter);
78 47
            $columnModels[] = new Column($column, $formatter, $this->_model);
79
        }
80
81 53
        return $columnModels;
82
    }
83
84
    /**
85
     * @param $column
86
     * @param $formatter
87
     * @return array
88
     */
89 47
    private function _setColumnFormatter($column, $formatter) : array
90
    {
91 47
        if (\is_int($column))
92
        {
93 45
            $column = $formatter;
94 45
            $formatter = null;
95
        }
96
97 47
        return [$column, $formatter];
98
    }
99
100
    /**
101
     * @return array
102
     */
103 5
    public function getColumns() : array
104
    {
105 5
        return $this->_columns;
106
    }
107
108
    /**
109
     * @return Builder
110
     */
111 38
    public function query() : Builder
112
    {
113 38
        return $this->_queryBuilder;
114
    }
115
116
    /**
117
     * Displayed columns
118
     *
119
     * @param array $columns
120
     * @return $this
121
     */
122 3
    public function columns(array $columns) : DataTable
123
    {
124 3
        $this->_columns += $this->_fetchColumns($columns);
125
126 3
        return $this;
127
    }
128
129
    /**
130
     * Add a component to the data table.
131
     * For example a "Paginator" or a "Sorter".
132
     *
133
     * @param DataComponent $component
134
     *
135
     * @return $this
136
     */
137 28
    public function addComponent(DataComponent $component) : DataTable
138
    {
139 28
        $this->_components[] = $component->init($this);
140
141 28
        return $this;
142
    }
143
144
    /**
145
     * Add a formatter for the column headers.
146
     *
147
     * @param HeaderFormatter $headerFormatter
148
     * @return DataTable
149
     */
150 12
    public function formatHeaders(HeaderFormatter $headerFormatter) : DataTable
151
    {
152 12
        $this->_headerFormatters[] = $headerFormatter;
153
154 12
        return $this;
155
    }
156
157
    /**
158
     * Add a formatter for a column.
159
     *
160
     * @param string $columnName
161
     * @param ColumnFormatter $columnFormatter
162
     * @return DataTable
163
     */
164 2
    public function formatColumn(string $columnName, ColumnFormatter $columnFormatter) : DataTable
165
    {
166
        /** @var Column $column */
167 2
        $column = \array_first(
168 2
            $this->_columns,
169
            function($index, $column) use ($columnName) {
170
                /** @var Column $column */
171 2
                return $column->getName() === $columnName;
172 2
            }
173
        );
174
175 2
        if ($column !== null)
176
        {
177 2
            $column->setFormatter($columnFormatter);
178
        }
179
180 2
        return $this;
181
    }
182
183
    /**
184
     * Add classes to the table.
185
     *
186
     * @param string $classes
187
     *
188
     * @return $this
189
     */
190 1
    public function classes(string $classes) : DataTable
191
    {
192 1
        $this->_classes = $classes;
193
194 1
        return $this;
195
    }
196
197
    /**
198
     * Add a relation to the table.
199
     *
200
     * @param array $relations
201
     * @return DataTable
202
     */
203 4
    public function with(array $relations) : DataTable
204
    {
205 4
        $this->_relations += $relations;
206
207 4
        return $this;
208
    }
209
210
    /**
211
     * Set the HTML which should be displayed when the dataset is empty.
212
     *
213
     * @param string $html
214
     * @return DataTable
215
     */
216 1
    public function noDataHtml(string $html) : DataTable
217
    {
218 1
        $this->_noDataHtml = $html;
219
220 1
        return $this;
221
    }
222
223
    /**
224
     * Set a view which should be displayed when the dataset is empty.
225
     *
226
     * @param string $viewName
227
     * @return DataTable
228
     * @throws \Throwable
229
     */
230 1
    public function noDataView(string $viewName) : DataTable
231
    {
232 1
        $this->_noDataHtml = \view($viewName)->render();
233
234 1
        return $this;
235
    }
236
237
    /**
238
     * Renders the table.
239
     *
240
     * @return string
241
     * @throws \RuntimeException
242
     */
243 40
    public function render() : string
244
    {
245 40
        $data = $this->_getData();
246
247 39
        if ($data->count() === 0)
248
        {
249 4
            return $this->_noDataHtml;
250
        }
251
252 35
        $this->_initColumns();
253
254 35
        return TableRenderer::open($this->_classes) .
255 35
               TableRenderer::renderHeaders($this->_fetchHeaders(), $this->_headerFormatters) .
256 35
               TableRenderer::renderBody($data, $this->_columns) .
257 35
               TableRenderer::close();
258
    }
259
260
    /**
261
     * Get data which should be displayed in the table.
262
     *
263
     * @return Collection
264
     *
265
     * @throws \RuntimeException
266
     */
267 40
    private function _getData() : Collection
268
    {
269 40
        if ($this->_queryBuilder === null)
270
        {
271 1
            throw new RuntimeException('Unknown base model!');
272
        }
273
274 39
        $this->_addRelations();
275
276
        /** @var DataComponent $component */
277 39
        foreach ($this->_components as $component)
278
        {
279 15
            $component->transformData();
280
        }
281
282 39
        return $this->_setSelection()->_queryBuilder->get();
283
    }
284
285 39
    private function _addRelations() : void
286
    {
287 39
        if (\count($this->_relations) > 0)
288
        {
289 4
            foreach ($this->_relations as $relation)
290
            {
291
                /** @var \Illuminate\Database\Eloquent\Relations\Relation $relationship */
292 4
                $relationship = $this->_model->$relation();
293
294
                /** @var Model $related */
295 4
                $related = $relationship->getRelated();
296
297 4
                $this->_queryBuilder->join(
298 4
                    $related->getTable() . ' as ' . $relation,
299 4
                    $this->_model->getTable() . '.' . $this->_model->getKeyName(),
300 4
                    '=',
301 4
                    $relation . '.' . $related->getForeignKey()
302
                );
303
            }
304
        }
305 39
    }
306
307
    /**
308
     * @return DataTable
309
     */
310 39
    private function _setSelection() : DataTable
311
    {
312 39
        [$raw, $columns] = $this->_getSelectStatements();
2 ignored issues
show
Bug introduced by
The variable $raw does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
Bug introduced by
The variable $columns does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
313
314 39
        $query = $this->_queryBuilder->getQuery();
315 39
        $query->addSelect($columns);
316 39
        if (!empty($raw))
317
        {
318 9
            $query->selectRaw($raw);
319
        }
320
321 39
        return $this;
322
    }
323
324
    /**
325
     * Formats the column names to be used in a select statement.
326
     *
327
     * @return array
328
     */
329 39
    private function _getSelectStatements() : array
330
    {
331 39
        if (empty($this->_columns))
332
        {
333 5
            return ['*', []];
334
        }
335
336 34
        $raw = [];
337 34
        $columns = [];
338
339
        /** @var Column $column */
340 34
        foreach ($this->_columns as $column)
341
        {
342 34
            if ($column->getRelation() !== null)
343
            {
344 4
                $raw[] = $column->getAttributeName() . ' AS ' . $column->getKey();
345
            } else
346
            {
347 33
                $columns[] = $column->getAttributeName();
348
            }
349
        }
350
351 34
        return [\implode(',', $raw), $columns];
352
    }
353
354 35
    private function _initColumns() : void
355
    {
356 35
        if (\count($this->_columns) === 0)
357
        {
358 2
            $this->_columns = $this->_fetchColumns(Schema::getColumnListing($this->_queryBuilder->getQuery()->from));
359
        }
360 35
    }
361
362
    /**
363
     * @return array
364
     */
365 35
    private function _fetchHeaders() : array
366
    {
367 35
        return \array_map(
368 35
            function($column) {
369
                /** @var Column $column */
370 35
                return new Header($column->getKey());
371 35
            },
372 35
            $this->_columns
373
        );
374
    }
375
}