1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Encore\Admin\Form; |
4
|
|
|
|
5
|
|
|
use Encore\Admin\Admin; |
6
|
|
|
use Encore\Admin\Form; |
7
|
|
|
use Encore\Admin\Form\Field\Hidden; |
8
|
|
|
use Illuminate\Support\Collection; |
9
|
|
|
use Illuminate\Support\Facades\URL; |
10
|
|
|
use Illuminate\Support\Str; |
11
|
|
|
|
12
|
|
|
/** |
13
|
|
|
* Class Builder. |
14
|
|
|
*/ |
15
|
|
|
class Builder |
16
|
|
|
{ |
17
|
|
|
/** |
18
|
|
|
* Previous url key. |
19
|
|
|
*/ |
20
|
|
|
const PREVIOUS_URL_KEY = '_previous_'; |
21
|
|
|
|
22
|
|
|
/** |
23
|
|
|
* @var mixed |
24
|
|
|
*/ |
25
|
|
|
protected $id; |
26
|
|
|
|
27
|
|
|
/** |
28
|
|
|
* @var Form |
29
|
|
|
*/ |
30
|
|
|
protected $form; |
31
|
|
|
|
32
|
|
|
/** |
33
|
|
|
* @var |
34
|
|
|
*/ |
35
|
|
|
protected $action; |
36
|
|
|
|
37
|
|
|
/** |
38
|
|
|
* @var Collection |
39
|
|
|
*/ |
40
|
|
|
protected $fields; |
41
|
|
|
|
42
|
|
|
/** |
43
|
|
|
* @var array |
44
|
|
|
*/ |
45
|
|
|
protected $options = []; |
46
|
|
|
|
47
|
|
|
/** |
48
|
|
|
* Modes constants. |
49
|
|
|
*/ |
50
|
|
|
const MODE_EDIT = 'edit'; |
51
|
|
|
const MODE_CREATE = 'create'; |
52
|
|
|
|
53
|
|
|
/** |
54
|
|
|
* Form action mode, could be create|view|edit. |
55
|
|
|
* |
56
|
|
|
* @var string |
57
|
|
|
*/ |
58
|
|
|
protected $mode = 'create'; |
59
|
|
|
|
60
|
|
|
/** |
61
|
|
|
* @var array |
62
|
|
|
*/ |
63
|
|
|
protected $hiddenFields = []; |
64
|
|
|
|
65
|
|
|
/** |
66
|
|
|
* @var Tools |
67
|
|
|
*/ |
68
|
|
|
protected $tools; |
69
|
|
|
|
70
|
|
|
/** |
71
|
|
|
* @var Footer |
72
|
|
|
*/ |
73
|
|
|
protected $footer; |
74
|
|
|
|
75
|
|
|
/** |
76
|
|
|
* Width for label and field. |
77
|
|
|
* |
78
|
|
|
* @var array |
79
|
|
|
*/ |
80
|
|
|
protected $width = [ |
81
|
|
|
'label' => 2, |
82
|
|
|
'field' => 8, |
83
|
|
|
]; |
84
|
|
|
|
85
|
|
|
/** |
86
|
|
|
* View for this form. |
87
|
|
|
* |
88
|
|
|
* @var string |
89
|
|
|
*/ |
90
|
|
|
protected $view = 'admin::form'; |
91
|
|
|
|
92
|
|
|
/** |
93
|
|
|
* Form title. |
94
|
|
|
* |
95
|
|
|
* @var string |
96
|
|
|
*/ |
97
|
|
|
protected $title; |
98
|
|
|
|
99
|
|
|
/** |
100
|
|
|
* Builder constructor. |
101
|
|
|
* |
102
|
|
|
* @param Form $form |
103
|
|
|
*/ |
104
|
|
|
public function __construct(Form $form) |
105
|
|
|
{ |
106
|
|
|
$this->form = $form; |
107
|
|
|
|
108
|
|
|
$this->fields = new Collection(); |
109
|
|
|
|
110
|
|
|
$this->init(); |
111
|
|
|
} |
112
|
|
|
|
113
|
|
|
/** |
114
|
|
|
* Do initialize. |
115
|
|
|
*/ |
116
|
|
|
public function init() |
117
|
|
|
{ |
118
|
|
|
$this->tools = new Tools($this); |
119
|
|
|
$this->footer = new Footer($this); |
120
|
|
|
} |
121
|
|
|
|
122
|
|
|
/** |
123
|
|
|
* Get form tools instance. |
124
|
|
|
* |
125
|
|
|
* @return Tools |
126
|
|
|
*/ |
127
|
|
|
public function getTools() |
128
|
|
|
{ |
129
|
|
|
return $this->tools; |
130
|
|
|
} |
131
|
|
|
|
132
|
|
|
/** |
133
|
|
|
* Get form footer instance. |
134
|
|
|
* |
135
|
|
|
* @return Footer |
136
|
|
|
*/ |
137
|
|
|
public function getFooter() |
138
|
|
|
{ |
139
|
|
|
return $this->footer; |
140
|
|
|
} |
141
|
|
|
|
142
|
|
|
/** |
143
|
|
|
* Set the builder mode. |
144
|
|
|
* |
145
|
|
|
* @param string $mode |
146
|
|
|
* |
147
|
|
|
* @return void |
148
|
|
|
*/ |
149
|
|
|
public function setMode($mode = 'create') |
150
|
|
|
{ |
151
|
|
|
$this->mode = $mode; |
152
|
|
|
} |
153
|
|
|
|
154
|
|
|
/** |
155
|
|
|
* @return string |
156
|
|
|
*/ |
157
|
|
|
public function getMode() |
158
|
|
|
{ |
159
|
|
|
return $this->mode; |
160
|
|
|
} |
161
|
|
|
|
162
|
|
|
/** |
163
|
|
|
* Returns builder is $mode. |
164
|
|
|
* |
165
|
|
|
* @param $mode |
166
|
|
|
* |
167
|
|
|
* @return bool |
168
|
|
|
*/ |
169
|
|
|
public function isMode($mode) |
170
|
|
|
{ |
171
|
|
|
return $this->mode == $mode; |
172
|
|
|
} |
173
|
|
|
|
174
|
|
|
/** |
175
|
|
|
* Check if is creating resource. |
176
|
|
|
* |
177
|
|
|
* @return bool |
178
|
|
|
*/ |
179
|
|
|
public function isCreating() |
180
|
|
|
{ |
181
|
|
|
return $this->isMode(static::MODE_CREATE); |
182
|
|
|
} |
183
|
|
|
|
184
|
|
|
/** |
185
|
|
|
* Check if is editing resource. |
186
|
|
|
* |
187
|
|
|
* @return bool |
188
|
|
|
*/ |
189
|
|
|
public function isEditing() |
190
|
|
|
{ |
191
|
|
|
return $this->isMode(static::MODE_EDIT); |
192
|
|
|
} |
193
|
|
|
|
194
|
|
|
/** |
195
|
|
|
* Set resource Id. |
196
|
|
|
* |
197
|
|
|
* @param $id |
198
|
|
|
* |
199
|
|
|
* @return void |
200
|
|
|
*/ |
201
|
|
|
public function setResourceId($id) |
202
|
|
|
{ |
203
|
|
|
$this->id = $id; |
204
|
|
|
} |
205
|
|
|
|
206
|
|
|
/** |
207
|
|
|
* Get Resource id. |
208
|
|
|
* |
209
|
|
|
* @return mixed |
210
|
|
|
*/ |
211
|
|
|
public function getResourceId() |
212
|
|
|
{ |
213
|
|
|
return $this->id; |
214
|
|
|
} |
215
|
|
|
|
216
|
|
|
/** |
217
|
|
|
* @return string |
218
|
|
|
*/ |
219
|
|
|
public function getResource($slice = null) |
220
|
|
|
{ |
221
|
|
|
if ($this->mode == self::MODE_CREATE) { |
222
|
|
|
return $this->form->resource(-1); |
223
|
|
|
} |
224
|
|
|
if ($slice !== null) { |
225
|
|
|
return $this->form->resource($slice); |
226
|
|
|
} |
227
|
|
|
|
228
|
|
|
return $this->form->resource(); |
229
|
|
|
} |
230
|
|
|
|
231
|
|
|
/** |
232
|
|
|
* @param int $field |
233
|
|
|
* @param int $label |
234
|
|
|
* |
235
|
|
|
* @return $this |
236
|
|
|
*/ |
237
|
|
|
public function setWidth($field = 8, $label = 2) |
238
|
|
|
{ |
239
|
|
|
$this->width = [ |
240
|
|
|
'label' => $label, |
241
|
|
|
'field' => $field, |
242
|
|
|
]; |
243
|
|
|
|
244
|
|
|
return $this; |
245
|
|
|
} |
246
|
|
|
|
247
|
|
|
/** |
248
|
|
|
* Get label and field width. |
249
|
|
|
* |
250
|
|
|
* @return array |
251
|
|
|
*/ |
252
|
|
|
public function getWidth() |
253
|
|
|
{ |
254
|
|
|
return $this->width; |
255
|
|
|
} |
256
|
|
|
|
257
|
|
|
/** |
258
|
|
|
* Set form action. |
259
|
|
|
* |
260
|
|
|
* @param string $action |
261
|
|
|
*/ |
262
|
|
|
public function setAction($action) |
263
|
|
|
{ |
264
|
|
|
$this->action = $action; |
265
|
|
|
} |
266
|
|
|
|
267
|
|
|
/** |
268
|
|
|
* Get Form action. |
269
|
|
|
* |
270
|
|
|
* @return string |
271
|
|
|
*/ |
272
|
|
|
public function getAction() |
273
|
|
|
{ |
274
|
|
|
if ($this->action) { |
275
|
|
|
return $this->action; |
276
|
|
|
} |
277
|
|
|
|
278
|
|
|
if ($this->isMode(static::MODE_EDIT)) { |
279
|
|
|
return $this->form->resource().'/'.$this->id; |
280
|
|
|
} |
281
|
|
|
|
282
|
|
|
if ($this->isMode(static::MODE_CREATE)) { |
283
|
|
|
return $this->form->resource(-1); |
284
|
|
|
} |
285
|
|
|
|
286
|
|
|
return ''; |
287
|
|
|
} |
288
|
|
|
|
289
|
|
|
/** |
290
|
|
|
* Set view for this form. |
291
|
|
|
* |
292
|
|
|
* @param string $view |
293
|
|
|
* |
294
|
|
|
* @return $this |
295
|
|
|
*/ |
296
|
|
|
public function setView($view) |
297
|
|
|
{ |
298
|
|
|
$this->view = $view; |
299
|
|
|
|
300
|
|
|
return $this; |
301
|
|
|
} |
302
|
|
|
|
303
|
|
|
/** |
304
|
|
|
* Set title for form. |
305
|
|
|
* |
306
|
|
|
* @param string $title |
307
|
|
|
* |
308
|
|
|
* @return $this |
309
|
|
|
*/ |
310
|
|
|
public function setTitle($title) |
311
|
|
|
{ |
312
|
|
|
$this->title = $title; |
313
|
|
|
|
314
|
|
|
return $this; |
315
|
|
|
} |
316
|
|
|
|
317
|
|
|
/** |
318
|
|
|
* Get fields of this builder. |
319
|
|
|
* |
320
|
|
|
* @return Collection |
321
|
|
|
*/ |
322
|
|
|
public function fields() |
323
|
|
|
{ |
324
|
|
|
return $this->fields; |
325
|
|
|
} |
326
|
|
|
|
327
|
|
|
/** |
328
|
|
|
* Get specify field. |
329
|
|
|
* |
330
|
|
|
* @param string $name |
331
|
|
|
* |
332
|
|
|
* @return mixed |
333
|
|
|
*/ |
334
|
|
|
public function field($name) |
335
|
|
|
{ |
336
|
|
|
return $this->fields()->first(function (Field $field) use ($name) { |
337
|
|
|
return $field->column() == $name; |
338
|
|
|
}); |
339
|
|
|
} |
340
|
|
|
|
341
|
|
|
/** |
342
|
|
|
* If the parant form has rows. |
343
|
|
|
* |
344
|
|
|
* @return bool |
345
|
|
|
*/ |
346
|
|
|
public function hasRows() |
347
|
|
|
{ |
348
|
|
|
return !empty($this->form->rows); |
349
|
|
|
} |
350
|
|
|
|
351
|
|
|
/** |
352
|
|
|
* Get field rows of form. |
353
|
|
|
* |
354
|
|
|
* @return array |
355
|
|
|
*/ |
356
|
|
|
public function getRows() |
357
|
|
|
{ |
358
|
|
|
return $this->form->rows; |
359
|
|
|
} |
360
|
|
|
|
361
|
|
|
/** |
362
|
|
|
* @return array |
363
|
|
|
*/ |
364
|
|
|
public function getHiddenFields() |
365
|
|
|
{ |
366
|
|
|
return $this->hiddenFields; |
367
|
|
|
} |
368
|
|
|
|
369
|
|
|
/** |
370
|
|
|
* @param Field $field |
371
|
|
|
* |
372
|
|
|
* @return void |
373
|
|
|
*/ |
374
|
|
|
public function addHiddenField(Field $field) |
375
|
|
|
{ |
376
|
|
|
$this->hiddenFields[] = $field; |
377
|
|
|
} |
378
|
|
|
|
379
|
|
|
/** |
380
|
|
|
* Add or get options. |
381
|
|
|
* |
382
|
|
|
* @param array $options |
383
|
|
|
* |
384
|
|
|
* @return array|null |
385
|
|
|
*/ |
386
|
|
|
public function options($options = []) |
387
|
|
|
{ |
388
|
|
|
if (empty($options)) { |
389
|
|
|
return $this->options; |
390
|
|
|
} |
391
|
|
|
|
392
|
|
|
$this->options = array_merge($this->options, $options); |
393
|
|
|
} |
394
|
|
|
|
395
|
|
|
/** |
396
|
|
|
* Get or set option. |
397
|
|
|
* |
398
|
|
|
* @param string $option |
399
|
|
|
* @param mixed $value |
400
|
|
|
* |
401
|
|
|
* @return $this |
402
|
|
|
*/ |
403
|
|
|
public function option($option, $value = null) |
404
|
|
|
{ |
405
|
|
|
if (func_num_args() == 1) { |
406
|
|
|
return array_get($this->options, $option); |
407
|
|
|
} |
408
|
|
|
|
409
|
|
|
$this->options[$option] = $value; |
410
|
|
|
|
411
|
|
|
return $this; |
412
|
|
|
} |
413
|
|
|
|
414
|
|
|
/** |
415
|
|
|
* @return string |
416
|
|
|
*/ |
417
|
|
|
public function title() |
418
|
|
|
{ |
419
|
|
|
if ($this->title) { |
420
|
|
|
return $this->title; |
421
|
|
|
} |
422
|
|
|
|
423
|
|
|
if ($this->mode == static::MODE_CREATE) { |
424
|
|
|
return trans('admin.create'); |
425
|
|
|
} |
426
|
|
|
|
427
|
|
|
if ($this->mode == static::MODE_EDIT) { |
428
|
|
|
return trans('admin.edit'); |
429
|
|
|
} |
430
|
|
|
|
431
|
|
|
return ''; |
432
|
|
|
} |
433
|
|
|
|
434
|
|
|
/** |
435
|
|
|
* Determine if form fields has files. |
436
|
|
|
* |
437
|
|
|
* @return bool |
438
|
|
|
*/ |
439
|
|
|
public function hasFile() |
440
|
|
|
{ |
441
|
|
|
foreach ($this->fields() as $field) { |
442
|
|
|
if ($field instanceof Field\File) { |
443
|
|
|
return true; |
444
|
|
|
} |
445
|
|
|
} |
446
|
|
|
|
447
|
|
|
return false; |
448
|
|
|
} |
449
|
|
|
|
450
|
|
|
/** |
451
|
|
|
* Add field for store redirect url after update or store. |
452
|
|
|
* |
453
|
|
|
* @return void |
454
|
|
|
*/ |
455
|
|
|
protected function addRedirectUrlField() |
456
|
|
|
{ |
457
|
|
|
$previous = URL::previous(); |
458
|
|
|
|
459
|
|
|
if (!$previous || $previous == URL::current()) { |
460
|
|
|
return; |
461
|
|
|
} |
462
|
|
|
|
463
|
|
|
if (Str::contains($previous, url($this->getResource()))) { |
|
|
|
|
464
|
|
|
$this->addHiddenField((new Hidden(static::PREVIOUS_URL_KEY))->value($previous)); |
465
|
|
|
} |
466
|
|
|
} |
467
|
|
|
|
468
|
|
|
/** |
469
|
|
|
* Open up a new HTML form. |
470
|
|
|
* |
471
|
|
|
* @param array $options |
472
|
|
|
* |
473
|
|
|
* @return string |
474
|
|
|
*/ |
475
|
|
|
public function open($options = []) |
476
|
|
|
{ |
477
|
|
|
$attributes = []; |
478
|
|
|
|
479
|
|
|
if ($this->isMode(self::MODE_EDIT)) { |
480
|
|
|
$this->addHiddenField((new Hidden('_method'))->value('PUT')); |
481
|
|
|
} |
482
|
|
|
|
483
|
|
|
$this->addRedirectUrlField(); |
484
|
|
|
|
485
|
|
|
$attributes['action'] = $this->getAction(); |
486
|
|
|
$attributes['method'] = array_get($options, 'method', 'post'); |
487
|
|
|
$attributes['accept-charset'] = 'UTF-8'; |
488
|
|
|
|
489
|
|
|
$attributes['class'] = array_get($options, 'class'); |
490
|
|
|
|
491
|
|
|
if ($this->hasFile()) { |
492
|
|
|
$attributes['enctype'] = 'multipart/form-data'; |
493
|
|
|
} |
494
|
|
|
|
495
|
|
|
$html = []; |
496
|
|
|
foreach ($attributes as $name => $value) { |
497
|
|
|
$html[] = "$name=\"$value\""; |
498
|
|
|
} |
499
|
|
|
|
500
|
|
|
return '<form '.implode(' ', $html).' pjax-container>'; |
501
|
|
|
} |
502
|
|
|
|
503
|
|
|
/** |
504
|
|
|
* Close the current form. |
505
|
|
|
* |
506
|
|
|
* @return string |
507
|
|
|
*/ |
508
|
|
|
public function close() |
509
|
|
|
{ |
510
|
|
|
$this->form = null; |
511
|
|
|
$this->fields = null; |
512
|
|
|
|
513
|
|
|
return '</form>'; |
514
|
|
|
} |
515
|
|
|
|
516
|
|
|
/** |
517
|
|
|
* Remove reserved fields like `id` `created_at` `updated_at` in form fields. |
518
|
|
|
* |
519
|
|
|
* @return void |
520
|
|
|
*/ |
521
|
|
|
protected function removeReservedFields() |
522
|
|
|
{ |
523
|
|
|
if (!$this->isMode(static::MODE_CREATE)) { |
524
|
|
|
return; |
525
|
|
|
} |
526
|
|
|
|
527
|
|
|
$reservedColumns = [ |
528
|
|
|
$this->form->model()->getKeyName(), |
529
|
|
|
$this->form->model()->getCreatedAtColumn(), |
530
|
|
|
$this->form->model()->getUpdatedAtColumn(), |
531
|
|
|
]; |
532
|
|
|
|
533
|
|
|
$this->fields = $this->fields()->reject(function (Field $field) use ($reservedColumns) { |
534
|
|
|
return in_array($field->column(), $reservedColumns); |
535
|
|
|
}); |
536
|
|
|
} |
537
|
|
|
|
538
|
|
|
/** |
539
|
|
|
* Render form header tools. |
540
|
|
|
* |
541
|
|
|
* @return string |
542
|
|
|
*/ |
543
|
|
|
public function renderTools() |
544
|
|
|
{ |
545
|
|
|
return $this->tools->render(); |
546
|
|
|
} |
547
|
|
|
|
548
|
|
|
/** |
549
|
|
|
* Render form footer. |
550
|
|
|
* |
551
|
|
|
* @return string |
552
|
|
|
*/ |
553
|
|
|
public function renderFooter() |
554
|
|
|
{ |
555
|
|
|
return $this->footer->render(); |
556
|
|
|
} |
557
|
|
|
|
558
|
|
|
/** |
559
|
|
|
* Render form. |
560
|
|
|
* |
561
|
|
|
* @return string |
562
|
|
|
*/ |
563
|
|
|
public function render() |
564
|
|
|
{ |
565
|
|
|
$this->removeReservedFields(); |
566
|
|
|
|
567
|
|
|
$tabObj = $this->form->getTab(); |
568
|
|
|
|
569
|
|
|
if (!$tabObj->isEmpty()) { |
570
|
|
|
$script = <<<'SCRIPT' |
571
|
|
|
|
572
|
|
|
var hash = document.location.hash; |
573
|
|
|
if (hash) { |
574
|
|
|
$('.nav-tabs a[href="' + hash + '"]').tab('show'); |
575
|
|
|
} |
576
|
|
|
|
577
|
|
|
// Change hash for page-reload |
578
|
|
|
$('.nav-tabs a').on('shown.bs.tab', function (e) { |
579
|
|
|
history.pushState(null,null, e.target.hash); |
580
|
|
|
}); |
581
|
|
|
|
582
|
|
|
if ($('.has-error').length) { |
583
|
|
|
$('.has-error').each(function () { |
584
|
|
|
var tabId = '#'+$(this).closest('.tab-pane').attr('id'); |
585
|
|
|
$('li a[href="'+tabId+'"] i').removeClass('hide'); |
586
|
|
|
}); |
587
|
|
|
|
588
|
|
|
var first = $('.has-error:first').closest('.tab-pane').attr('id'); |
589
|
|
|
$('li a[href="#'+first+'"]').tab('show'); |
590
|
|
|
} |
591
|
|
|
|
592
|
|
|
SCRIPT; |
593
|
|
|
Admin::script($script); |
594
|
|
|
} |
595
|
|
|
|
596
|
|
|
$data = [ |
597
|
|
|
'form' => $this, |
598
|
|
|
'tabObj' => $tabObj, |
599
|
|
|
'width' => $this->width, |
600
|
|
|
]; |
601
|
|
|
|
602
|
|
|
return view($this->view, $data)->render(); |
|
|
|
|
603
|
|
|
} |
604
|
|
|
} |
605
|
|
|
|
This check looks at variables that are passed out again to other methods.
If the outgoing method call has stricter type requirements than the method itself, an issue is raised.
An additional type check may prevent trouble.