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\Concerns; |
||
9 | use Encore\Admin\Grid\Displayers; |
||
10 | use Encore\Admin\Grid\Exporter; |
||
11 | use Encore\Admin\Grid\Exporters\AbstractExporter; |
||
12 | use Encore\Admin\Grid\Model; |
||
13 | use Encore\Admin\Grid\Row; |
||
14 | use Encore\Admin\Grid\Tools; |
||
15 | use Illuminate\Database\Eloquent\Model as Eloquent; |
||
16 | use Illuminate\Database\Eloquent\Relations; |
||
17 | use Illuminate\Support\Collection; |
||
18 | use Illuminate\Support\Facades\Input; |
||
19 | use Illuminate\Support\Str; |
||
20 | use Illuminate\Support\Traits\Macroable; |
||
21 | use Jenssegers\Mongodb\Eloquent\Model as MongodbModel; |
||
22 | |||
23 | class Grid |
||
24 | { |
||
25 | use Concerns\HasElementNames, |
||
26 | Concerns\HasHeader, |
||
27 | Concerns\HasFooter, |
||
28 | Concerns\HasFilter, |
||
29 | Concerns\HasTools, |
||
30 | Concerns\HasTotalRow, |
||
31 | Concerns\HasHotKeys, |
||
32 | Concerns\HasQuickCreate, |
||
33 | Concerns\HasActions, |
||
34 | Concerns\HasSelector, |
||
35 | Concerns\CanHidesColumns, |
||
36 | Concerns\CanFixColumns, |
||
37 | Macroable { |
||
38 | __call as macroCall; |
||
39 | } |
||
40 | |||
41 | /** |
||
42 | * The grid data model instance. |
||
43 | * |
||
44 | * @var \Encore\Admin\Grid\Model |
||
45 | */ |
||
46 | protected $model; |
||
47 | |||
48 | /** |
||
49 | * Collection of all grid columns. |
||
50 | * |
||
51 | * @var \Illuminate\Support\Collection |
||
52 | */ |
||
53 | protected $columns; |
||
54 | |||
55 | /** |
||
56 | * Collection of all data rows. |
||
57 | * |
||
58 | * @var \Illuminate\Support\Collection |
||
59 | */ |
||
60 | protected $rows; |
||
61 | |||
62 | /** |
||
63 | * Rows callable fucntion. |
||
64 | * |
||
65 | * @var \Closure |
||
66 | */ |
||
67 | protected $rowsCallback; |
||
68 | |||
69 | /** |
||
70 | * All column names of the grid. |
||
71 | * |
||
72 | * @var array |
||
73 | */ |
||
74 | public $columnNames = []; |
||
75 | |||
76 | /** |
||
77 | * Grid builder. |
||
78 | * |
||
79 | * @var \Closure |
||
80 | */ |
||
81 | protected $builder; |
||
82 | |||
83 | /** |
||
84 | * Mark if the grid is builded. |
||
85 | * |
||
86 | * @var bool |
||
87 | */ |
||
88 | protected $builded = false; |
||
89 | |||
90 | /** |
||
91 | * All variables in grid view. |
||
92 | * |
||
93 | * @var array |
||
94 | */ |
||
95 | protected $variables = []; |
||
96 | |||
97 | /** |
||
98 | * Resource path of the grid. |
||
99 | * |
||
100 | * @var |
||
101 | */ |
||
102 | protected $resourcePath; |
||
103 | |||
104 | /** |
||
105 | * Default primary key name. |
||
106 | * |
||
107 | * @var string |
||
108 | */ |
||
109 | protected $keyName = 'id'; |
||
110 | |||
111 | /** |
||
112 | * Export driver. |
||
113 | * |
||
114 | * @var string |
||
115 | */ |
||
116 | protected $exporter; |
||
117 | |||
118 | /** |
||
119 | * View for grid to render. |
||
120 | * |
||
121 | * @var string |
||
122 | */ |
||
123 | protected $view = 'admin::grid.table'; |
||
124 | |||
125 | /** |
||
126 | * Per-page options. |
||
127 | * |
||
128 | * @var array |
||
129 | */ |
||
130 | public $perPages = [10, 20, 30, 50, 100]; |
||
131 | |||
132 | /** |
||
133 | * Default items count per-page. |
||
134 | * |
||
135 | * @var int |
||
136 | */ |
||
137 | public $perPage = 20; |
||
138 | |||
139 | /** |
||
140 | * @var []callable |
||
141 | */ |
||
142 | protected $renderingCallbacks = []; |
||
143 | |||
144 | /** |
||
145 | * Options for grid. |
||
146 | * |
||
147 | * @var array |
||
148 | */ |
||
149 | protected $options = [ |
||
150 | 'show_pagination' => true, |
||
151 | 'show_tools' => true, |
||
152 | 'show_filter' => true, |
||
153 | 'show_exporter' => true, |
||
154 | 'show_actions' => true, |
||
155 | 'show_row_selector' => true, |
||
156 | 'show_create_btn' => true, |
||
157 | 'show_column_selector' => true, |
||
158 | ]; |
||
159 | |||
160 | /** |
||
161 | * @var string |
||
162 | */ |
||
163 | public $tableID; |
||
164 | |||
165 | /** |
||
166 | * Initialization closure array. |
||
167 | * |
||
168 | * @var []Closure |
||
169 | */ |
||
170 | protected static $initCallbacks = []; |
||
171 | |||
172 | /** |
||
173 | * Create a new grid instance. |
||
174 | * |
||
175 | * @param Eloquent $model |
||
176 | * @param Closure $builder |
||
177 | */ |
||
178 | public function __construct(Eloquent $model, Closure $builder = null) |
||
179 | { |
||
180 | $this->model = new Model($model, $this); |
||
181 | $this->keyName = $model->getKeyName(); |
||
182 | $this->builder = $builder; |
||
183 | |||
184 | $this->initialize(); |
||
185 | |||
186 | $this->handleExportRequest(); |
||
187 | |||
188 | $this->callInitCallbacks(); |
||
189 | } |
||
190 | |||
191 | /** |
||
192 | * Initialize. |
||
193 | */ |
||
194 | protected function initialize() |
||
195 | { |
||
196 | $this->tableID = uniqid('grid-table'); |
||
197 | |||
198 | $this->columns = Collection::make(); |
||
199 | $this->rows = Collection::make(); |
||
200 | |||
201 | $this->initTools() |
||
202 | ->initFilter(); |
||
203 | } |
||
204 | |||
205 | /** |
||
206 | * Initialize with user pre-defined default disables and exporter, etc. |
||
207 | * |
||
208 | * @param Closure $callback |
||
209 | */ |
||
210 | public static function init(Closure $callback = null) |
||
211 | { |
||
212 | static::$initCallbacks[] = $callback; |
||
213 | } |
||
214 | |||
215 | /** |
||
216 | * Call the initialization closure array in sequence. |
||
217 | */ |
||
218 | protected function callInitCallbacks() |
||
219 | { |
||
220 | if (empty(static::$initCallbacks)) { |
||
221 | return; |
||
222 | } |
||
223 | |||
224 | foreach (static::$initCallbacks as $callback) { |
||
225 | call_user_func($callback, $this); |
||
226 | } |
||
227 | } |
||
228 | |||
229 | /** |
||
230 | * Handle export request. |
||
231 | * |
||
232 | * @param bool $forceExport |
||
233 | */ |
||
234 | protected function handleExportRequest($forceExport = false) |
||
235 | { |
||
236 | if (!$scope = request(Exporter::$queryName)) { |
||
237 | return; |
||
238 | } |
||
239 | |||
240 | // clear output buffer. |
||
241 | if (ob_get_length()) { |
||
242 | ob_end_clean(); |
||
243 | } |
||
244 | |||
245 | $this->model()->usePaginate(false); |
||
246 | |||
247 | if ($this->builder) { |
||
248 | call_user_func($this->builder, $this); |
||
249 | |||
250 | $this->getExporter($scope)->export(); |
||
251 | } |
||
252 | |||
253 | if ($forceExport) { |
||
254 | $this->getExporter($scope)->export(); |
||
255 | } |
||
256 | } |
||
257 | |||
258 | /** |
||
259 | * @param string $scope |
||
260 | * |
||
261 | * @return AbstractExporter |
||
262 | */ |
||
263 | protected function getExporter($scope) |
||
264 | { |
||
265 | return (new Exporter($this))->resolve($this->exporter)->withScope($scope); |
||
266 | } |
||
267 | |||
268 | /** |
||
269 | * Get or set option for grid. |
||
270 | * |
||
271 | * @param string $key |
||
272 | * @param mixed $value |
||
273 | * |
||
274 | * @return $this|mixed |
||
275 | */ |
||
276 | public function option($key, $value = null) |
||
277 | { |
||
278 | if (is_null($value)) { |
||
279 | return $this->options[$key]; |
||
280 | } |
||
281 | |||
282 | $this->options[$key] = $value; |
||
283 | |||
284 | return $this; |
||
285 | } |
||
286 | |||
287 | /** |
||
288 | * Get primary key name of model. |
||
289 | * |
||
290 | * @return string |
||
291 | */ |
||
292 | public function getKeyName() |
||
293 | { |
||
294 | return $this->keyName ?: 'id'; |
||
295 | } |
||
296 | |||
297 | /** |
||
298 | * Add a column to Grid. |
||
299 | * |
||
300 | * @param string $name |
||
301 | * @param string $label |
||
302 | * |
||
303 | * @return Column |
||
304 | */ |
||
305 | public function column($name, $label = '') |
||
306 | { |
||
307 | if (Str::contains($name, '.')) { |
||
308 | return $this->addRelationColumn($name, $label); |
||
309 | } |
||
310 | |||
311 | if (Str::contains($name, '->')) { |
||
312 | return $this->addJsonColumn($name, $label); |
||
313 | } |
||
314 | |||
315 | return $this->__call($name, array_filter([$label])); |
||
316 | } |
||
317 | |||
318 | /** |
||
319 | * Batch add column to grid. |
||
320 | * |
||
321 | * @example |
||
322 | * 1.$grid->columns(['name' => 'Name', 'email' => 'Email' ...]); |
||
323 | * 2.$grid->columns('name', 'email' ...) |
||
324 | * |
||
325 | * @param array $columns |
||
326 | * |
||
327 | * @return Collection|null |
||
328 | */ |
||
329 | public function columns($columns = []) |
||
330 | { |
||
331 | if (func_num_args() == 0) { |
||
332 | return $this->columns; |
||
333 | } |
||
334 | |||
335 | if (func_num_args() == 1 && is_array($columns)) { |
||
336 | foreach ($columns as $column => $label) { |
||
337 | $this->column($column, $label); |
||
338 | } |
||
339 | |||
340 | return; |
||
341 | } |
||
342 | |||
343 | foreach (func_get_args() as $column) { |
||
344 | $this->column($column); |
||
345 | } |
||
346 | } |
||
347 | |||
348 | /** |
||
349 | * Add column to grid. |
||
350 | * |
||
351 | * @param string $column |
||
352 | * @param string $label |
||
353 | * |
||
354 | * @return Column |
||
355 | */ |
||
356 | View Code Duplication | protected function addColumn($column = '', $label = '') |
|
357 | { |
||
358 | $column = new Column($column, $label); |
||
359 | $column->setGrid($this); |
||
360 | |||
361 | return tap($column, function ($value) { |
||
362 | $this->columns->push($value); |
||
363 | }); |
||
364 | } |
||
365 | |||
366 | /** |
||
367 | * Add a relation column to grid. |
||
368 | * |
||
369 | * @param string $name |
||
370 | * @param string $label |
||
371 | * |
||
372 | * @return $this|bool|Column |
||
373 | */ |
||
374 | protected function addRelationColumn($name, $label = '') |
||
375 | { |
||
376 | list($relation, $column) = explode('.', $name); |
||
377 | |||
378 | $model = $this->model()->eloquent(); |
||
379 | |||
380 | if (!method_exists($model, $relation) || !$model->{$relation}() instanceof Relations\Relation) { |
||
381 | $class = get_class($model); |
||
382 | |||
383 | admin_error("Call to undefined relationship [{$relation}] on model [{$class}]."); |
||
384 | |||
385 | return $this; |
||
386 | } |
||
387 | |||
388 | $name = Str::snake($relation).'.'.$column; |
||
389 | |||
390 | $this->model()->with($relation); |
||
391 | |||
392 | return $this->addColumn($name, $label)->setRelation($relation, $column); |
||
393 | } |
||
394 | |||
395 | /** |
||
396 | * Add a json type column to grid. |
||
397 | * |
||
398 | * @param string $name |
||
399 | * @param string $label |
||
400 | * |
||
401 | * @return Column |
||
402 | */ |
||
403 | protected function addJsonColumn($name, $label = '') |
||
404 | { |
||
405 | $column = substr($name, strrpos($name, '->') + 2); |
||
406 | |||
407 | $name = str_replace('->', '.', $name); |
||
408 | |||
409 | return $this->addColumn($name, $label ?: ucfirst($column)); |
||
410 | } |
||
411 | |||
412 | /** |
||
413 | * Prepend column to grid. |
||
414 | * |
||
415 | * @param string $column |
||
416 | * @param string $label |
||
417 | * |
||
418 | * @return Column |
||
419 | */ |
||
420 | View Code Duplication | protected function prependColumn($column = '', $label = '') |
|
421 | { |
||
422 | $column = new Column($column, $label); |
||
423 | $column->setGrid($this); |
||
424 | |||
425 | return tap($column, function ($value) { |
||
426 | $this->columns->prepend($value); |
||
427 | }); |
||
428 | } |
||
429 | |||
430 | /** |
||
431 | * Get Grid model. |
||
432 | * |
||
433 | * @return Model |
||
434 | */ |
||
435 | public function model() |
||
436 | { |
||
437 | return $this->model; |
||
438 | } |
||
439 | |||
440 | /** |
||
441 | * Paginate the grid. |
||
442 | * |
||
443 | * @param int $perPage |
||
444 | * |
||
445 | * @return void |
||
446 | */ |
||
447 | public function paginate($perPage = 20) |
||
448 | { |
||
449 | $this->perPage = $perPage; |
||
450 | |||
451 | $this->model()->setPerPage($perPage); |
||
452 | } |
||
453 | |||
454 | /** |
||
455 | * Get the grid paginator. |
||
456 | * |
||
457 | * @return mixed |
||
458 | */ |
||
459 | public function paginator() |
||
460 | { |
||
461 | return new Tools\Paginator($this); |
||
462 | } |
||
463 | |||
464 | /** |
||
465 | * Disable grid pagination. |
||
466 | * |
||
467 | * @return $this |
||
468 | */ |
||
469 | public function disablePagination(bool $disable = true) |
||
470 | { |
||
471 | $this->model->usePaginate(!$disable); |
||
472 | |||
473 | return $this->option('show_pagination', !$disable); |
||
474 | } |
||
475 | |||
476 | /** |
||
477 | * If this grid use pagination. |
||
478 | * |
||
479 | * @return bool |
||
480 | */ |
||
481 | public function showPagination() |
||
482 | { |
||
483 | return $this->option('show_pagination'); |
||
484 | } |
||
485 | |||
486 | /** |
||
487 | * Set per-page options. |
||
488 | * |
||
489 | * @param array $perPages |
||
490 | */ |
||
491 | public function perPages(array $perPages) |
||
492 | { |
||
493 | $this->perPages = $perPages; |
||
494 | } |
||
495 | |||
496 | /** |
||
497 | * Disable row selector. |
||
498 | * |
||
499 | * @return Grid|mixed |
||
500 | */ |
||
501 | public function disableRowSelector(bool $disable = true) |
||
502 | { |
||
503 | return $this->disableBatchActions($disable); |
||
504 | } |
||
505 | |||
506 | /** |
||
507 | * Prepend checkbox column for grid. |
||
508 | * |
||
509 | * @return void |
||
510 | */ |
||
511 | protected function prependRowSelectorColumn() |
||
512 | { |
||
513 | if (!$this->option('show_row_selector')) { |
||
514 | return; |
||
515 | } |
||
516 | |||
517 | $checkAllBox = "<input type=\"checkbox\" class=\"{$this->getSelectAllName()}\" /> "; |
||
518 | |||
519 | $this->prependColumn(Column::SELECT_COLUMN_NAME, ' ') |
||
520 | ->displayUsing(Displayers\RowSelector::class) |
||
521 | ->addHeader($checkAllBox); |
||
522 | } |
||
523 | |||
524 | /** |
||
525 | * Apply column filter to grid query. |
||
526 | * |
||
527 | * @return void |
||
528 | */ |
||
529 | protected function applyColumnFilter() |
||
530 | { |
||
531 | $this->columns->each->bindFilterQuery($this->model()); |
||
532 | } |
||
533 | |||
534 | /** |
||
535 | * @return array|Collection|mixed |
||
536 | */ |
||
537 | protected function applyQuery() |
||
538 | { |
||
539 | $this->applyQuickSearch(); |
||
540 | |||
541 | $this->applyColumnFilter(); |
||
542 | |||
543 | $this->applySelectorQuery(); |
||
544 | |||
545 | return $this->applyFilter(false); |
||
546 | } |
||
547 | |||
548 | /** |
||
549 | * Add row selector columns and action columns before and after the grid. |
||
550 | * |
||
551 | * @return void |
||
552 | */ |
||
553 | protected function addDefaultColumns() |
||
554 | { |
||
555 | $this->prependRowSelectorColumn(); |
||
556 | |||
557 | $this->appendActionsColumn(); |
||
558 | } |
||
559 | |||
560 | /** |
||
561 | * Build the grid. |
||
562 | * |
||
563 | * @return void |
||
564 | */ |
||
565 | public function build() |
||
566 | { |
||
567 | if ($this->builded) { |
||
568 | return; |
||
569 | } |
||
570 | |||
571 | $collection = $this->applyQuery(); |
||
572 | |||
573 | $this->addDefaultColumns(); |
||
574 | |||
575 | Column::setOriginalGridModels($collection); |
||
576 | |||
577 | $data = $collection->toArray(); |
||
578 | |||
579 | $this->columns->map(function (Column $column) use (&$data) { |
||
580 | $data = $column->fill($data); |
||
581 | |||
582 | $this->columnNames[] = $column->getName(); |
||
583 | }); |
||
584 | |||
585 | $this->buildRows($data); |
||
586 | |||
587 | $this->builded = true; |
||
588 | } |
||
589 | |||
590 | /** |
||
591 | * Build the grid rows. |
||
592 | * |
||
593 | * @param array $data |
||
594 | * |
||
595 | * @return void |
||
596 | */ |
||
597 | protected function buildRows(array $data) |
||
598 | { |
||
599 | $this->rows = collect($data)->map(function ($model, $number) { |
||
600 | return new Row($number, $model, $this->keyName); |
||
601 | }); |
||
602 | |||
603 | if ($this->rowsCallback) { |
||
604 | $this->rows->map($this->rowsCallback); |
||
605 | } |
||
606 | } |
||
607 | |||
608 | /** |
||
609 | * Set grid row callback function. |
||
610 | * |
||
611 | * @param Closure $callable |
||
612 | * |
||
613 | * @return Collection|null |
||
614 | */ |
||
615 | public function rows(Closure $callable = null) |
||
616 | { |
||
617 | if (is_null($callable)) { |
||
618 | return $this->rows; |
||
619 | } |
||
620 | |||
621 | $this->rowsCallback = $callable; |
||
622 | } |
||
623 | |||
624 | /** |
||
625 | * Set exporter driver for Grid to export. |
||
626 | * |
||
627 | * @param $exporter |
||
628 | * |
||
629 | * @return $this |
||
630 | */ |
||
631 | public function exporter($exporter) |
||
632 | { |
||
633 | $this->exporter = $exporter; |
||
634 | |||
635 | return $this; |
||
636 | } |
||
637 | |||
638 | /** |
||
639 | * Get the export url. |
||
640 | * |
||
641 | * @param int $scope |
||
642 | * @param null $args |
||
643 | * |
||
644 | * @return string |
||
645 | */ |
||
646 | public function getExportUrl($scope = 1, $args = null) |
||
647 | { |
||
648 | $input = array_merge(Input::all(), Exporter::formatExportQuery($scope, $args)); |
||
649 | |||
650 | if ($constraints = $this->model()->getConstraints()) { |
||
651 | $input = array_merge($input, $constraints); |
||
652 | } |
||
653 | |||
654 | return $this->resource().'?'.http_build_query($input); |
||
655 | } |
||
656 | |||
657 | /** |
||
658 | * Get create url. |
||
659 | * |
||
660 | * @return string |
||
661 | */ |
||
662 | public function getCreateUrl() |
||
663 | { |
||
664 | $queryString = ''; |
||
665 | |||
666 | if ($constraints = $this->model()->getConstraints()) { |
||
667 | $queryString = http_build_query($constraints); |
||
668 | } |
||
669 | |||
670 | return sprintf('%s/create%s', |
||
671 | $this->resource(), |
||
672 | $queryString ? ('?'.$queryString) : '' |
||
673 | ); |
||
674 | } |
||
675 | |||
676 | /** |
||
677 | * If grid show export btn. |
||
678 | * |
||
679 | * @return bool |
||
680 | */ |
||
681 | public function showExportBtn() |
||
682 | { |
||
683 | return $this->option('show_exporter'); |
||
684 | } |
||
685 | |||
686 | /** |
||
687 | * Disable export. |
||
688 | * |
||
689 | * @return $this |
||
690 | */ |
||
691 | public function disableExport(bool $disable = true) |
||
692 | { |
||
693 | return $this->option('show_exporter', !$disable); |
||
694 | } |
||
695 | |||
696 | /** |
||
697 | * Render export button. |
||
698 | * |
||
699 | * @return string |
||
700 | */ |
||
701 | public function renderExportButton() |
||
702 | { |
||
703 | return (new Tools\ExportButton($this))->render(); |
||
704 | } |
||
705 | |||
706 | /** |
||
707 | * Alias for method `disableCreateButton`. |
||
708 | * |
||
709 | * @return $this |
||
710 | * |
||
711 | * @deprecated |
||
712 | */ |
||
713 | public function disableCreation() |
||
714 | { |
||
715 | return $this->disableCreateButton(); |
||
716 | } |
||
717 | |||
718 | /** |
||
719 | * Remove create button on grid. |
||
720 | * |
||
721 | * @return $this |
||
722 | */ |
||
723 | public function disableCreateButton(bool $disable = true) |
||
724 | { |
||
725 | return $this->option('show_create_btn', !$disable); |
||
726 | } |
||
727 | |||
728 | /** |
||
729 | * If allow creation. |
||
730 | * |
||
731 | * @return bool |
||
732 | */ |
||
733 | public function showCreateBtn() |
||
734 | { |
||
735 | return $this->option('show_create_btn'); |
||
736 | } |
||
737 | |||
738 | /** |
||
739 | * Render create button for grid. |
||
740 | * |
||
741 | * @return string |
||
742 | */ |
||
743 | public function renderCreateButton() |
||
744 | { |
||
745 | return (new Tools\CreateButton($this))->render(); |
||
746 | } |
||
747 | |||
748 | /** |
||
749 | * Get current resource uri. |
||
750 | * |
||
751 | * @param string $path |
||
752 | * |
||
753 | * @return string |
||
754 | */ |
||
755 | public function resource($path = null) |
||
756 | { |
||
757 | if (!empty($path)) { |
||
758 | $this->resourcePath = $path; |
||
759 | |||
760 | return $this; |
||
0 ignored issues
–
show
|
|||
761 | } |
||
762 | |||
763 | if (!empty($this->resourcePath)) { |
||
764 | return $this->resourcePath; |
||
765 | } |
||
766 | |||
767 | return request()->getPathInfo(); |
||
768 | } |
||
769 | |||
770 | /** |
||
771 | * Handle get mutator column for grid. |
||
772 | * |
||
773 | * @param string $method |
||
774 | * @param string $label |
||
775 | * |
||
776 | * @return bool|Column |
||
777 | */ |
||
778 | protected function handleGetMutatorColumn($method, $label) |
||
779 | { |
||
780 | if ($this->model()->eloquent()->hasGetMutator($method)) { |
||
781 | return $this->addColumn($method, $label); |
||
782 | } |
||
783 | |||
784 | return false; |
||
785 | } |
||
786 | |||
787 | /** |
||
788 | * Handle relation column for grid. |
||
789 | * |
||
790 | * @param string $method |
||
791 | * @param string $label |
||
792 | * |
||
793 | * @return bool|Column |
||
794 | */ |
||
795 | protected function handleRelationColumn($method, $label) |
||
796 | { |
||
797 | $model = $this->model()->eloquent(); |
||
798 | |||
799 | if (!method_exists($model, $method)) { |
||
800 | return false; |
||
801 | } |
||
802 | |||
803 | if (!($relation = $model->$method()) instanceof Relations\Relation) { |
||
804 | return false; |
||
805 | } |
||
806 | |||
807 | if ($relation instanceof Relations\HasOne || |
||
808 | $relation instanceof Relations\BelongsTo || |
||
809 | $relation instanceof Relations\MorphOne |
||
810 | ) { |
||
811 | $this->model()->with($method); |
||
812 | |||
813 | return $this->addColumn($method, $label)->setRelation(Str::snake($method)); |
||
814 | } |
||
815 | |||
816 | if ($relation instanceof Relations\HasMany |
||
817 | || $relation instanceof Relations\BelongsToMany |
||
818 | || $relation instanceof Relations\MorphToMany |
||
819 | || $relation instanceof Relations\HasManyThrough |
||
820 | ) { |
||
821 | $this->model()->with($method); |
||
822 | |||
823 | return $this->addColumn(Str::snake($method), $label); |
||
824 | } |
||
825 | |||
826 | return false; |
||
827 | } |
||
828 | |||
829 | /** |
||
830 | * Dynamically add columns to the grid view. |
||
831 | * |
||
832 | * @param $method |
||
833 | * @param $arguments |
||
834 | * |
||
835 | * @return Column |
||
836 | */ |
||
837 | public function __call($method, $arguments) |
||
838 | { |
||
839 | if (static::hasMacro($method)) { |
||
840 | return $this->macroCall($method, $arguments); |
||
841 | } |
||
842 | |||
843 | $label = $arguments[0] ?? null; |
||
844 | |||
845 | if ($this->model()->eloquent() instanceof MongodbModel) { |
||
846 | return $this->addColumn($method, $label); |
||
847 | } |
||
848 | |||
849 | if ($column = $this->handleGetMutatorColumn($method, $label)) { |
||
850 | return $column; |
||
851 | } |
||
852 | |||
853 | if ($column = $this->handleRelationColumn($method, $label)) { |
||
854 | return $column; |
||
855 | } |
||
856 | |||
857 | return $this->addColumn($method, $label); |
||
858 | } |
||
859 | |||
860 | /** |
||
861 | * Add variables to grid view. |
||
862 | * |
||
863 | * @param array $variables |
||
864 | * |
||
865 | * @return $this |
||
866 | */ |
||
867 | public function with($variables = []) |
||
868 | { |
||
869 | $this->variables = $variables; |
||
870 | |||
871 | return $this; |
||
872 | } |
||
873 | |||
874 | /** |
||
875 | * Get all variables will used in grid view. |
||
876 | * |
||
877 | * @return array |
||
878 | */ |
||
879 | protected function variables() |
||
880 | { |
||
881 | $this->variables['grid'] = $this; |
||
882 | |||
883 | return $this->variables; |
||
884 | } |
||
885 | |||
886 | /** |
||
887 | * Set a view to render. |
||
888 | * |
||
889 | * @param string $view |
||
890 | * @param array $variables |
||
891 | */ |
||
892 | public function setView($view, $variables = []) |
||
893 | { |
||
894 | if (!empty($variables)) { |
||
895 | $this->with($variables); |
||
896 | } |
||
897 | |||
898 | $this->view = $view; |
||
899 | } |
||
900 | |||
901 | /** |
||
902 | * Set grid title. |
||
903 | * |
||
904 | * @param string $title |
||
905 | * |
||
906 | * @return $this |
||
907 | */ |
||
908 | public function setTitle($title) |
||
909 | { |
||
910 | $this->variables['title'] = $title; |
||
911 | |||
912 | return $this; |
||
913 | } |
||
914 | |||
915 | /** |
||
916 | * Set relation for grid. |
||
917 | * |
||
918 | * @param Relations\Relation $relation |
||
919 | * |
||
920 | * @return $this |
||
921 | */ |
||
922 | public function setRelation(Relations\Relation $relation) |
||
923 | { |
||
924 | $this->model()->setRelation($relation); |
||
925 | |||
926 | return $this; |
||
927 | } |
||
928 | |||
929 | /** |
||
930 | * Set resource path for grid. |
||
931 | * |
||
932 | * @param string $path |
||
933 | * |
||
934 | * @return $this |
||
935 | */ |
||
936 | public function setResource($path) |
||
937 | { |
||
938 | $this->resourcePath = $path; |
||
939 | |||
940 | return $this; |
||
941 | } |
||
942 | |||
943 | /** |
||
944 | * Set rendering callback. |
||
945 | * |
||
946 | * @param callable $callback |
||
947 | * |
||
948 | * @return $this |
||
949 | */ |
||
950 | public function rendering(callable $callback) |
||
951 | { |
||
952 | $this->renderingCallbacks[] = $callback; |
||
953 | |||
954 | return $this; |
||
955 | } |
||
956 | |||
957 | /** |
||
958 | * Call callbacks before render. |
||
959 | * |
||
960 | * @return void |
||
961 | */ |
||
962 | protected function callRenderingCallback() |
||
963 | { |
||
964 | foreach ($this->renderingCallbacks as $callback) { |
||
965 | call_user_func($callback, $this); |
||
966 | } |
||
967 | } |
||
968 | |||
969 | /** |
||
970 | * Get the string contents of the grid view. |
||
971 | * |
||
972 | * @return string |
||
973 | */ |
||
974 | public function render() |
||
975 | { |
||
976 | $this->handleExportRequest(true); |
||
977 | |||
978 | try { |
||
979 | $this->build(); |
||
980 | } catch (\Exception $e) { |
||
981 | return Handler::renderException($e); |
||
982 | } |
||
983 | |||
984 | $this->callRenderingCallback(); |
||
985 | |||
986 | return view($this->view, $this->variables())->render(); |
||
987 | } |
||
988 | } |
||
989 |
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:
Our function
my_function
expects aPost
object, and outputs the author of the post. The base classPost
returns a simple string and outputting a simple string will work just fine. However, the child classBlogPost
which is a sub-type ofPost
instead decided to return anobject
, and is therefore violating the SOLID principles. If aBlogPost
were passed tomy_function
, PHP would not complain, but ultimately fail when executing thestrtoupper
call in its body.