|
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
|
|
|
|
|
9
|
|
|
/** |
|
10
|
|
|
* Class NestedForm. |
|
11
|
|
|
* |
|
12
|
|
|
* @method Field\Text text($column, $label = '') |
|
13
|
|
|
* @method Field\Checkbox checkbox($column, $label = '') |
|
14
|
|
|
* @method Field\Radio radio($column, $label = '') |
|
15
|
|
|
* @method Field\Select select($column, $label = '') |
|
16
|
|
|
* @method Field\MultipleSelect multipleSelect($column, $label = '') |
|
17
|
|
|
* @method Field\Textarea textarea($column, $label = '') |
|
18
|
|
|
* @method Field\Hidden hidden($column, $label = '') |
|
19
|
|
|
* @method Field\Id id($column, $label = '') |
|
20
|
|
|
* @method Field\Ip ip($column, $label = '') |
|
21
|
|
|
* @method Field\Url url($column, $label = '') |
|
22
|
|
|
* @method Field\Color color($column, $label = '') |
|
23
|
|
|
* @method Field\Email email($column, $label = '') |
|
24
|
|
|
* @method Field\Mobile mobile($column, $label = '') |
|
25
|
|
|
* @method Field\Slider slider($column, $label = '') |
|
26
|
|
|
* @method Field\Map map($latitude, $longitude, $label = '') |
|
27
|
|
|
* @method Field\Editor editor($column, $label = '') |
|
28
|
|
|
* @method Field\File file($column, $label = '') |
|
29
|
|
|
* @method Field\Image image($column, $label = '') |
|
30
|
|
|
* @method Field\Date date($column, $label = '') |
|
31
|
|
|
* @method Field\Datetime datetime($column, $label = '') |
|
32
|
|
|
* @method Field\Time time($column, $label = '') |
|
33
|
|
|
* @method Field\Year year($column, $label = '') |
|
34
|
|
|
* @method Field\Month month($column, $label = '') |
|
35
|
|
|
* @method Field\DateRange dateRange($start, $end, $label = '') |
|
36
|
|
|
* @method Field\DateTimeRange datetimeRange($start, $end, $label = '') |
|
37
|
|
|
* @method Field\TimeRange timeRange($start, $end, $label = '') |
|
38
|
|
|
* @method Field\Number number($column, $label = '') |
|
39
|
|
|
* @method Field\Currency currency($column, $label = '') |
|
40
|
|
|
* @method Field\HasMany hasMany($relationName, $callback) |
|
41
|
|
|
* @method Field\SwitchField switch($column, $label = '') |
|
42
|
|
|
* @method Field\Display display($column, $label = '') |
|
43
|
|
|
* @method Field\Rate rate($column, $label = '') |
|
44
|
|
|
* @method Field\Divide divider() |
|
45
|
|
|
* @method Field\Password password($column, $label = '') |
|
46
|
|
|
* @method Field\Decimal decimal($column, $label = '') |
|
47
|
|
|
* @method Field\Html html($html, $label = '') |
|
48
|
|
|
* @method Field\Tags tags($column, $label = '') |
|
49
|
|
|
* @method Field\Icon icon($column, $label = '') |
|
50
|
|
|
* @method Field\Embeds embeds($column, $label = '') |
|
51
|
|
|
*/ |
|
52
|
|
|
class NestedForm |
|
53
|
|
|
{ |
|
54
|
|
|
const DEFAULT_KEY_NAME = '__LA_KEY__'; |
|
55
|
|
|
|
|
56
|
|
|
const REMOVE_FLAG_NAME = '_remove_'; |
|
57
|
|
|
|
|
58
|
|
|
const REMOVE_FLAG_CLASS = 'fom-removed'; |
|
59
|
|
|
|
|
60
|
|
|
/** |
|
61
|
|
|
* @var string |
|
62
|
|
|
*/ |
|
63
|
|
|
protected $relationName; |
|
64
|
|
|
|
|
65
|
|
|
/** |
|
66
|
|
|
* NestedForm key. |
|
67
|
|
|
* |
|
68
|
|
|
* @var |
|
69
|
|
|
*/ |
|
70
|
|
|
protected $key; |
|
71
|
|
|
|
|
72
|
|
|
/** |
|
73
|
|
|
* Fields in form. |
|
74
|
|
|
* |
|
75
|
|
|
* @var Collection |
|
76
|
|
|
*/ |
|
77
|
|
|
protected $fields; |
|
78
|
|
|
|
|
79
|
|
|
/** |
|
80
|
|
|
* Original data for this field. |
|
81
|
|
|
* |
|
82
|
|
|
* @var array |
|
83
|
|
|
*/ |
|
84
|
|
|
protected $original = []; |
|
85
|
|
|
|
|
86
|
|
|
/** |
|
87
|
|
|
* @var \Encore\Admin\Form |
|
88
|
|
|
*/ |
|
89
|
|
|
protected $form; |
|
90
|
|
|
|
|
91
|
|
|
/** |
|
92
|
|
|
* Create a new NestedForm instance. |
|
93
|
|
|
* |
|
94
|
|
|
* NestedForm constructor. |
|
95
|
|
|
* |
|
96
|
|
|
* @param string $relation |
|
97
|
|
|
* @param null $key |
|
98
|
|
|
*/ |
|
99
|
|
|
public function __construct($relation, $key = null) |
|
100
|
|
|
{ |
|
101
|
|
|
$this->relationName = $relation; |
|
102
|
|
|
|
|
103
|
|
|
$this->key = $key; |
|
104
|
|
|
|
|
105
|
|
|
$this->fields = new Collection(); |
|
106
|
|
|
} |
|
107
|
|
|
|
|
108
|
|
|
/** |
|
109
|
|
|
* Set Form. |
|
110
|
|
|
* |
|
111
|
|
|
* @param Form $form |
|
112
|
|
|
* |
|
113
|
|
|
* @return $this |
|
114
|
|
|
*/ |
|
115
|
|
|
public function setForm(Form $form = null) |
|
116
|
|
|
{ |
|
117
|
|
|
$this->form = $form; |
|
118
|
|
|
|
|
119
|
|
|
return $this; |
|
120
|
|
|
} |
|
121
|
|
|
|
|
122
|
|
|
/** |
|
123
|
|
|
* Get form. |
|
124
|
|
|
* |
|
125
|
|
|
* @return Form |
|
126
|
|
|
*/ |
|
127
|
|
|
public function getForm() |
|
128
|
|
|
{ |
|
129
|
|
|
return $this->form; |
|
130
|
|
|
} |
|
131
|
|
|
|
|
132
|
|
|
/** |
|
133
|
|
|
* Set original values for fields. |
|
134
|
|
|
* |
|
135
|
|
|
* @param array $data |
|
136
|
|
|
* @param string $relatedKeyName |
|
137
|
|
|
* |
|
138
|
|
|
* @return $this |
|
139
|
|
|
*/ |
|
140
|
|
|
public function setOriginal($data, $relatedKeyName) |
|
141
|
|
|
{ |
|
142
|
|
|
if (empty($data)) { |
|
143
|
|
|
return $this; |
|
144
|
|
|
} |
|
145
|
|
|
|
|
146
|
|
|
foreach ($data as $value) { |
|
147
|
|
|
/* |
|
|
|
|
|
|
148
|
|
|
* like $this->original[30] = [ id = 30, .....] |
|
149
|
|
|
*/ |
|
150
|
|
|
$this->original[$value[$relatedKeyName]] = $value; |
|
151
|
|
|
} |
|
152
|
|
|
|
|
153
|
|
|
return $this; |
|
154
|
|
|
} |
|
155
|
|
|
|
|
156
|
|
|
/** |
|
157
|
|
|
* Prepare for insert or update. |
|
158
|
|
|
* |
|
159
|
|
|
* @param array $input |
|
160
|
|
|
* |
|
161
|
|
|
* @return mixed |
|
162
|
|
|
*/ |
|
163
|
|
|
public function prepare($input) |
|
164
|
|
|
{ |
|
165
|
|
|
foreach ($input as $key => $record) { |
|
166
|
|
|
$this->setFieldOriginalValue($key); |
|
167
|
|
|
$input[$key] = $this->prepareRecord($record); |
|
168
|
|
|
} |
|
169
|
|
|
|
|
170
|
|
|
return $input; |
|
171
|
|
|
} |
|
172
|
|
|
|
|
173
|
|
|
/** |
|
174
|
|
|
* Set original data for each field. |
|
175
|
|
|
* |
|
176
|
|
|
* @param string $key |
|
177
|
|
|
* |
|
178
|
|
|
* @return void |
|
179
|
|
|
*/ |
|
180
|
|
View Code Duplication |
protected function setFieldOriginalValue($key) |
|
|
|
|
|
|
181
|
|
|
{ |
|
182
|
|
|
$values = []; |
|
183
|
|
|
if (array_key_exists($key, $this->original)) { |
|
184
|
|
|
$values = $this->original[$key]; |
|
185
|
|
|
} |
|
186
|
|
|
$this->fields->each(function (Field $field) use ($values) { |
|
187
|
|
|
$field->setOriginal($values); |
|
188
|
|
|
}); |
|
189
|
|
|
} |
|
190
|
|
|
|
|
191
|
|
|
/** |
|
192
|
|
|
* Do prepare work before store and update. |
|
193
|
|
|
* |
|
194
|
|
|
* @param array $record |
|
195
|
|
|
* |
|
196
|
|
|
* @return array |
|
197
|
|
|
*/ |
|
198
|
|
|
protected function prepareRecord($record) |
|
199
|
|
|
{ |
|
200
|
|
|
if ($record[static::REMOVE_FLAG_NAME] == 1) { |
|
201
|
|
|
return $record; |
|
202
|
|
|
} |
|
203
|
|
|
|
|
204
|
|
|
$prepared = []; |
|
205
|
|
|
|
|
206
|
|
|
/* @var Field $field */ |
|
207
|
|
|
foreach ($this->fields as $field) { |
|
208
|
|
|
$columns = $field->column(); |
|
209
|
|
|
|
|
210
|
|
|
$value = $this->fetchColumnValue($record, $columns); |
|
211
|
|
|
|
|
212
|
|
|
if (is_null($value)) { |
|
213
|
|
|
continue; |
|
214
|
|
|
} |
|
215
|
|
|
|
|
216
|
|
|
if (method_exists($field, 'prepare')) { |
|
217
|
|
|
$value = $field->prepare($value); |
|
218
|
|
|
} |
|
219
|
|
|
|
|
220
|
|
|
if (($field instanceof \Encore\Admin\Form\Field\Hidden) || $value != $field->original()) { |
|
221
|
|
|
if (is_array($columns)) { |
|
222
|
|
|
foreach ($columns as $name => $column) { |
|
223
|
|
|
array_set($prepared, $column, $value[$name]); |
|
224
|
|
|
} |
|
225
|
|
|
} elseif (is_string($columns)) { |
|
226
|
|
|
array_set($prepared, $columns, $value); |
|
227
|
|
|
} |
|
228
|
|
|
} |
|
229
|
|
|
} |
|
230
|
|
|
|
|
231
|
|
|
$prepared[static::REMOVE_FLAG_NAME] = $record[static::REMOVE_FLAG_NAME]; |
|
232
|
|
|
|
|
233
|
|
|
return $prepared; |
|
234
|
|
|
} |
|
235
|
|
|
|
|
236
|
|
|
/** |
|
237
|
|
|
* Fetch value in input data by column name. |
|
238
|
|
|
* |
|
239
|
|
|
* @param array $data |
|
240
|
|
|
* @param string|array $columns |
|
241
|
|
|
* |
|
242
|
|
|
* @return array|mixed |
|
243
|
|
|
*/ |
|
244
|
|
View Code Duplication |
protected function fetchColumnValue($data, $columns) |
|
|
|
|
|
|
245
|
|
|
{ |
|
246
|
|
|
if (is_string($columns)) { |
|
247
|
|
|
return array_get($data, $columns); |
|
248
|
|
|
} |
|
249
|
|
|
|
|
250
|
|
|
if (is_array($columns)) { |
|
251
|
|
|
$value = []; |
|
252
|
|
|
foreach ($columns as $name => $column) { |
|
253
|
|
|
if (!array_has($data, $column)) { |
|
254
|
|
|
continue; |
|
255
|
|
|
} |
|
256
|
|
|
$value[$name] = array_get($data, $column); |
|
257
|
|
|
} |
|
258
|
|
|
|
|
259
|
|
|
return $value; |
|
260
|
|
|
} |
|
261
|
|
|
} |
|
262
|
|
|
|
|
263
|
|
|
/** |
|
264
|
|
|
* @param Field $field |
|
265
|
|
|
* |
|
266
|
|
|
* @return $this |
|
267
|
|
|
*/ |
|
268
|
|
|
public function pushField(Field $field) |
|
269
|
|
|
{ |
|
270
|
|
|
$this->fields->push($field); |
|
271
|
|
|
|
|
272
|
|
|
return $this; |
|
273
|
|
|
} |
|
274
|
|
|
|
|
275
|
|
|
/** |
|
276
|
|
|
* Get fields of this form. |
|
277
|
|
|
* |
|
278
|
|
|
* @return Collection |
|
279
|
|
|
*/ |
|
280
|
|
|
public function fields() |
|
281
|
|
|
{ |
|
282
|
|
|
return $this->fields; |
|
283
|
|
|
} |
|
284
|
|
|
|
|
285
|
|
|
/** |
|
286
|
|
|
* Fill data to all fields in form. |
|
287
|
|
|
* |
|
288
|
|
|
* @param array $data |
|
289
|
|
|
* |
|
290
|
|
|
* @return $this |
|
291
|
|
|
*/ |
|
292
|
|
|
public function fill(array $data) |
|
293
|
|
|
{ |
|
294
|
|
|
/* @var Field $field */ |
|
295
|
|
|
foreach ($this->fields() as $field) { |
|
296
|
|
|
$field->fill($data); |
|
297
|
|
|
} |
|
298
|
|
|
|
|
299
|
|
|
return $this; |
|
300
|
|
|
} |
|
301
|
|
|
|
|
302
|
|
|
/** |
|
303
|
|
|
* Get the html and script of template. |
|
304
|
|
|
* |
|
305
|
|
|
* @return array |
|
306
|
|
|
*/ |
|
307
|
|
|
public function getTemplateHtmlAndScript() |
|
308
|
|
|
{ |
|
309
|
|
|
$html = ''; |
|
310
|
|
|
$scripts = []; |
|
311
|
|
|
|
|
312
|
|
|
/* @var Field $field */ |
|
313
|
|
|
foreach ($this->fields() as $field) { |
|
314
|
|
|
|
|
315
|
|
|
//when field render, will push $script to Admin |
|
316
|
|
|
$html .= $field->render(); |
|
317
|
|
|
|
|
318
|
|
|
/* |
|
319
|
|
|
* Get and remove the last script of Admin::$script stack. |
|
320
|
|
|
*/ |
|
321
|
|
|
if ($field->getScript()) { |
|
322
|
|
|
$scripts[] = array_pop(Admin::$script); |
|
323
|
|
|
} |
|
324
|
|
|
} |
|
325
|
|
|
|
|
326
|
|
|
return [$html, implode("\r\n", $scripts)]; |
|
327
|
|
|
} |
|
328
|
|
|
|
|
329
|
|
|
/** |
|
330
|
|
|
* Set `errorKey` `elementName` `elementClass` for fields inside hasmany fields. |
|
331
|
|
|
* |
|
332
|
|
|
* @param Field $field |
|
333
|
|
|
* |
|
334
|
|
|
* @return Field |
|
335
|
|
|
*/ |
|
336
|
|
|
protected function formatField(Field $field) |
|
337
|
|
|
{ |
|
338
|
|
|
$column = $field->column(); |
|
339
|
|
|
|
|
340
|
|
|
$elementName = $elementClass = $errorKey = ''; |
|
341
|
|
|
|
|
342
|
|
|
$key = $this->key ?: 'new_'.static::DEFAULT_KEY_NAME; |
|
343
|
|
|
|
|
344
|
|
|
if (is_array($column)) { |
|
345
|
|
|
foreach ($column as $k => $name) { |
|
346
|
|
|
$errorKey[$k] = sprintf('%s.%s.%s', $this->relationName, $key, $name); |
|
347
|
|
|
$elementName[$k] = sprintf('%s[%s][%s]', $this->relationName, $key, $name); |
|
348
|
|
|
$elementClass[$k] = [$this->relationName, $name]; |
|
349
|
|
|
} |
|
350
|
|
|
} else { |
|
351
|
|
|
$errorKey = sprintf('%s.%s.%s', $this->relationName, $key, $column); |
|
352
|
|
|
$elementName = sprintf('%s[%s][%s]', $this->relationName, $key, $column); |
|
353
|
|
|
$elementClass = [$this->relationName, $column]; |
|
354
|
|
|
} |
|
355
|
|
|
|
|
356
|
|
|
return $field->setErrorKey($errorKey) |
|
357
|
|
|
->setElementName($elementName) |
|
358
|
|
|
->setElementClass($elementClass); |
|
|
|
|
|
|
359
|
|
|
} |
|
360
|
|
|
|
|
361
|
|
|
/** |
|
362
|
|
|
* Add nested-form fields dynamically. |
|
363
|
|
|
* |
|
364
|
|
|
* @param string $method |
|
365
|
|
|
* @param array $arguments |
|
366
|
|
|
* |
|
367
|
|
|
* @return mixed |
|
368
|
|
|
*/ |
|
369
|
|
|
public function __call($method, $arguments) |
|
370
|
|
|
{ |
|
371
|
|
|
if ($className = Form::findFieldClass($method)) { |
|
372
|
|
|
$column = array_get($arguments, 0, ''); |
|
373
|
|
|
|
|
374
|
|
|
/* @var Field $field */ |
|
375
|
|
|
$field = new $className($column, array_slice($arguments, 1)); |
|
376
|
|
|
|
|
377
|
|
|
$field->setForm($this->form); |
|
378
|
|
|
|
|
379
|
|
|
$field = $this->formatField($field); |
|
380
|
|
|
|
|
381
|
|
|
$this->pushField($field); |
|
382
|
|
|
|
|
383
|
|
|
return $field; |
|
384
|
|
|
} |
|
385
|
|
|
|
|
386
|
|
|
return $this; |
|
387
|
|
|
} |
|
388
|
|
|
} |
|
389
|
|
|
|
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.
The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.
This check looks for comments that seem to be mostly valid code and reports them.