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