Completed
Push — master ( f9866e...fa91dc )
by Song
02:51
created

src/Grid.php (2 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
namespace Encore\Admin;
4
5
use Closure;
6
use Encore\Admin\Exception\Handler;
7
use Encore\Admin\Grid\Column;
8
use Encore\Admin\Grid\Displayers;
9
use Encore\Admin\Grid\Exporter;
10
use Encore\Admin\Grid\Exporters\AbstractExporter;
11
use Encore\Admin\Grid\Filter;
12
use Encore\Admin\Grid\HasElementNames;
13
use Encore\Admin\Grid\Model;
14
use Encore\Admin\Grid\Row;
15
use Encore\Admin\Grid\Tools;
16
use Illuminate\Database\Eloquent\Model as Eloquent;
17
use Illuminate\Database\Eloquent\Relations;
18
use Illuminate\Support\Collection;
19
use Illuminate\Support\Facades\Input;
20
use Jenssegers\Mongodb\Eloquent\Model as MongodbModel;
21
22
class Grid
23
{
24
    use HasElementNames;
25
26
    /**
27
     * The grid data model instance.
28
     *
29
     * @var \Encore\Admin\Grid\Model
30
     */
31
    protected $model;
32
33
    /**
34
     * Collection of all grid columns.
35
     *
36
     * @var \Illuminate\Support\Collection
37
     */
38
    protected $columns;
39
40
    /**
41
     * Collection of all data rows.
42
     *
43
     * @var \Illuminate\Support\Collection
44
     */
45
    protected $rows;
46
47
    /**
48
     * Rows callable fucntion.
49
     *
50
     * @var \Closure
51
     */
52
    protected $rowsCallback;
53
54
    /**
55
     * All column names of the grid.
56
     *
57
     * @var array
58
     */
59
    public $columnNames = [];
60
61
    /**
62
     * Grid builder.
63
     *
64
     * @var \Closure
65
     */
66
    protected $builder;
67
68
    /**
69
     * Mark if the grid is builded.
70
     *
71
     * @var bool
72
     */
73
    protected $builded = false;
74
75
    /**
76
     * All variables in grid view.
77
     *
78
     * @var array
79
     */
80
    protected $variables = [];
81
82
    /**
83
     * The grid Filter.
84
     *
85
     * @var \Encore\Admin\Grid\Filter
86
     */
87
    protected $filter;
88
89
    /**
90
     * Resource path of the grid.
91
     *
92
     * @var
93
     */
94
    protected $resourcePath;
95
96
    /**
97
     * Default primary key name.
98
     *
99
     * @var string
100
     */
101
    protected $keyName = 'id';
102
103
    /**
104
     * Export driver.
105
     *
106
     * @var string
107
     */
108
    protected $exporter;
109
110
    /**
111
     * View for grid to render.
112
     *
113
     * @var string
114
     */
115
    protected $view = 'admin::grid.table';
116
117
    /**
118
     * Per-page options.
119
     *
120
     * @var array
121
     */
122
    public $perPages = [10, 20, 30, 50, 100];
123
124
    /**
125
     * Default items count per-page.
126
     *
127
     * @var int
128
     */
129
    public $perPage = 20;
130
131
    /**
132
     * Header tools.
133
     *
134
     * @var Tools
135
     */
136
    public $tools;
137
138
    /**
139
     * Callback for grid actions.
140
     *
141
     * @var Closure
142
     */
143
    protected $actionsCallback;
144
145
    /**
146
     * Options for grid.
147
     *
148
     * @var array
149
     */
150
    protected $options = [
151
        'usePagination'  => true,
152
        'useTools'       => true,
153
        'useFilter'      => true,
154
        'useExporter'    => true,
155
        'useActions'     => true,
156
        'useRowSelector' => true,
157
        'allowCreate'    => true,
158
    ];
159
160
    /**
161
     * @var Closure
162
     */
163
    protected $footer;
164
165
    /**
166
     * Create a new grid instance.
167
     *
168
     * @param Eloquent $model
169
     * @param Closure  $builder
170
     */
171
    public function __construct(Eloquent $model, Closure $builder = null)
172
    {
173
        $this->keyName = $model->getKeyName();
174
        $this->model = new Model($model);
175
        $this->columns = new Collection();
176
        $this->rows = new Collection();
177
        $this->builder = $builder;
178
179
        $this->model()->setGrid($this);
180
181
        $this->setupTools();
182
        $this->setupFilter();
183
184
        $this->handleExportRequest();
185
    }
186
187
    /**
188
     * Setup grid tools.
189
     */
190
    public function setupTools()
191
    {
192
        $this->tools = new Tools($this);
193
    }
194
195
    /**
196
     * Setup grid filter.
197
     *
198
     * @return void
199
     */
200
    protected function setupFilter()
201
    {
202
        $this->filter = new Filter($this->model());
203
    }
204
205
    /**
206
     * Handle export request.
207
     *
208
     * @param bool $forceExport
209
     */
210
    protected function handleExportRequest($forceExport = false)
211
    {
212
        if (!$scope = request(Exporter::$queryName)) {
213
            return;
214
        }
215
216
        // clear output buffer.
217
        if (ob_get_length()) {
218
            ob_end_clean();
219
        }
220
221
        $this->model()->usePaginate(false);
222
223
        if ($this->builder) {
224
            call_user_func($this->builder, $this);
225
226
            $this->getExporter($scope)->export();
227
        }
228
229
        if ($forceExport) {
230
            $this->getExporter($scope)->export();
231
        }
232
    }
233
234
    /**
235
     * @param string $scope
236
     *
237
     * @return AbstractExporter
238
     */
239
    protected function getExporter($scope)
240
    {
241
        return (new Exporter($this))->resolve($this->exporter)->withScope($scope);
242
    }
243
244
    /**
245
     * Get or set option for grid.
246
     *
247
     * @param string $key
248
     * @param mixed  $value
249
     *
250
     * @return $this|mixed
251
     */
252
    public function option($key, $value = null)
253
    {
254
        if (is_null($value)) {
255
            return $this->options[$key];
256
        }
257
258
        $this->options[$key] = $value;
259
260
        return $this;
261
    }
262
263
    /**
264
     * Get primary key name of model.
265
     *
266
     * @return string
267
     */
268
    public function getKeyName()
269
    {
270
        return $this->keyName ?: 'id';
271
    }
272
273
    /**
274
     * Add column to Grid.
275
     *
276
     * @param string $name
277
     * @param string $label
278
     *
279
     * @return Column
280
     */
281
    public function column($name, $label = '')
282
    {
283
        $relationName = $relationColumn = '';
284
285
        if (strpos($name, '.') !== false) {
286
            list($relationName, $relationColumn) = explode('.', $name);
287
288
            $relation = $this->model()->eloquent()->$relationName();
289
290
            $label = empty($label) ? ucfirst($relationColumn) : $label;
291
292
            $name = snake_case($relationName).'.'.$relationColumn;
293
        }
294
295
        $column = $this->addColumn($name, $label);
296
297
        if (isset($relation) && $relation instanceof Relations\Relation) {
298
            $this->model()->with($relationName);
299
            $column->setRelation($relationName, $relationColumn);
300
        }
301
302
        return $column;
303
    }
304
305
    /**
306
     * Batch add column to grid.
307
     *
308
     * @example
309
     * 1.$grid->columns(['name' => 'Name', 'email' => 'Email' ...]);
310
     * 2.$grid->columns('name', 'email' ...)
311
     *
312
     * @param array $columns
313
     *
314
     * @return Collection|null
315
     */
316
    public function columns($columns = [])
317
    {
318
        if (func_num_args() == 0) {
319
            return $this->columns;
320
        }
321
322
        if (func_num_args() == 1 && is_array($columns)) {
323
            foreach ($columns as $column => $label) {
324
                $this->column($column, $label);
325
            }
326
327
            return;
328
        }
329
330
        foreach (func_get_args() as $column) {
331
            $this->column($column);
332
        }
333
    }
334
335
    /**
336
     * Add column to grid.
337
     *
338
     * @param string $column
339
     * @param string $label
340
     *
341
     * @return Column
342
     */
343 View Code Duplication
    protected function addColumn($column = '', $label = '')
344
    {
345
        $column = new Column($column, $label);
346
        $column->setGrid($this);
347
348
        return tap($column, function ($value) {
349
            $this->columns->push($value);
350
        });
351
    }
352
353
    /**
354
     * Prepend column to grid.
355
     *
356
     * @param string $column
357
     * @param string $label
358
     *
359
     * @return Column
360
     */
361 View Code Duplication
    protected function prependColumn($column = '', $label = '')
362
    {
363
        $column = new Column($column, $label);
364
        $column->setGrid($this);
365
366
        return tap($column, function ($value) {
367
            $this->columns->prepend($value);
368
        });
369
    }
370
371
    /**
372
     * Get Grid model.
373
     *
374
     * @return Model
375
     */
376
    public function model()
377
    {
378
        return $this->model;
379
    }
380
381
    /**
382
     * Paginate the grid.
383
     *
384
     * @param int $perPage
385
     *
386
     * @return void
387
     */
388
    public function paginate($perPage = 20)
389
    {
390
        $this->perPage = $perPage;
391
392
        $this->model()->paginate($perPage);
393
    }
394
395
    /**
396
     * Get the grid paginator.
397
     *
398
     * @return mixed
399
     */
400
    public function paginator()
401
    {
402
        return new Tools\Paginator($this);
403
    }
404
405
    /**
406
     * Disable grid pagination.
407
     *
408
     * @return $this
409
     */
410
    public function disablePagination()
411
    {
412
        $this->model->usePaginate(false);
413
414
        $this->option('usePagination', false);
415
416
        return $this;
417
    }
418
419
    /**
420
     * If this grid use pagination.
421
     *
422
     * @return bool
423
     */
424
    public function usePagination()
425
    {
426
        return $this->option('usePagination');
427
    }
428
429
    /**
430
     * Set per-page options.
431
     *
432
     * @param array $perPages
433
     */
434
    public function perPages(array $perPages)
435
    {
436
        $this->perPages = $perPages;
437
    }
438
439
    /**
440
     * Disable all actions.
441
     *
442
     * @return $this
443
     */
444
    public function disableActions()
445
    {
446
        return $this->option('useActions', false);
447
    }
448
449
    /**
450
     * Set grid action callback.
451
     *
452
     * @param Closure $callback
453
     *
454
     * @return $this
455
     */
456
    public function actions(Closure $callback)
457
    {
458
        $this->actionsCallback = $callback;
459
460
        return $this;
461
    }
462
463
    /**
464
     * Add `actions` column for grid.
465
     *
466
     * @return void
467
     */
468
    protected function appendActionsColumn()
469
    {
470
        if (!$this->option('useActions')) {
471
            return;
472
        }
473
474
        $this->addColumn('__actions__', trans('admin.action'))
475
            ->displayUsing(Displayers\Actions::class, [$this->actionsCallback]);
476
    }
477
478
    /**
479
     * Disable row selector.
480
     *
481
     * @return Grid|mixed
482
     */
483
    public function disableRowSelector()
484
    {
485
        $this->tools(function ($tools) {
486
            /* @var Grid\Tools $tools */
487
            $tools->disableBatchActions();
488
        });
489
490
        return $this->option('useRowSelector', false);
491
    }
492
493
    /**
494
     * Prepend checkbox column for grid.
495
     *
496
     * @return void
497
     */
498
    protected function prependRowSelectorColumn()
499
    {
500
        if (!$this->option('useRowSelector')) {
501
            return;
502
        }
503
504
        $this->prependColumn(Column::SELECT_COLUMN_NAME, ' ')
505
            ->displayUsing(Displayers\RowSelector::class);
506
    }
507
508
    /**
509
     * Build the grid.
510
     *
511
     * @return void
512
     */
513
    public function build()
514
    {
515
        if ($this->builded) {
516
            return;
517
        }
518
519
        $collection = $this->processFilter(false);
520
521
        $data = $collection->toArray();
522
523
        $this->prependRowSelectorColumn();
524
        $this->appendActionsColumn();
525
526
        Column::setOriginalGridModels($collection);
527
528
        $this->columns->map(function (Column $column) use (&$data) {
529
            $data = $column->fill($data);
530
531
            $this->columnNames[] = $column->getName();
532
        });
533
534
        $this->buildRows($data);
535
536
        $this->builded = true;
537
    }
538
539
    /**
540
     * Disable header tools.
541
     *
542
     * @return $this
543
     */
544
    public function disableTools()
545
    {
546
        $this->option('useTools', false);
547
548
        return $this;
549
    }
550
551
    /**
552
     * Disable grid filter.
553
     *
554
     * @return $this
555
     */
556
    public function disableFilter()
557
    {
558
        $this->option('useFilter', false);
559
560
        $this->tools->disableFilterButton();
561
562
        return $this;
563
    }
564
565
    /**
566
     * Get filter of Grid.
567
     *
568
     * @return Filter
569
     */
570
    public function getFilter()
571
    {
572
        return $this->filter;
573
    }
574
575
    /**
576
     * Process the grid filter.
577
     *
578
     * @param bool $toArray
579
     *
580
     * @return array|Collection|mixed
581
     */
582
    public function processFilter($toArray = true)
583
    {
584
        if ($this->builder) {
585
            call_user_func($this->builder, $this);
586
        }
587
588
        return $this->filter->execute($toArray);
589
    }
590
591
    /**
592
     * Set the grid filter.
593
     *
594
     * @param Closure $callback
595
     */
596
    public function filter(Closure $callback)
597
    {
598
        call_user_func($callback, $this->filter);
599
    }
600
601
    /**
602
     * Render the grid filter.
603
     *
604
     * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View|string
605
     */
606
    public function renderFilter()
607
    {
608
        if (!$this->option('useFilter')) {
609
            return '';
610
        }
611
612
        return $this->filter->render();
613
    }
614
615
    /**
616
     * Expand filter.
617
     *
618
     * @return $this
619
     */
620
    public function expandFilter()
621
    {
622
        $this->filter->expand();
623
624
        return $this;
625
    }
626
627
    /**
628
     * Build the grid rows.
629
     *
630
     * @param array $data
631
     *
632
     * @return void
633
     */
634
    protected function buildRows(array $data)
635
    {
636
        $this->rows = collect($data)->map(function ($model, $number) {
637
            return new Row($number, $model);
638
        });
639
640
        if ($this->rowsCallback) {
641
            $this->rows->map($this->rowsCallback);
642
        }
643
    }
644
645
    /**
646
     * Set grid row callback function.
647
     *
648
     * @param Closure $callable
649
     *
650
     * @return Collection|null
651
     */
652
    public function rows(Closure $callable = null)
653
    {
654
        if (is_null($callable)) {
655
            return $this->rows;
656
        }
657
658
        $this->rowsCallback = $callable;
659
    }
660
661
    /**
662
     * Setup grid tools.
663
     *
664
     * @param Closure $callback
665
     *
666
     * @return void
667
     */
668
    public function tools(Closure $callback)
669
    {
670
        call_user_func($callback, $this->tools);
671
    }
672
673
    /**
674
     * Render custom tools.
675
     *
676
     * @return string
677
     */
678
    public function renderHeaderTools()
679
    {
680
        return $this->tools->render();
681
    }
682
683
    /**
684
     * Set exporter driver for Grid to export.
685
     *
686
     * @param $exporter
687
     *
688
     * @return $this
689
     */
690
    public function exporter($exporter)
691
    {
692
        $this->exporter = $exporter;
693
694
        return $this;
695
    }
696
697
    /**
698
     * Get the export url.
699
     *
700
     * @param int  $scope
701
     * @param null $args
702
     *
703
     * @return string
704
     */
705
    public function getExportUrl($scope = 1, $args = null)
706
    {
707
        $input = array_merge(Input::all(), Exporter::formatExportQuery($scope, $args));
708
709
        if ($constraints = $this->model()->getConstraints()) {
710
            $input = array_merge($input, $constraints);
711
        }
712
713
        return $this->resource().'?'.http_build_query($input);
714
    }
715
716
    /**
717
     * Get create url.
718
     *
719
     * @return string
720
     */
721
    public function getCreateUrl()
722
    {
723
        $queryString = '';
724
725
        if ($constraints = $this->model()->getConstraints()) {
726
            $queryString = http_build_query($constraints);
727
        }
728
729
        return sprintf('%s/create%s',
730
            $this->resource(),
731
            $queryString ? ('?'.$queryString) : ''
732
        );
733
    }
734
735
    /**
736
     * If grid allows to use header tools.
737
     *
738
     * @return bool
739
     */
740
    public function allowTools()
741
    {
742
        return $this->option('useTools');
743
    }
744
745
    /**
746
     * If grid allows export.s.
747
     *
748
     * @return bool
749
     */
750
    public function allowExport()
751
    {
752
        return $this->option('useExporter');
753
    }
754
755
    /**
756
     * Disable export.
757
     *
758
     * @return $this
759
     */
760
    public function disableExport()
761
    {
762
        return $this->option('useExporter', false);
763
    }
764
765
    /**
766
     * Render export button.
767
     *
768
     * @return string
769
     */
770
    public function renderExportButton()
771
    {
772
        return (new Tools\ExportButton($this))->render();
773
    }
774
775
    /**
776
     * Alias for method `disableCreateButton`.
777
     *
778
     * @return $this
779
     *
780
     * @deprecated
781
     */
782
    public function disableCreation()
783
    {
784
        return $this->disableCreateButton();
785
    }
786
787
    /**
788
     * Remove create button on grid.
789
     *
790
     * @return $this
791
     */
792
    public function disableCreateButton()
793
    {
794
        return $this->option('allowCreate', false);
795
    }
796
797
    /**
798
     * If allow creation.
799
     *
800
     * @return bool
801
     */
802
    public function allowCreation()
803
    {
804
        return $this->option('allowCreate');
805
    }
806
807
    /**
808
     * Render create button for grid.
809
     *
810
     * @return string
811
     */
812
    public function renderCreateButton()
813
    {
814
        return (new Tools\CreateButton($this))->render();
815
    }
816
817
    /**
818
     * Set grid footer.
819
     *
820
     * @param Closure|null $closure
821
     *
822
     * @return Closure
823
     */
824
    public function footer(Closure $closure = null)
825
    {
826
        if (!$closure) {
827
            return $this->footer;
828
        }
829
830
        $this->footer = $closure;
831
832
        return $this;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this; (Encore\Admin\Grid) is incompatible with the return type documented by Encore\Admin\Grid::footer of type Closure.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
833
    }
834
835
    /**
836
     * Render grid footer.
837
     *
838
     * @return Tools\Footer|string
839
     */
840
    public function renderFooter()
841
    {
842
        if (!$this->footer) {
843
            return '';
844
        }
845
846
        return (new Tools\Footer($this))->render();
847
    }
848
849
    /**
850
     * Get current resource uri.
851
     *
852
     * @param string $path
853
     *
854
     * @return string
855
     */
856
    public function resource($path = null)
857
    {
858
        if (!empty($path)) {
859
            $this->resourcePath = $path;
860
861
            return $this;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this; (Encore\Admin\Grid) is incompatible with the return type documented by Encore\Admin\Grid::resource of type string.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
862
        }
863
864
        if (!empty($this->resourcePath)) {
865
            return $this->resourcePath;
866
        }
867
868
        return app('request')->getPathInfo();
869
    }
870
871
    /**
872
     * Handle get mutator column for grid.
873
     *
874
     * @param string $method
875
     * @param string $label
876
     *
877
     * @return bool|Column
878
     */
879
    protected function handleGetMutatorColumn($method, $label)
880
    {
881
        if ($this->model()->eloquent()->hasGetMutator($method)) {
882
            return $this->addColumn($method, $label);
883
        }
884
885
        return false;
886
    }
887
888
    /**
889
     * Handle relation column for grid.
890
     *
891
     * @param string $method
892
     * @param string $label
893
     *
894
     * @return bool|Column
895
     */
896
    protected function handleRelationColumn($method, $label)
897
    {
898
        $model = $this->model()->eloquent();
899
900
        if (!method_exists($model, $method)) {
901
            return false;
902
        }
903
904
        if (!($relation = $model->$method()) instanceof Relations\Relation) {
905
            return false;
906
        }
907
908
        if ($relation instanceof Relations\HasOne ||
909
            $relation instanceof Relations\BelongsTo ||
910
            $relation instanceof Relations\MorphOne
911
        ) {
912
            $this->model()->with($method);
913
914
            return $this->addColumn($method, $label)->setRelation(snake_case($method));
915
        }
916
917
        if ($relation instanceof Relations\HasMany
918
            || $relation instanceof Relations\BelongsToMany
919
            || $relation instanceof Relations\MorphToMany
920
        ) {
921
            $this->model()->with($method);
922
923
            return $this->addColumn(snake_case($method), $label);
924
        }
925
926
        return false;
927
    }
928
929
    /**
930
     * Dynamically add columns to the grid view.
931
     *
932
     * @param $method
933
     * @param $arguments
934
     *
935
     * @return Column
936
     */
937
    public function __call($method, $arguments)
938
    {
939
        $label = isset($arguments[0]) ? $arguments[0] : ucfirst($method);
940
941
        if ($this->model()->eloquent() instanceof MongodbModel) {
942
            return $this->addColumn($method, $label);
943
        }
944
945
        if ($column = $this->handleGetMutatorColumn($method, $label)) {
946
            return $column;
947
        }
948
949
        if ($column = $this->handleRelationColumn($method, $label)) {
950
            return $column;
951
        }
952
953
        return $this->addColumn($method, $label);
954
    }
955
956
    /**
957
     * Register column displayers.
958
     *
959
     * @return void.
960
     */
961
    public static function registerColumnDisplayer()
962
    {
963
        $map = [
964
            'editable'    => Displayers\Editable::class,
965
            'switch'      => Displayers\SwitchDisplay::class,
966
            'switchGroup' => Displayers\SwitchGroup::class,
967
            'select'      => Displayers\Select::class,
968
            'image'       => Displayers\Image::class,
969
            'label'       => Displayers\Label::class,
970
            'button'      => Displayers\Button::class,
971
            'link'        => Displayers\Link::class,
972
            'badge'       => Displayers\Badge::class,
973
            'progressBar' => Displayers\ProgressBar::class,
974
            'radio'       => Displayers\Radio::class,
975
            'checkbox'    => Displayers\Checkbox::class,
976
            'orderable'   => Displayers\Orderable::class,
977
            'table'       => Displayers\Table::class,
978
        ];
979
980
        foreach ($map as $abstract => $class) {
981
            Column::extend($abstract, $class);
982
        }
983
    }
984
985
    /**
986
     * Add variables to grid view.
987
     *
988
     * @param array $variables
989
     *
990
     * @return $this
991
     */
992
    public function with($variables = [])
993
    {
994
        $this->variables = $variables;
995
996
        return $this;
997
    }
998
999
    /**
1000
     * Get all variables will used in grid view.
1001
     *
1002
     * @return array
1003
     */
1004
    protected function variables()
1005
    {
1006
        $this->variables['grid'] = $this;
1007
1008
        return $this->variables;
1009
    }
1010
1011
    /**
1012
     * Set a view to render.
1013
     *
1014
     * @param string $view
1015
     * @param array  $variables
1016
     */
1017
    public function setView($view, $variables = [])
1018
    {
1019
        if (!empty($variables)) {
1020
            $this->with($variables);
1021
        }
1022
1023
        $this->view = $view;
1024
    }
1025
1026
    /**
1027
     * Set grid title.
1028
     *
1029
     * @param string $title
1030
     *
1031
     * @return $this
1032
     */
1033
    public function setTitle($title)
1034
    {
1035
        $this->variables['title'] = $title;
1036
1037
        return $this;
1038
    }
1039
1040
    /**
1041
     * Set relation for grid.
1042
     *
1043
     * @param Relations\Relation $relation
1044
     *
1045
     * @return $this
1046
     */
1047
    public function setRelation(Relations\Relation $relation)
1048
    {
1049
        $this->model()->setRelation($relation);
1050
1051
        return $this;
1052
    }
1053
1054
    /**
1055
     * Set resource path for grid.
1056
     *
1057
     * @param string $path
1058
     *
1059
     * @return $this
1060
     */
1061
    public function setResource($path)
1062
    {
1063
        $this->resourcePath = $path;
1064
1065
        return $this;
1066
    }
1067
1068
    /**
1069
     * Get the string contents of the grid view.
1070
     *
1071
     * @return string
1072
     */
1073
    public function render()
1074
    {
1075
        $this->handleExportRequest(true);
1076
1077
        try {
1078
            $this->build();
1079
        } catch (\Exception $e) {
1080
            return Handler::renderException($e);
1081
        }
1082
1083
        return view($this->view, $this->variables())->render();
1084
    }
1085
}
1086