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\Form\Field; |
||
4 | |||
5 | use Encore\Admin\Admin; |
||
6 | use Encore\Admin\Form; |
||
7 | use Encore\Admin\Form\Field; |
||
8 | use Encore\Admin\Form\NestedForm; |
||
9 | use Encore\Admin\Widgets\Form as WidgetForm; |
||
10 | use Illuminate\Database\Eloquent\Relations\HasMany as Relation; |
||
11 | use Illuminate\Database\Eloquent\Relations\MorphMany; |
||
12 | use Illuminate\Support\Arr; |
||
13 | use Illuminate\Support\Str; |
||
14 | |||
15 | /** |
||
16 | * Class HasMany. |
||
17 | */ |
||
18 | class HasMany extends Field |
||
19 | { |
||
20 | /** |
||
21 | * Relation name. |
||
22 | * |
||
23 | * @var string |
||
24 | */ |
||
25 | protected $relationName = ''; |
||
26 | |||
27 | /** |
||
28 | * Form builder. |
||
29 | * |
||
30 | * @var \Closure |
||
31 | */ |
||
32 | protected $builder = null; |
||
33 | |||
34 | /** |
||
35 | * Form data. |
||
36 | * |
||
37 | * @var array |
||
38 | */ |
||
39 | protected $value = []; |
||
40 | |||
41 | /** |
||
42 | * View Mode. |
||
43 | * |
||
44 | * Supports `default` and `tab` currently. |
||
45 | * |
||
46 | * @var string |
||
47 | */ |
||
48 | protected $viewMode = 'default'; |
||
49 | |||
50 | /** |
||
51 | * Available views for HasMany field. |
||
52 | * |
||
53 | * @var array |
||
54 | */ |
||
55 | protected $views = [ |
||
56 | 'default' => 'admin::form.hasmany', |
||
57 | 'tab' => 'admin::form.hasmanytab', |
||
58 | 'table' => 'admin::form.hasmanytable', |
||
59 | ]; |
||
60 | |||
61 | /** |
||
62 | * Options for template. |
||
63 | * |
||
64 | * @var array |
||
65 | */ |
||
66 | protected $options = [ |
||
67 | 'allowCreate' => true, |
||
68 | 'allowDelete' => true, |
||
69 | ]; |
||
70 | |||
71 | /** |
||
72 | * Distinct fields. |
||
73 | * |
||
74 | * @var array |
||
75 | */ |
||
76 | protected $distinctFields = []; |
||
77 | |||
78 | /** |
||
79 | * Create a new HasMany field instance. |
||
80 | * |
||
81 | * @param $relationName |
||
82 | * @param array $arguments |
||
83 | */ |
||
84 | View Code Duplication | public function __construct($relationName, $arguments = []) |
|
85 | { |
||
86 | $this->relationName = $relationName; |
||
87 | |||
88 | $this->column = $relationName; |
||
89 | |||
90 | if (count($arguments) == 1) { |
||
91 | $this->label = $this->formatLabel(); |
||
92 | $this->builder = $arguments[0]; |
||
93 | } |
||
94 | |||
95 | if (count($arguments) == 2) { |
||
96 | list($this->label, $this->builder) = $arguments; |
||
97 | } |
||
98 | } |
||
99 | |||
100 | /** |
||
101 | * Get validator for this field. |
||
102 | * |
||
103 | * @param array $input |
||
104 | * |
||
105 | * @return bool|\Illuminate\Contracts\Validation\Validator |
||
106 | */ |
||
107 | public function getValidator(array $input) |
||
108 | { |
||
109 | if (!array_key_exists($this->column, $input)) { |
||
110 | return false; |
||
111 | } |
||
112 | |||
113 | $input = Arr::only($input, $this->column); |
||
114 | |||
115 | /** unset item that contains remove flag */ |
||
116 | foreach ($input[$this->column] as $key => $value) { |
||
117 | if ($value[NestedForm::REMOVE_FLAG_NAME]) { |
||
118 | unset($input[$this->column][$key]); |
||
119 | } |
||
120 | } |
||
121 | |||
122 | $form = $this->buildNestedForm($this->column, $this->builder); |
||
123 | |||
124 | $rules = $attributes = []; |
||
125 | |||
126 | /* @var Field $field */ |
||
127 | foreach ($form->fields() as $field) { |
||
128 | if (!$fieldRules = $field->getRules()) { |
||
129 | continue; |
||
130 | } |
||
131 | |||
132 | $column = $field->column(); |
||
133 | |||
134 | if (is_array($column)) { |
||
135 | foreach ($column as $key => $name) { |
||
136 | $rules[$name.$key] = $fieldRules; |
||
137 | } |
||
138 | |||
139 | $this->resetInputKey($input, $column); |
||
140 | } else { |
||
141 | $rules[$column] = $fieldRules; |
||
142 | } |
||
143 | |||
144 | $attributes = array_merge( |
||
145 | $attributes, |
||
146 | $this->formatValidationAttribute($input, $field->label(), $column) |
||
147 | ); |
||
148 | } |
||
149 | |||
150 | Arr::forget($rules, NestedForm::REMOVE_FLAG_NAME); |
||
151 | |||
152 | if (empty($rules)) { |
||
153 | return false; |
||
154 | } |
||
155 | |||
156 | $newRules = []; |
||
157 | $newInput = []; |
||
158 | |||
159 | foreach ($rules as $column => $rule) { |
||
160 | foreach (array_keys($input[$this->column]) as $key) { |
||
161 | $newRules["{$this->column}.$key.$column"] = $rule; |
||
162 | if (isset($input[$this->column][$key][$column]) && |
||
163 | is_array($input[$this->column][$key][$column])) { |
||
164 | foreach ($input[$this->column][$key][$column] as $vkey => $value) { |
||
165 | $newInput["{$this->column}.$key.{$column}$vkey"] = $value; |
||
166 | } |
||
167 | } |
||
168 | } |
||
169 | } |
||
170 | |||
171 | if (empty($newInput)) { |
||
172 | $newInput = $input; |
||
173 | } |
||
174 | |||
175 | $this->appendDistinctRules($newRules); |
||
176 | |||
177 | return \validator($newInput, $newRules, $this->getValidationMessages(), $attributes); |
||
178 | } |
||
179 | |||
180 | /** |
||
181 | * Set distinct fields. |
||
182 | * |
||
183 | * @param array $fields |
||
184 | * |
||
185 | * @return $this |
||
186 | */ |
||
187 | public function distinctFields(array $fields) |
||
188 | { |
||
189 | $this->distinctFields = $fields; |
||
190 | |||
191 | return $this; |
||
192 | } |
||
193 | |||
194 | /** |
||
195 | * Append distinct rules. |
||
196 | * |
||
197 | * @param array $rules |
||
198 | */ |
||
199 | protected function appendDistinctRules(array &$rules) |
||
200 | { |
||
201 | foreach ($this->distinctFields as $field) { |
||
202 | $rules["{$this->column}.*.$field"] = 'distinct'; |
||
203 | } |
||
204 | } |
||
205 | |||
206 | /** |
||
207 | * Format validation attributes. |
||
208 | * |
||
209 | * @param array $input |
||
210 | * @param string $label |
||
211 | * @param string $column |
||
212 | * |
||
213 | * @return array |
||
214 | */ |
||
215 | View Code Duplication | protected function formatValidationAttribute($input, $label, $column) |
|
216 | { |
||
217 | $new = $attributes = []; |
||
218 | |||
219 | if (is_array($column)) { |
||
220 | foreach ($column as $index => $col) { |
||
221 | $new[$col.$index] = $col; |
||
222 | } |
||
223 | } |
||
224 | |||
225 | foreach (array_keys(Arr::dot($input)) as $key) { |
||
226 | if (is_string($column)) { |
||
227 | if (Str::endsWith($key, ".$column")) { |
||
228 | $attributes[$key] = $label; |
||
229 | } |
||
230 | } else { |
||
231 | foreach ($new as $k => $val) { |
||
232 | if (Str::endsWith($key, ".$k")) { |
||
233 | $attributes[$key] = $label."[$val]"; |
||
234 | } |
||
235 | } |
||
236 | } |
||
237 | } |
||
238 | |||
239 | return $attributes; |
||
240 | } |
||
241 | |||
242 | /** |
||
243 | * Reset input key for validation. |
||
244 | * |
||
245 | * @param array $input |
||
246 | * @param array $column $column is the column name array set |
||
247 | * |
||
248 | * @return void. |
||
249 | */ |
||
250 | protected function resetInputKey(array &$input, array $column) |
||
251 | { |
||
252 | /** |
||
253 | * flip the column name array set. |
||
254 | * |
||
255 | * for example, for the DateRange, the column like as below |
||
256 | * |
||
257 | * ["start" => "created_at", "end" => "updated_at"] |
||
258 | * |
||
259 | * to: |
||
260 | * |
||
261 | * [ "created_at" => "start", "updated_at" => "end" ] |
||
262 | */ |
||
263 | $column = array_flip($column); |
||
264 | |||
265 | /** |
||
266 | * $this->column is the inputs array's node name, default is the relation name. |
||
267 | * |
||
268 | * So... $input[$this->column] is the data of this column's inputs data |
||
269 | * |
||
270 | * in the HasMany relation, has many data/field set, $set is field set in the below |
||
271 | */ |
||
272 | foreach ($input[$this->column] as $index => $set) { |
||
273 | |||
274 | /* |
||
275 | * foreach the field set to find the corresponding $column |
||
276 | */ |
||
277 | View Code Duplication | foreach ($set as $name => $value) { |
|
278 | /* |
||
279 | * if doesn't have column name, continue to the next loop |
||
280 | */ |
||
281 | if (!array_key_exists($name, $column)) { |
||
282 | continue; |
||
283 | } |
||
284 | |||
285 | /** |
||
286 | * example: $newKey = created_atstart. |
||
287 | * |
||
288 | * Σ( ° △ °|||)︴ |
||
289 | * |
||
290 | * I don't know why a form need range input? Only can imagine is for range search.... |
||
291 | */ |
||
292 | $newKey = $name.$column[$name]; |
||
293 | |||
294 | /* |
||
295 | * set new key |
||
296 | */ |
||
297 | Arr::set($input, "{$this->column}.$index.$newKey", $value); |
||
298 | /* |
||
299 | * forget the old key and value |
||
300 | */ |
||
301 | Arr::forget($input, "{$this->column}.$index.$name"); |
||
302 | } |
||
303 | } |
||
304 | } |
||
305 | |||
306 | /** |
||
307 | * Prepare input data for insert or update. |
||
308 | * |
||
309 | * @param array $input |
||
310 | * |
||
311 | * @return array |
||
312 | */ |
||
313 | public function prepare($input) |
||
314 | { |
||
315 | $form = $this->buildNestedForm($this->column, $this->builder); |
||
316 | |||
317 | return $form->setOriginal($this->original, $this->getKeyName())->prepare($input); |
||
318 | } |
||
319 | |||
320 | /** |
||
321 | * Build a Nested form. |
||
322 | * |
||
323 | * @param string $column |
||
324 | * @param \Closure $builder |
||
325 | * @param null $model |
||
326 | * |
||
327 | * @return NestedForm |
||
328 | */ |
||
329 | View Code Duplication | protected function buildNestedForm($column, \Closure $builder, $model = null) |
|
330 | { |
||
331 | $form = new Form\NestedForm($column, $model); |
||
332 | |||
333 | if ($this->form instanceof WidgetForm) { |
||
334 | $form->setWidgetForm($this->form); |
||
335 | } else { |
||
336 | $form->setForm($this->form); |
||
337 | } |
||
338 | |||
339 | call_user_func($builder, $form); |
||
340 | |||
341 | $form->hidden($this->getKeyName()); |
||
342 | |||
343 | $form->hidden(NestedForm::REMOVE_FLAG_NAME)->default(0)->addElementClass(NestedForm::REMOVE_FLAG_CLASS); |
||
344 | |||
345 | return $form; |
||
346 | } |
||
347 | |||
348 | /** |
||
349 | * Get the HasMany relation key name. |
||
350 | * |
||
351 | * @return string |
||
352 | */ |
||
353 | protected function getKeyName() |
||
354 | { |
||
355 | if (is_null($this->form)) { |
||
356 | return; |
||
357 | } |
||
358 | |||
359 | return $this->form->model()->{$this->relationName}()->getRelated()->getKeyName(); |
||
0 ignored issues
–
show
|
|||
360 | } |
||
361 | |||
362 | /** |
||
363 | * Set view mode. |
||
364 | * |
||
365 | * @param string $mode currently support `tab` mode. |
||
366 | * |
||
367 | * @return $this |
||
368 | * |
||
369 | * @author Edwin Hui |
||
370 | */ |
||
371 | public function mode($mode) |
||
372 | { |
||
373 | $this->viewMode = $mode; |
||
374 | |||
375 | return $this; |
||
376 | } |
||
377 | |||
378 | /** |
||
379 | * Use tab mode to showing hasmany field. |
||
380 | * |
||
381 | * @return HasMany |
||
382 | */ |
||
383 | public function useTab() |
||
384 | { |
||
385 | return $this->mode('tab'); |
||
386 | } |
||
387 | |||
388 | /** |
||
389 | * Use table mode to showing hasmany field. |
||
390 | * |
||
391 | * @return HasMany |
||
392 | */ |
||
393 | public function useTable() |
||
394 | { |
||
395 | return $this->mode('table'); |
||
396 | } |
||
397 | |||
398 | /** |
||
399 | * Build Nested form for related data. |
||
400 | * |
||
401 | * @throws \Exception |
||
402 | * |
||
403 | * @return array |
||
404 | */ |
||
405 | protected function buildRelatedForms() |
||
406 | { |
||
407 | if (is_null($this->form)) { |
||
408 | return []; |
||
409 | } |
||
410 | |||
411 | $model = $this->form->model(); |
||
0 ignored issues
–
show
The method
model does only exist in Encore\Admin\Form , but not in Encore\Admin\Widgets\Form .
It seems like the method you are trying to call exists only in some of the possible types. Let’s take a look at an example: class A
{
public function foo() { }
}
class B extends A
{
public function bar() { }
}
/**
* @param A|B $x
*/
function someFunction($x)
{
$x->foo(); // This call is fine as the method exists in A and B.
$x->bar(); // This method only exists in B and might cause an error.
}
Available Fixes
Loading history...
|
|||
412 | |||
413 | $relation = call_user_func([$model, $this->relationName]); |
||
414 | |||
415 | if (!$relation instanceof Relation && !$relation instanceof MorphMany) { |
||
416 | throw new \Exception('hasMany field must be a HasMany or MorphMany relation.'); |
||
417 | } |
||
418 | |||
419 | $forms = []; |
||
420 | |||
421 | /* |
||
422 | * If redirect from `exception` or `validation error` page. |
||
423 | * |
||
424 | * Then get form data from session flash. |
||
425 | * |
||
426 | * Else get data from database. |
||
427 | */ |
||
428 | if ($values = old($this->column)) { |
||
429 | foreach ($values as $key => $data) { |
||
430 | if ($data[NestedForm::REMOVE_FLAG_NAME] == 1) { |
||
431 | continue; |
||
432 | } |
||
433 | |||
434 | $model = $relation->getRelated()->replicate()->forceFill($data); |
||
435 | |||
436 | $forms[$key] = $this->buildNestedForm($this->column, $this->builder, $model) |
||
437 | ->fill($data); |
||
438 | } |
||
439 | } else { |
||
440 | if (empty($this->value)) { |
||
441 | return []; |
||
442 | } |
||
443 | |||
444 | foreach ($this->value as $data) { |
||
445 | $key = Arr::get($data, $relation->getRelated()->getKeyName()); |
||
446 | |||
447 | $model = $relation->getRelated()->replicate()->forceFill($data); |
||
448 | |||
449 | $forms[$key] = $this->buildNestedForm($this->column, $this->builder, $model) |
||
450 | ->fill($data); |
||
451 | } |
||
452 | } |
||
453 | |||
454 | return $forms; |
||
455 | } |
||
456 | |||
457 | /** |
||
458 | * Setup script for this field in different view mode. |
||
459 | * |
||
460 | * @param string $script |
||
461 | * |
||
462 | * @return void |
||
463 | */ |
||
464 | protected function setupScript($script) |
||
465 | { |
||
466 | $method = 'setupScriptFor'.ucfirst($this->viewMode).'View'; |
||
467 | |||
468 | call_user_func([$this, $method], $script); |
||
469 | } |
||
470 | |||
471 | /** |
||
472 | * Setup default template script. |
||
473 | * |
||
474 | * @param string $templateScript |
||
475 | * |
||
476 | * @return void |
||
477 | */ |
||
478 | protected function setupScriptForDefaultView($templateScript) |
||
479 | { |
||
480 | $removeClass = NestedForm::REMOVE_FLAG_CLASS; |
||
481 | $defaultKey = NestedForm::DEFAULT_KEY_NAME; |
||
482 | |||
483 | /** |
||
484 | * When add a new sub form, replace all element key in new sub form. |
||
485 | * |
||
486 | * @example comments[new___key__][title] => comments[new_{index}][title] |
||
487 | * |
||
488 | * {count} is increment number of current sub form count. |
||
489 | */ |
||
490 | $script = <<<EOT |
||
491 | var index = 0; |
||
492 | $('#has-many-{$this->column}').off('click', '.add').on('click', '.add', function () { |
||
493 | |||
494 | var tpl = $('template.{$this->column}-tpl'); |
||
495 | |||
496 | index++; |
||
497 | |||
498 | var template = tpl.html().replace(/{$defaultKey}/g, index); |
||
499 | $('.has-many-{$this->column}-forms').append(template); |
||
500 | {$templateScript} |
||
501 | return false; |
||
502 | }); |
||
503 | |||
504 | $('#has-many-{$this->column}').off('click', '.remove').on('click', '.remove', function () { |
||
505 | $(this).closest('.has-many-{$this->column}-form').find('input').removeAttr('required'); |
||
506 | $(this).closest('.has-many-{$this->column}-form').hide(); |
||
507 | $(this).closest('.has-many-{$this->column}-form').find('.$removeClass').val(1); |
||
508 | return false; |
||
509 | }); |
||
510 | |||
511 | EOT; |
||
512 | |||
513 | Admin::script($script); |
||
514 | } |
||
515 | |||
516 | /** |
||
517 | * Setup tab template script. |
||
518 | * |
||
519 | * @param string $templateScript |
||
520 | * |
||
521 | * @return void |
||
522 | */ |
||
523 | View Code Duplication | protected function setupScriptForTabView($templateScript) |
|
524 | { |
||
525 | $removeClass = NestedForm::REMOVE_FLAG_CLASS; |
||
526 | $defaultKey = NestedForm::DEFAULT_KEY_NAME; |
||
527 | |||
528 | $script = <<<EOT |
||
529 | |||
530 | $('#has-many-{$this->column} > .nav').off('click', 'i.close-tab').on('click', 'i.close-tab', function(){ |
||
531 | var \$navTab = $(this).siblings('a'); |
||
532 | var \$pane = $(\$navTab.attr('href')); |
||
533 | if( \$pane.hasClass('new') ){ |
||
534 | \$pane.remove(); |
||
535 | }else{ |
||
536 | \$pane.removeClass('active').find('.$removeClass').val(1); |
||
537 | } |
||
538 | if(\$navTab.closest('li').hasClass('active')){ |
||
539 | \$navTab.closest('li').remove(); |
||
540 | $('#has-many-{$this->column} > .nav > li:nth-child(1) > a').tab('show'); |
||
541 | }else{ |
||
542 | \$navTab.closest('li').remove(); |
||
543 | } |
||
544 | }); |
||
545 | |||
546 | var index = 0; |
||
547 | $('#has-many-{$this->column} > .header').off('click', '.add').on('click', '.add', function(){ |
||
548 | index++; |
||
549 | var navTabHtml = $('#has-many-{$this->column} > template.nav-tab-tpl').html().replace(/{$defaultKey}/g, index); |
||
550 | var paneHtml = $('#has-many-{$this->column} > template.pane-tpl').html().replace(/{$defaultKey}/g, index); |
||
551 | $('#has-many-{$this->column} > .nav').append(navTabHtml); |
||
552 | $('#has-many-{$this->column} > .tab-content').append(paneHtml); |
||
553 | $('#has-many-{$this->column} > .nav > li:last-child a').tab('show'); |
||
554 | {$templateScript} |
||
555 | }); |
||
556 | |||
557 | if ($('.has-error').length) { |
||
558 | $('.has-error').parent('.tab-pane').each(function () { |
||
559 | var tabId = '#'+$(this).attr('id'); |
||
560 | $('li a[href="'+tabId+'"] i').removeClass('hide'); |
||
561 | }); |
||
562 | |||
563 | var first = $('.has-error:first').parent().attr('id'); |
||
564 | $('li a[href="#'+first+'"]').tab('show'); |
||
565 | } |
||
566 | EOT; |
||
567 | |||
568 | Admin::script($script); |
||
569 | } |
||
570 | |||
571 | /** |
||
572 | * Setup default template script. |
||
573 | * |
||
574 | * @param string $templateScript |
||
575 | * |
||
576 | * @return void |
||
577 | */ |
||
578 | View Code Duplication | protected function setupScriptForTableView($templateScript) |
|
579 | { |
||
580 | $removeClass = NestedForm::REMOVE_FLAG_CLASS; |
||
581 | $defaultKey = NestedForm::DEFAULT_KEY_NAME; |
||
582 | |||
583 | /** |
||
584 | * When add a new sub form, replace all element key in new sub form. |
||
585 | * |
||
586 | * @example comments[new___key__][title] => comments[new_{index}][title] |
||
587 | * |
||
588 | * {count} is increment number of current sub form count. |
||
589 | */ |
||
590 | $script = <<<EOT |
||
591 | var index = 0; |
||
592 | $('#has-many-{$this->column}').on('click', '.add', function () { |
||
593 | |||
594 | var tpl = $('template.{$this->column}-tpl'); |
||
595 | |||
596 | index++; |
||
597 | |||
598 | var template = tpl.html().replace(/{$defaultKey}/g, index); |
||
599 | $('.has-many-{$this->column}-forms').append(template); |
||
600 | {$templateScript} |
||
601 | return false; |
||
602 | }); |
||
603 | |||
604 | $('#has-many-{$this->column}').on('click', '.remove', function () { |
||
605 | var first_input_name = $(this).closest('.has-many-{$this->column}-form').find('input[name]:first').attr('name'); |
||
606 | if (first_input_name.match('{$this->column}\\\[new_')) { |
||
607 | $(this).closest('.has-many-{$this->column}-form').remove(); |
||
608 | } else { |
||
609 | $(this).closest('.has-many-{$this->column}-form').hide(); |
||
610 | $(this).closest('.has-many-{$this->column}-form').find('.$removeClass').val(1); |
||
611 | $(this).closest('.has-many-{$this->column}-form').find('input').removeAttr('required'); |
||
612 | } |
||
613 | return false; |
||
614 | }); |
||
615 | |||
616 | EOT; |
||
617 | |||
618 | Admin::script($script); |
||
619 | } |
||
620 | |||
621 | /** |
||
622 | * Disable create button. |
||
623 | * |
||
624 | * @return $this |
||
625 | */ |
||
626 | public function disableCreate() |
||
627 | { |
||
628 | $this->options['allowCreate'] = false; |
||
629 | |||
630 | return $this; |
||
631 | } |
||
632 | |||
633 | /** |
||
634 | * Disable delete button. |
||
635 | * |
||
636 | * @return $this |
||
637 | */ |
||
638 | public function disableDelete() |
||
639 | { |
||
640 | $this->options['allowDelete'] = false; |
||
641 | |||
642 | return $this; |
||
643 | } |
||
644 | |||
645 | /** |
||
646 | * Render the `HasMany` field. |
||
647 | * |
||
648 | * @throws \Exception |
||
649 | * |
||
650 | * @return \Illuminate\View\View |
||
651 | */ |
||
652 | public function render() |
||
653 | { |
||
654 | if (!$this->shouldRender()) { |
||
655 | return ''; |
||
656 | } |
||
657 | |||
658 | if ($this->viewMode == 'table') { |
||
659 | return $this->renderTable(); |
||
660 | } |
||
661 | |||
662 | // specify a view to render. |
||
663 | $this->view = $this->views[$this->viewMode]; |
||
664 | |||
665 | list($template, $script) = $this->buildNestedForm($this->column, $this->builder) |
||
666 | ->getTemplateHtmlAndScript(); |
||
667 | |||
668 | $this->setupScript($script); |
||
669 | |||
670 | return parent::fieldRender([ |
||
671 | 'forms' => $this->buildRelatedForms(), |
||
672 | 'template' => $template, |
||
673 | 'relationName' => $this->relationName, |
||
674 | 'options' => $this->options, |
||
675 | ]); |
||
676 | } |
||
677 | |||
678 | /** |
||
679 | * Render the `HasMany` field for table style. |
||
680 | * |
||
681 | * @throws \Exception |
||
682 | * |
||
683 | * @return mixed |
||
684 | */ |
||
685 | protected function renderTable() |
||
686 | { |
||
687 | $headers = []; |
||
688 | $fields = []; |
||
689 | $hidden = []; |
||
690 | $scripts = []; |
||
691 | |||
692 | /* @var Field $field */ |
||
693 | foreach ($this->buildNestedForm($this->column, $this->builder)->fields() as $field) { |
||
694 | if (is_a($field, Hidden::class)) { |
||
695 | $hidden[] = $field->render(); |
||
696 | } else { |
||
697 | /* Hide label and set field width 100% */ |
||
698 | $field->setLabelClass(['hidden']); |
||
699 | $field->setWidth(12, 0); |
||
700 | $fields[] = $field->render(); |
||
701 | $headers[] = $field->label(); |
||
702 | } |
||
703 | |||
704 | /* |
||
705 | * Get and remove the last script of Admin::$script stack. |
||
706 | */ |
||
707 | if ($field->getScript()) { |
||
708 | $scripts[] = array_pop(Admin::$script); |
||
709 | } |
||
710 | } |
||
711 | |||
712 | /* Build row elements */ |
||
713 | $template = array_reduce($fields, function ($all, $field) { |
||
714 | $all .= "<td>{$field}</td>"; |
||
715 | |||
716 | return $all; |
||
717 | }, ''); |
||
718 | |||
719 | /* Build cell with hidden elements */ |
||
720 | $template .= '<td class="hidden">'.implode('', $hidden).'</td>'; |
||
721 | |||
722 | $this->setupScript(implode("\r\n", $scripts)); |
||
723 | |||
724 | // specify a view to render. |
||
725 | $this->view = $this->views[$this->viewMode]; |
||
726 | |||
727 | return parent::fieldRender([ |
||
728 | 'headers' => $headers, |
||
729 | 'forms' => $this->buildRelatedForms(), |
||
730 | 'template' => $template, |
||
731 | 'relationName' => $this->relationName, |
||
732 | 'options' => $this->options, |
||
733 | ]); |
||
734 | } |
||
735 | } |
||
736 |
It seems like the method you are trying to call exists only in some of the possible types.
Let’s take a look at an example:
Available Fixes
Add an additional type-check:
Only allow a single type to be passed if the variable comes from a parameter: