Completed
Branch feature-direct_component_acces... (d14e40)
by Timo
03:34
created

DataTable::__get()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 2.1481

Importance

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

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
311
    {
312 2
        if (\array_key_exists($name, $this->_components)) return $this->_components[$name];
313
314
        return $this->$name;
315
    }
316
}