Completed
Push — master ( 0a0c13...17c496 )
by Timo
06:16
created

DataTable::_renderHeaders()   B

Complexity

Conditions 3
Paths 4

Size

Total Lines 28
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 3

Importance

Changes 0
Metric Value
dl 0
loc 28
ccs 14
cts 14
cp 1
rs 8.8571
c 0
b 0
f 0
cc 3
eloc 13
nc 4
nop 0
crap 3
1
<?php
2
3
namespace hamburgscleanest\DataTables\Models;
4
5
use Closure;
6
use hamburgscleanest\DataTables\Interfaces\HeaderFormatter;
7
use Illuminate\Database\Eloquent\Builder;
8
use Illuminate\Database\Eloquent\Model;
9
use Illuminate\Http\Request;
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 Request */
21
    private $_request;
22
23
    /** @var Builder */
24
    private $_queryBuilder;
25
26
    /** @var array */
27
    private $_headerFormatters = [];
28
29
    /** @var array */
30
    private $_components = [];
31
32
    /** @var Closure */
33
    private $_rowRenderer; // TODO: IColumnFormatter => DateColumnFormatter etc.
34
35
    /** @var string */
36
    private $_classes;
37
38
    /** @var array */
39
    private $_columns = [];
40
41
    /** @var array */
42
    private $_relations = [];
43
44
    /**
45
     * DataTable constructor.
46
     * @param Request $request
47
     */
48 22
    public function __construct(Request $request)
49
    {
50 22
        $this->_request = $request;
51 22
    }
52
53
    /**
54
     * Set the base model whose data is displayed in the table.
55
     *
56
     * @param string $modelName
57
     * @param array $columns
58
     * @return $this
59
     * @throws \RuntimeException
60
     */
61 21
    public function model(string $modelName, array $columns = [])
62
    {
63 21
        if (!\class_exists($modelName))
64
        {
65 1
            throw new RuntimeException('Class "' . $modelName . '" does not exist!');
66
        }
67
68 20
        if (!\is_subclass_of($modelName, Model::class))
69
        {
70 1
            throw new RuntimeException('"' . $modelName . '" is not an active record!');
71
        }
72
73 19
        $this->_queryBuilder = (new $modelName)->newQuery();
74 19
        $this->_columns = $columns;
75
76 19
        return $this;
77
    }
78
79
    /**
80
     * @return Builder
81
     */
82 11
    public function query()
83
    {
84 11
        return $this->_queryBuilder;
85
    }
86
87
    /**
88
     * Displayed columns
89
     *
90
     * @param array $columns
91
     * @return $this
92
     */
93 1
    public function columns(array $columns)
94
    {
95 1
        $this->_columns += $columns;
96
97 1
        return $this;
98
    }
99
100
    /**
101
     * Manipulate each rendered row.
102
     *
103
     * @param Closure $customRowRenderer
104
     * @return $this
105
     */
106 1
    public function renderRow(Closure $customRowRenderer)
107
    {
108 1
        $this->_rowRenderer = $customRowRenderer;
109
110 1
        return $this;
111
    }
112
113
    /**
114
     * Add a component to the data table.
115
     * For example a "Paginator" or a "Sorter".
116
     *
117
     * @param DataComponent $component
118
     *
119
     * @return $this
120
     */
121 8
    public function addComponent(DataComponent $component)
122
    {
123 8
        $component->init($this->_queryBuilder, $this->_request);
124 8
        $this->_components[] = $component;
125
126 8
        return $this;
127
    }
128
129
    /**
130
     * Add a formatter for the column headers.
131
     *
132
     * @param HeaderFormatter $headerFormatter
133
     * @return $this
134
     */
135 5
    public function formatHeaders(HeaderFormatter $headerFormatter)
136
    {
137 5
        $this->_headerFormatters[] = $headerFormatter;
138
139 5
        return $this;
140
    }
141
142
    /**
143
     * Get data which should be displayed in the table.
144
     *
145
     * @return \Illuminate\Database\Eloquent\Collection
146
     *
147
     * @throws \RuntimeException
148
     */
149 16
    private function _getData()
150
    {
151 16
        if ($this->_queryBuilder === null)
152
        {
153 1
            throw new RuntimeException('Unknown base model!');
154
        }
155
156 15
        if (\count($this->_relations) > 0)
157
        {
158
            $this->_queryBuilder->with($this->_relations);
159
        }
160
161
        /** @var DataComponent $component */
162 15
        foreach ($this->_components as $component)
163
        {
164 4
            $component->transformData();
165
        }
166
167 15
        return $this->_queryBuilder->get();
168
    }
169
170
    /**
171
     * Add classes to the table.
172
     *
173
     * @param string $classes
174
     *
175
     * @return $this
176
     */
177 1
    public function classes(string $classes)
178
    {
179 1
        $this->_classes = $classes;
180
181 1
        return $this;
182
    }
183
184
    /**
185
     * Renders the column headers.
186
     *
187
     * @return string
188
     */
189 13
    private function _renderHeaders()
190
    {
191 13
        if (\count($this->_columns) === 0)
192
        {
193 1
            $this->_columns = Schema::getColumnListing($this->_queryBuilder->getQuery()->from);
194
        }
195
196 13
        $headers = array_map(
197 13
            function($name)
198
            {
199 13
                return new Header($name);
200 13
            },
201 13
            $this->_columns
202
        );
203
204 13
        $html = '<tr>';
205
206
        /** @var Header $header */
207 13
        foreach ($headers as $header)
208
        {
209 13
            $header->formatArray($this->_headerFormatters, $this->_request);
210
211 13
            $html .= '<th>' . $header->name . '</th>';
212
        }
213 13
        $html .= '</tr>';
214
215 13
        return $html;
216
    }
217
218
    /**
219
     * Displays the table body.
220
     *
221
     * @param Collection $data
222
     *
223
     * @return string
224
     */
225 13
    private function _renderBody(Collection $data)
226
    {
227 13
        $html = '';
228 13
        foreach ($data as $row)
229
        {
230 13
            $html .= $this->_renderRow($row);
231
        }
232
233 13
        return $html;
234
    }
235
236
    /**
237
     * Get all the mutated attributes which are needed.
238
     *
239
     * @param Model $model
240
     * @param array $columns
241
     * @return array
242
     */
243 13
    private function _getMutatedAttributes(Model $model, array $columns = []): array
244
    {
245 13
        $attributes = [];
246 13
        foreach (\array_intersect_key($model->getMutatedAttributes(), $columns) as $attribute)
247
        {
248 13
            $attributes[$attribute] = $model->{$attribute};
249
        }
250
251 13
        return $attributes;
252
    }
253
254
    /**
255
     * Displays a single row.
256
     *
257
     * @param Model $rowModel
258
     *
259
     * @return string
260
     */
261 13
    private function _renderRow(Model $rowModel)
262
    {
263 13
        if ($this->_rowRenderer !== null)
264
        {
265 1
            $rowModel = $this->_rowRenderer->call($this, $rowModel);
266
        }
267
268 13
        $attributes = $rowModel->getAttributes() + $this->_getMutatedAttributes($rowModel, $this->_columns);
269
270 13
        $html = '<tr>';
271 13
        foreach ($this->_columns as $column)
272
        {
273 13
            $html .= '<td>' . ($attributes[$column] ?? '') . '</td>';
274
        }
275 13
        $html .= '</tr>';
276
277 13
        return $html;
278
    }
279
280
    /**
281
     * Add a relation to the table.
282
     *
283
     * @param array $relations
284
     * @return $this
285
     */
286
    public function with(array $relations)
287
    {
288
        $this->_relations += $relations;
289
290
        return $this;
291
    }
292
293
    /**
294
     * Starts the table.
295
     *
296
     * @return string
297
     */
298 13
    private function _open()
299
    {
300 13
        return '<table class="' . ($this->_classes ?? 'table') . '">';
301
    }
302
303
    /**
304
     * Closes the table.
305
     *
306
     * @return string
307
     */
308 13
    private function _close()
309
    {
310 13
        return '</table>';
311
    }
312
313
    /**
314
     * Renders the table.
315
     *
316
     * @param null|Closure $noDataView
317
     *
318
     * @return string
319
     * @throws \RuntimeException
320
     */
321 16
    public function render(Closure $noDataView = null)
322
    {
323 16
        $data = $this->_getData();
324
325 15
        if ($data->count() === 0)
326
        {
327 2
            return $noDataView !== null ? $noDataView->call($this) : '<div>no data</div>';
328
        }
329
330 13
        return $this->_open() . $this->_renderHeaders() . $this->_renderBody($data) . $this->_close();
331
    }
332
}