Completed
Push — master ( 597177...94412d )
by Timo
04:45
created

DataTable::formatColumn()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 19
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 2

Importance

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