Completed
Push — master ( 6a8432...b81c5a )
by Adam
05:46
created

Grid   B

Complexity

Total Complexity 48

Size/Duplication

Total Lines 468
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 15

Importance

Changes 0
Metric Value
dl 0
loc 468
rs 7.3961
c 0
b 0
f 0
wmc 48
lcom 1
cbo 15

31 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 1
A buildGrid() 0 4 1
A getGridHelper() 0 4 1
A setSource() 0 6 1
A addColumn() 0 13 2
A getColumns() 0 4 1
A setDefaultOrder() 0 6 1
A getOrder() 0 4 1
A setPerPage() 0 6 1
A getPerPage() 0 4 1
A getEmptyMessage() 0 4 1
A setEmptyMessage() 0 6 1
A addRowAction() 0 7 1
A setEnablePagination() 0 7 1
A isPaginationEnabled() 0 4 1
A each() 0 4 1
A after() 0 6 1
A addComponent() 0 7 1
A setViewData() 0 6 1
A render() 0 17 2
A isFilterable() 0 13 3
C getRows() 0 53 10
A getPaginator() 0 6 1
A execute() 0 12 2
A resolveCurrentPage() 0 4 1
A resolveCurrentPath() 0 4 1
A getValidatorRules() 0 15 3
A makeDefaultOrder() 0 6 2
A makeColumn() 0 6 1
A setupColumnOptions() 0 6 1
A __toString() 0 4 1

How to fix   Complexity   

Complex Class

Complex classes like Grid often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Grid, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Boduch\Grid;
4
5
use Boduch\Grid\Components\Component;
6
use Boduch\Grid\Components\RowAction;
7
use Boduch\Grid\Source\SourceInterface;
8
use Illuminate\Pagination\LengthAwarePaginator;
9
use Illuminate\Pagination\Paginator;
10
11
class Grid
12
{
13
    /**
14
     * @var string
15
     */
16
    protected $template = 'laravel-grid::grid';
17
18
    /**
19
     * @var GridHelper
20
     */
21
    protected $gridHelper;
22
23
    /**
24
     * @var SourceInterface
25
     */
26
    protected $source;
27
28
    /**
29
     * @var Column[]
30
     */
31
    protected $columns = [];
32
33
    /**
34
     * @var int
35
     */
36
    protected $perPage = 15;
37
38
    /**
39
     * @var string
40
     */
41
    protected $emptyMessage = 'Brak danych do wyświetlenia.';
42
43
    /**
44
     * @var Rows
45
     */
46
    protected $rows;
47
48
    /**
49
     * Total number of records.
50
     *
51
     * @var int
52
     */
53
    protected $total = 0;
54
55
    /**
56
     * @var RowAction[]
57
     */
58
    protected $rowActions = [];
59
60
    /**
61
     * @var array
62
     */
63
    protected $defaultOrder = [
64
        'column' => 'id',
65
        'direction' => 'desc'
66
    ];
67
68
    /**
69
     * @var Order
70
     */
71
    protected $order;
72
73
    /**
74
     * @var bool
75
     */
76
    protected $enablePagination = true;
77
78
    /**
79
     * @var callable
80
     */
81
    protected $afterCallback;
82
83
    /**
84
     * @var array
85
     */
86
    protected $viewData = [];
87
88
    /**
89
     * @param GridHelper $gridHelper
90
     */
91
    public function __construct(GridHelper $gridHelper)
92
    {
93
        $this->gridHelper = $gridHelper;
94
95
        $this->makeDefaultOrder();
96
    }
97
98
    public function buildGrid()
99
    {
100
        //
101
    }
102
103
    /**
104
     * @return GridHelper
105
     */
106
    public function getGridHelper()
107
    {
108
        return $this->gridHelper;
109
    }
110
111
    /**
112
     * @param SourceInterface $source
113
     * @return $this
114
     */
115
    public function setSource(SourceInterface $source)
116
    {
117
        $this->source = $source;
118
119
        return $this;
120
    }
121
122
    /**
123
     * @param string $name
124
     * @param array $options
125
     * @return $this
126
     */
127
    public function addColumn($name, array $options = [])
128
    {
129
        if ($name instanceof Column) {
130
            $column = $name;
131
        } else {
132
            $column = $this->makeColumn($name, $options);
133
        }
134
135
        $column->setGrid($this);
136
        $this->columns[$column->getName()] = $column;
137
138
        return $this;
139
    }
140
141
    /**
142
     * @return Column[]
143
     */
144
    public function getColumns()
145
    {
146
        return $this->columns;
147
    }
148
149
    /**
150
     * @param Order $order
151
     * @return $this
152
     */
153
    public function setDefaultOrder(Order $order)
154
    {
155
        $this->order = $order;
156
157
        return $this;
158
    }
159
160
    /**
161
     * @return Order
162
     */
163
    public function getOrder()
164
    {
165
        return $this->order;
166
    }
167
168
    /**
169
     * @param int $perPage
170
     * @return $this
171
     */
172
    public function setPerPage($perPage)
173
    {
174
        $this->perPage = $perPage;
175
176
        return $this;
177
    }
178
179
    /**
180
     * @return int
181
     */
182
    public function getPerPage()
183
    {
184
        return $this->perPage;
185
    }
186
187
    /**
188
     * @return string
189
     */
190
    public function getEmptyMessage()
191
    {
192
        return $this->emptyMessage;
193
    }
194
195
    /**
196
     * @param string $emptyMessage
197
     * @return $this
198
     */
199
    public function setEmptyMessage($emptyMessage)
200
    {
201
        $this->emptyMessage = $emptyMessage;
202
203
        return $this;
204
    }
205
206
    /**
207
     * @param RowAction $rowAction
208
     * @return $this
209
     */
210
    public function addRowAction(RowAction $rowAction)
211
    {
212
        $rowAction->setGrid($this);
213
        $this->rowActions[] = $rowAction;
214
215
        return $this;
216
    }
217
218
    /**
219
     * @param bool $flag
220
     * @return $this
221
     */
222
    public function setEnablePagination($flag)
223
    {
224
        $this->enablePagination = (bool) $flag;
225
        $this->setPerPage(null);
226
227
        return $this;
228
    }
229
230
    /**
231
     * @return bool
232
     */
233
    public function isPaginationEnabled()
234
    {
235
        return $this->enablePagination;
236
    }
237
238
    /**
239
     * @param callable $callback
240
     * @deprecated
241
     */
242
    public function each(callable $callback)
243
    {
244
        $this->after($callback);
245
    }
246
247
    /**
248
     * @param callable $callback
249
     * @return $this
250
     */
251
    public function after(callable $callback)
252
    {
253
        $this->afterCallback = $callback;
254
255
        return $this;
256
    }
257
258
    /**
259
     * @param Component $component
260
     * @return $this
261
     */
262
    public function addComponent(Component $component)
263
    {
264
        $component->setGrid($this);
265
        $this->viewData[$component->getName()] = $component->render();
266
267
        return $this;
268
    }
269
270
    /**
271
     * @param array $viewData
272
     * @return $this
273
     */
274
    public function setViewData($viewData)
275
    {
276
        $this->viewData = $viewData;
277
278
        return $this;
279
    }
280
281
    /**
282
     * @return \Illuminate\View\View
283
     */
284
    public function render()
285
    {
286
        $rows = $this->getRows();
287
        $pagination = null;
288
289
        if ($this->enablePagination) {
290
            $pagination = $this->getPaginator($rows)->appends($this->gridHelper->getRequest()->except('page'));
291
        }
292
293
        return $this->gridHelper->getView()->make($this->template, [
294
            'columns'       => $this->columns,
295
            'rows'          => $rows,
296
            'pagination'    => $pagination,
297
            'grid'          => $this,
298
            'is_filterable' => $this->isFilterable()
299
        ], $this->viewData);
300
    }
301
302
    /**
303
     * Is table filterable?
304
     *
305
     * @return bool
306
     */
307
    public function isFilterable()
308
    {
309
        $hasFilters = false;
310
311
        foreach ($this->columns as $column) {
312
            if ($column->isFilterable()) {
313
                $hasFilters = true;
314
                break;
315
            }
316
        }
317
318
        return $hasFilters;
319
    }
320
321
    /**
322
     * @return Rows
323
     */
324
    public function getRows()
325
    {
326
        if (empty($this->source)) {
327
            throw new \InvalidArgumentException('You MUST set the data grid source by calling setSource() method.');
328
        }
329
330
        if (!empty($this->rows)) {
331
            return $this->rows;
332
        }
333
334
        if ($this->gridHelper->getRequest()->has('column') && !empty($this->defaultOrder)) {
335
            $this->order = new Order(
336
                $this->gridHelper->getRequest()->get('column', $this->defaultOrder['column']),
337
                $this->gridHelper->getRequest()->get('direction', $this->defaultOrder['direction'])
338
            );
339
340
            $validator = $this->gridHelper->getValidatorInstance($this->getValidatorRules());
341
342
            if ($validator->fails()) {
343
                $this->makeDefaultOrder();
344
            }
345
        }
346
347
        $data = $this->execute();
348
        $this->rows = new Rows();
349
350
        // special column for action buttons
351
        $actions = new Column(['name' => '__actions__']);
352
        $actions->setGrid($this);
353
354
        foreach ($data as $mixed) {
355
            $row = new Row($mixed);
356
            $row->setGrid($this);
357
358
            foreach ($this->columns as $column) {
359
                $row->addCell(new Cell($column, $mixed));
360
            }
361
362
            $row->addCell((new Action($actions, $mixed))->setRowActions($this->rowActions));
363
            $this->rows->addRow($row);
364
        }
365
366
        $this->columns[] = $actions;
367
368
        // finally call callback on every row so we can modify rows, cells, attributes etc...
369
        if ($this->afterCallback) {
370
            foreach ($this->rows as $row) {
371
                $this->afterCallback->call($this, $row);
372
            }
373
        }
374
375
        return $this->rows;
376
    }
377
378
    /**
379
     * @param Rows $rows
380
     * @return LengthAwarePaginator
381
     */
382
    protected function getPaginator(Rows $rows)
383
    {
384
        return new LengthAwarePaginator($rows, $this->total, $this->perPage, $this->resolveCurrentPage(), [
385
            'path' => $this->resolveCurrentPath(),
386
        ]);
387
    }
388
389
    /**
390
     * @return mixed
391
     */
392
    protected function execute()
393
    {
394
        // apply filters first
395
        $this->source->applyFilters($this->columns);
396
397
        // calculate total rows to build pagination
398
        if ($this->enablePagination) {
399
            $this->total = $this->source->total();
400
        }
401
402
        return $this->source->execute($this->perPage, $this->resolveCurrentPage(), $this->order);
403
    }
404
405
    /**
406
     * @return int
407
     */
408
    protected function resolveCurrentPage()
409
    {
410
        return Paginator::resolveCurrentPage();
411
    }
412
413
    /**
414
     * @return string
415
     */
416
    protected function resolveCurrentPath()
417
    {
418
        return Paginator::resolveCurrentPath();
419
    }
420
421
    /**
422
     * @return array
423
     */
424
    protected function getValidatorRules()
425
    {
426
        $allowed = [];
427
428
        foreach ($this->columns as $column) {
429
            if ($column->isSortable()) {
430
                $allowed[] = $column->getName();
431
            }
432
        }
433
434
        return [
435
            'column' => 'sometimes|in:' . implode(',', $allowed),
436
            'direction' => 'sometimes|in:asc,desc'
437
        ];
438
    }
439
440
    protected function makeDefaultOrder()
441
    {
442
        $this->order = $this->defaultOrder
443
            ? new Order($this->defaultOrder['column'], $this->defaultOrder['direction'])
444
            : new Order();
445
    }
446
447
    /**
448
     * @param string $name
449
     * @param array $options
450
     * @return Column
451
     */
452
    protected function makeColumn($name, array $options = [])
453
    {
454
        $options = $this->setupColumnOptions($name, $options);
455
456
        return new Column($options);
457
    }
458
459
    /**
460
     * @param string $name
461
     * @param array $options
462
     * @return array
463
     */
464
    protected function setupColumnOptions($name, array $options)
465
    {
466
        $default = ['name' => $name];
467
468
        return array_merge($default, $options);
469
    }
470
471
    /**
472
     * @return string
473
     */
474
    public function __toString()
475
    {
476
        return (string) $this->render();
477
    }
478
}
479