Completed
Push — master ( ce6f95...09fff3 )
by Roberts
05:17 queued 36s
created

Grid::setupLayout()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Arbory\Base\Admin;
4
5
use Closure;
6
use Arbory\Base\Admin\Grid\Builder;
7
use Arbory\Base\Admin\Grid\Column;
8
use Arbory\Base\Admin\Grid\Filter;
9
use Arbory\Base\Admin\Grid\FilterInterface;
10
use Arbory\Base\Admin\Grid\Row;
11
use Arbory\Base\Html\Elements\Content;
12
use Illuminate\Contracts\Support\Renderable;
13
use Illuminate\Database\Eloquent\Model;
14
use Illuminate\Pagination\LengthAwarePaginator;
15
use Illuminate\Support\Collection;
16
17
/**
18
 * Class Grid
19
 * @package Arbory\Base\Admin
20
 */
21
class Grid implements Renderable
22
{
23
    use ModuleComponent;
24
25
    const FOOTER_SIDE_PRIMARY = 'primary';
26
    const FOOTER_SIDE_SECONDARY = 'secondary';
27
28
    /**
29
     * @var Model
30
     */
31
    protected $model;
32
33
    /**
34
     * @var Collection
35
     */
36
    protected $columns;
37
38
    /**
39
     * @var Collection
40
     */
41
    protected $rows;
42
43
    /**
44
     * @var array
45
     */
46
    protected $enabledDefaultTools = [ 'create', 'search' ];
47
48
    /**
49
     * @var array
50
     */
51
    protected $tools = [];
52
53
    /**
54
     * @var Collection|null
55
     */
56
    protected $items;
57
58
    /**
59
     * @var bool
60
     */
61
    protected $paginated = true;
62
63
    /**
64
     * @var Builder
65
     */
66
    protected $renderer;
67
68
    /**
69
     * @var Filter
70
     */
71
    protected $filter;
72
73
    /**
74
     * @param Model $model
75
     * @param Closure $layout
76
     */
77
    public function __construct( Model $model, Closure $layout )
78
    {
79
        $this->model = $model;
80
        $this->columns = new Collection();
81
        $this->rows = new Collection();
82
        $this->renderer = new Builder( $this );
83
84
        $this->setupFilter();
85
        $this->setupLayout( $layout );
86
    }
87
88
    /**
89
     * @return string
90
     */
91
    public function __toString()
92
    {
93
        return (string) $this->render();
94
    }
95
96
    /**
97
     * @param Closure $layout
98
     */
99
    protected function setupLayout( Closure $layout ): void
100
    {
101
        $layout($this);
102
    }
103
104
    /**
105
     * @return void
106
     */
107
    protected function setupFilter()
108
    {
109
        $this->setFilter( new Filter( $this->model ) );
110
    }
111
112
    /**
113
     * @param FilterInterface $filter
114
     * @return Grid
115
     */
116
    public function setFilter( FilterInterface $filter )
117
    {
118
        $this->filter = $filter;
0 ignored issues
show
Documentation Bug introduced by
$filter is of type Arbory\Base\Admin\Grid\FilterInterface, but the property $filter was declared to be of type Arbory\Base\Admin\Grid\Filter. Are you sure that you always receive this specific sub-class here, or does it make sense to add an instanceof check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a given class or a super-class is assigned to a property that is type hinted more strictly.

Either this assignment is in error or an instanceof check should be added for that assignment.

class Alien {}

class Dalek extends Alien {}

class Plot
{
    /** @var  Dalek */
    public $villain;
}

$alien = new Alien();
$plot = new Plot();
if ($alien instanceof Dalek) {
    $plot->villain = $alien;
}
Loading history...
119
120
        return $this;
121
    }
122
123
    /**
124
     * @return FilterInterface
125
     */
126
    public function getFilter()
127
    {
128
        return $this->filter;
129
    }
130
131
    /**
132
     * @param Renderable $tool
133
     * @param string|null $side
134
     * @return void
135
     */
136
    public function addTool( Renderable $tool, string $side = null )
137
    {
138
        $this->tools[] = [ $tool, $side ?: self::FOOTER_SIDE_SECONDARY ];
139
    }
140
141
    /**
142
     * @return array
143
     */
144
    public function getTools()
145
    {
146
        return $this->tools;
147
    }
148
149
    /**
150
     * @param string[] $tools
151
     * @return Grid
152
     */
153
    public function tools( array $tools )
154
    {
155
        $this->enabledDefaultTools = $tools;
156
157
        return $this;
158
    }
159
160
    /**
161
     * @param array|Collection $items
162
     * @return Grid
163
     */
164
    public function items( $items )
165
    {
166
        if( is_array( $items ) )
167
        {
168
            $items = new Collection( $items );
169
        }
170
171
        $this->items = $items;
172
173
        return $this;
174
    }
175
176
    /**
177
     * @param bool $paginate
178
     * @return Grid
179
     */
180
    public function paginate( bool $paginate = true )
181
    {
182
        $this->paginated = $paginate;
183
184
        return $this;
185
    }
186
187
    /**
188
     * @param $renderer
189
     */
190
    public function setRenderer( $renderer )
191
    {
192
        $this->renderer = $renderer;
193
    }
194
195
    /**
196
     * @return Collection|Column[]
197
     */
198
    public function getColumns()
199
    {
200
        return $this->columns;
201
    }
202
203
    /**
204
     * @return Collection
205
     */
206
    public function getRows()
207
    {
208
        return $this->rows;
209
    }
210
211
    /**
212
     * @param null $name
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $name is correct as it would always require null to be passed?
Loading history...
213
     * @param null $label
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $label is correct as it would always require null to be passed?
Loading history...
214
     * @return Column
215
     */
216
    public function column( $name = null, $label = null )
217
    {
218
        $column = new Column( $name, $label );
219
        $column->setGrid( $this );
220
221
        $this->columns->push( $column );
222
223
        if( strpos( $name, '.' ) !== false )
224
        {
225
            list( $relationName, $relationColumn ) = explode( '.', $name );
226
227
            $this->filter->withRelation( $relationName );
228
            $column->setRelation( $relationName, $relationColumn );
229
        }
230
231
        return $column;
232
    }
233
234
    /**
235
     * @param Collection|LengthAwarePaginator $items
236
     */
237
    protected function buildRows( $items )
238
    {
239
        if( $items instanceof LengthAwarePaginator )
240
        {
241
            $items = new Collection( $items->items() );
242
        }
243
244
        $this->rows = $items->map( function( $model )
245
        {
246
            return new Row( $this, $model );
247
        } );
248
    }
249
250
    /**
251
     * @param Closure $callback
252
     */
253
    public function filter( Closure $callback )
254
    {
255
        call_user_func( $callback, $this->filter );
256
    }
257
258
    /**
259
     * @return LengthAwarePaginator|Collection
260
     */
261
    protected function fetchData()
262
    {
263
        if( method_exists( $this->filter, 'setPaginated' ) )
264
        {
265
            $this->filter->setPaginated( $this->paginated );
266
        }
267
268
        return $this->filter->execute( $this->getColumns() );
269
    }
270
271
    /**
272
     * @return Content
273
     */
274
    public function render()
275
    {
276
        $result = $this->fetchData();
277
        $items = $this->items ?? $result;
278
279
        $this->buildRows( $items );
280
281
        return $this->renderer->render( $items );
282
    }
283
284
    /**
285
     * @return string[]
286
     */
287
    public function getEnabledDefaultTools(): array
288
    {
289
        return $this->enabledDefaultTools;
290
    }
291
292
    /**
293
     * @return bool
294
     */
295
    public function isPaginated(): bool
296
    {
297
        return $this->paginated;
298
    }
299
300
    /**
301
     * @return bool
302
     */
303
    public function hasTools(): bool
304
    {
305
        return !empty( $this->enabledDefaultTools );
306
    }
307
308
    /**
309
     * @param string $tool
310
     * @return bool
311
     */
312
    public function hasTool( string $tool ): bool
313
    {
314
        return in_array( $tool, $this->enabledDefaultTools, false );
315
    }
316
317
    /**
318
     * @return array
319
     */
320
    public function toArray(): array
321
    {
322
        $items = $this->fetchData();
323
324
        $this->buildRows( $items );
325
326
        $columns = $this->columns->map( function( Column $column )
327
        {
328
            return (string) $column;
329
        } )->toArray();
330
331
        return $this->rows->map( function( Row $row ) use ( $columns )
332
        {
333
            return array_combine( $columns, $row->toArray() );
334
        } )->toArray();
335
    }
336
}
337