Completed
Push — master ( 895c5c...49cc4e )
by Timo
11:39
created

DataTable::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 3
cts 3
cp 1
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
crap 1
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 array */
36
    private $_relations = [];
37
38
    /** @var string */
39
    private $_noDataHtml = '<div>no data</div>';
40
41
    /**
42
     * Set the base model whose data is displayed in the table.
43
     *
44
     * @param string $modelName
45
     * @param array $columns
46
     * @return $this
47
     * @throws \RuntimeException
48
     */
49 50
    public function model(string $modelName, array $columns = []) : DataTable
50
    {
51 50
        if (!\class_exists($modelName) || !\is_subclass_of($modelName, Model::class))
52
        {
53 2
            throw new RuntimeException('Class "' . $modelName . '" does not exist or is not an active record!');
54
        }
55
56 48
        $this->_queryBuilder = (new $modelName)->newQuery();
57 48
        $this->_columns = $this->_fetchColumns($columns);
58
59 48
        return $this;
60
    }
61
62
    /**
63
     * Returns an array of Column objects which may be bound to a formatter.
64
     *
65
     * @param array $columns
66
     * @return array
67
     */
68 49
    private function _fetchColumns(array $columns) : array
69
    {
70 49
        $columnModels = [];
71 49
        foreach ($columns as $column => $formatter)
72
        {
73 43
            [$column, $formatter] = $this->_setColumnFormatter($column, $formatter);
74 43
            $columnModels[] = new Column($column, $formatter);
75
        }
76
77 49
        return $columnModels;
78
    }
79
80
    /**
81
     * @param $column
82
     * @param $formatter
83
     * @return array
84
     */
85 43
    private function _setColumnFormatter($column, $formatter) : array
86
    {
87 43
        if (\is_int($column))
88
        {
89 41
            $column = $formatter;
90 41
            $formatter = null;
91
        }
92
93 43
        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
     * @return $this
124
     */
125 24
    public function addComponent(DataComponent $component) : DataTable
126
    {
127 24
        $component->init($this->_queryBuilder, $this->_columns);
128 24
        $this->_components[] = $component;
129
130 24
        return $this;
131
    }
132
133
    /**
134
     * Add a formatter for the column headers.
135
     *
136
     * @param HeaderFormatter $headerFormatter
137
     * @return DataTable
138
     */
139 12
    public function formatHeaders(HeaderFormatter $headerFormatter) : DataTable
140
    {
141 12
        $this->_headerFormatters[] = $headerFormatter;
142
143 12
        return $this;
144
    }
145
146
    /**
147
     * Add a formatter for a column.
148
     *
149
     * @param string $columnName
150
     * @param ColumnFormatter $columnFormatter
151
     * @return DataTable
152
     */
153 2
    public function formatColumn(string $columnName, ColumnFormatter $columnFormatter) : DataTable
154
    {
155
        /** @var Column $column */
156 2
        $column = \array_first(
157 2
            $this->_columns,
158
            function($index, $column) use ($columnName)
159
            {
160
                /** @var Column $column */
161 2
                return $column->getName() === $columnName;
162 2
            }
163
        );
164
165 2
        if ($column !== null)
166
        {
167 2
            $column->setFormatter($columnFormatter);
168
        }
169
170 2
        return $this;
171
    }
172
173
    /**
174
     * Add classes to the table.
175
     *
176
     * @param string $classes
177
     *
178
     * @return $this
179
     */
180 1
    public function classes(string $classes) : DataTable
181
    {
182 1
        $this->_classes = $classes;
183
184 1
        return $this;
185
    }
186
187
    /**
188
     * Add a relation to the table.
189
     *
190
     * @param array $relations
191
     * @return DataTable
192
     */
193 3
    public function with(array $relations) : DataTable
194
    {
195 3
        $this->_relations += $relations;
196
197 3
        return $this;
198
    }
199
200
    /**
201
     * Set the HTML which should be displayed when the dataset is empty.
202
     *
203
     * @param string $html
204
     * @return DataTable
205
     */
206 1
    public function noDataHtml(string $html) : DataTable
207
    {
208 1
        $this->_noDataHtml = $html;
209
210 1
        return $this;
211
    }
212
213
    /**
214
     * Set a view which should be displayed when the dataset is empty.
215
     *
216
     * @param string $viewName
217
     * @return DataTable
218
     */
219 1
    public function noDataView(string $viewName) : DataTable
220
    {
221 1
        $this->_noDataHtml = \view($viewName)->render();
222
223 1
        return $this;
224
    }
225
226
    /**
227
     * Renders the table.
228
     *
229
     * @return string
230
     * @throws \RuntimeException
231
     */
232 38
    public function render() : string
233
    {
234 38
        $data = $this->_getData();
235
236 37
        if ($data->count() === 0)
237
        {
238 4
            return $this->_noDataHtml;
239
        }
240
241 33
        $this->_initColumns();
242
243 33
        return TableRenderer::open($this->_classes) .
244 33
               TableRenderer::renderHeaders($this->_fetchHeaders(), $this->_headerFormatters) .
245 33
               TableRenderer::renderBody($data, $this->_columns) .
246 33
               TableRenderer::close();
247
    }
248
249
    /**
250
     * Get data which should be displayed in the table.
251
     *
252
     * @return Collection
253
     *
254
     * @throws \RuntimeException
255
     */
256 38
    private function _getData() : Collection
257
    {
258 38
        if ($this->_queryBuilder === null)
259
        {
260 1
            throw new RuntimeException('Unknown base model!');
261
        }
262
263 37
        $this->_addRelations();
264
265
        /** @var DataComponent $component */
266 37
        foreach ($this->_components as $component)
267
        {
268 13
            $component->transformData();
269
        }
270
271 37
        return $this->_queryBuilder->get();
272
    }
273
274 37
    private function _addRelations() : void
275
    {
276 37
        if (\count($this->_relations) > 0)
277
        {
278 3
            $this->_queryBuilder->with($this->_relations);
279
        }
280 37
    }
281
282 33
    private function _initColumns() : void
283
    {
284 33
        if (\count($this->_columns) === 0)
285
        {
286 2
            $this->_columns = $this->_fetchColumns(Schema::getColumnListing($this->_queryBuilder->getQuery()->from));
287
        }
288 33
    }
289
290
    /**
291
     * @return array
292
     */
293 33
    private function _fetchHeaders() : array
294
    {
295 33
        return \array_map(
296 33
            function($column)
297
            {
298
                /** @var Column $column */
299 33
                return new Header($column->getKey());
300 33
            },
301 33
            $this->_columns
302
        );
303
    }
304
}