1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Helmut\Forms; |
4
|
|
|
|
5
|
|
|
use Helmut\Forms\Utility\Str; |
6
|
|
|
use Helmut\Forms\Utility\Reflect; |
7
|
|
|
|
8
|
|
|
abstract class Form { |
9
|
|
|
|
10
|
|
|
/** |
11
|
|
|
* The request implementation used by the form. |
12
|
|
|
* |
13
|
|
|
* @var \Helmut\Forms\Request |
14
|
|
|
*/ |
15
|
|
|
protected $request; |
16
|
|
|
|
17
|
|
|
/** |
18
|
|
|
* The renderer used by the form. |
19
|
|
|
* |
20
|
|
|
* @var \Helmut\Forms\Renderer |
21
|
|
|
*/ |
22
|
|
|
protected $renderer; |
23
|
|
|
|
24
|
|
|
/** |
25
|
|
|
* The unique id of the form. |
26
|
|
|
* |
27
|
|
|
* @var string |
28
|
|
|
*/ |
29
|
|
|
public $id; |
30
|
|
|
|
31
|
|
|
/** |
32
|
|
|
* The action for the form. |
33
|
|
|
* |
34
|
|
|
* @var string |
35
|
|
|
*/ |
36
|
|
|
public $action = ''; |
37
|
|
|
|
38
|
|
|
/** |
39
|
|
|
* Array containing all the form fields. |
40
|
|
|
* |
41
|
|
|
* @var array |
42
|
|
|
*/ |
43
|
|
|
protected $fields = []; |
44
|
|
|
|
45
|
|
|
/** |
46
|
|
|
* Array of button keys. |
47
|
|
|
* |
48
|
|
|
* @var array |
49
|
|
|
*/ |
50
|
|
|
protected $buttons = []; |
51
|
|
|
|
52
|
|
|
/** |
53
|
|
|
* Array of models. |
54
|
|
|
* |
55
|
|
|
* @var array |
56
|
|
|
*/ |
57
|
|
|
protected $models = []; |
58
|
|
|
|
59
|
|
|
/** |
60
|
|
|
* The active template package. |
61
|
|
|
* |
62
|
|
|
* @var string |
63
|
|
|
*/ |
64
|
|
|
protected $template; |
65
|
|
|
|
66
|
|
|
/** |
67
|
|
|
* The active language used. |
68
|
|
|
* |
69
|
|
|
* @var string |
70
|
|
|
*/ |
71
|
|
|
protected $lang = 'en'; |
72
|
|
|
|
73
|
|
|
/** |
74
|
|
|
* A cache of language translations. |
75
|
|
|
* |
76
|
|
|
* @var array |
77
|
|
|
*/ |
78
|
|
|
protected $translations = []; |
79
|
|
|
|
80
|
|
|
/** |
81
|
|
|
* An array of plugins. |
82
|
|
|
* |
83
|
|
|
* @var array |
84
|
|
|
*/ |
85
|
|
|
protected $plugins = []; |
86
|
|
|
|
87
|
|
|
/** |
88
|
|
|
* An array of namespaces for class loading. |
89
|
|
|
* |
90
|
|
|
* @var array |
91
|
|
|
*/ |
92
|
|
|
protected $namespaces = []; |
93
|
|
|
|
94
|
|
|
/** |
95
|
|
|
* An array of paths for autoloading. |
96
|
|
|
* |
97
|
|
|
* @var array |
98
|
|
|
*/ |
99
|
|
|
protected $paths = []; |
100
|
|
|
|
101
|
|
|
/** |
102
|
|
|
* The static instantiation counter. Used |
103
|
|
|
* to make sure each form implementation can |
104
|
|
|
* have a unique id. |
105
|
|
|
* |
106
|
|
|
* @var integer |
107
|
|
|
*/ |
108
|
|
|
protected static $count = 0; |
109
|
|
|
|
110
|
|
|
/** |
111
|
|
|
* Create a new form. |
112
|
|
|
* |
113
|
|
|
* @param \Helmut\Forms\Request $request |
114
|
|
|
* @param \Helmut\Forms\Renderer $renderer |
115
|
|
|
*/ |
116
|
|
|
public function __construct(Request $request = null, Renderer $renderer = null) |
117
|
|
|
{ |
118
|
|
|
$this->request = $request ?: new Requests\Globals; |
119
|
|
|
$this->renderer = $renderer ?: new Renderer; |
120
|
134 |
|
|
121
|
|
|
$this->loadNamespaces(); |
122
|
134 |
|
|
123
|
134 |
|
$this->setId(); |
124
|
|
|
$this->loadDefaults(); |
125
|
134 |
|
$this->triggerDefinition(); |
126
|
|
|
} |
127
|
134 |
|
|
128
|
134 |
|
/** |
129
|
134 |
|
* Load namespaces. |
130
|
134 |
|
* |
131
|
|
|
* @return void |
132
|
|
|
*/ |
133
|
|
|
public function loadNamespaces() |
134
|
|
|
{ |
135
|
|
|
$class = get_class($this); |
136
|
|
|
|
137
|
134 |
|
$classes = [$class]; |
138
|
|
|
|
139
|
134 |
|
while ($class = get_parent_class($class)) { |
140
|
|
|
$classes[] = $class; |
141
|
134 |
|
} |
142
|
|
|
|
143
|
134 |
|
$classes = array_reverse($classes); |
144
|
134 |
|
|
145
|
|
|
foreach ($classes as $class) { |
146
|
|
|
$this->addNamespaceForClass($class); |
147
|
134 |
|
} |
148
|
|
|
} |
149
|
134 |
|
|
150
|
134 |
|
/** |
151
|
|
|
* Set a unique id. |
152
|
134 |
|
* |
153
|
|
|
* @return void |
154
|
|
|
*/ |
155
|
|
|
public function setId() |
156
|
|
|
{ |
157
|
|
|
self::$count++; |
158
|
|
|
$id = substr(md5(self::$count), 0, 5); |
159
|
134 |
|
$this->id = 'form_'.$id; |
160
|
|
|
} |
161
|
134 |
|
|
162
|
134 |
|
/** |
163
|
134 |
|
* Load form defaults |
164
|
134 |
|
* |
165
|
|
|
* @return void |
166
|
|
|
*/ |
167
|
|
|
public function loadDefaults() |
168
|
|
|
{ |
169
|
|
|
if (is_null($this->template)) { |
170
|
|
|
$this->setTemplate('bootstrap'); |
171
|
134 |
|
} |
172
|
|
|
} |
173
|
134 |
|
|
174
|
134 |
|
/** |
175
|
|
|
* Trigger the definition. |
176
|
134 |
|
* |
177
|
|
|
* @return void |
178
|
|
|
*/ |
179
|
|
|
public function triggerDefinition() |
180
|
|
|
{ |
181
|
|
|
if (method_exists($this, 'define')) { |
182
|
|
|
call_user_func_array([$this, 'define'], []); |
183
|
134 |
|
} |
184
|
|
|
|
185
|
134 |
|
$this->broadcast('define'); |
186
|
1 |
|
} |
187
|
|
|
|
188
|
|
|
/** |
189
|
134 |
|
* Add a new field. |
190
|
134 |
|
* |
191
|
|
|
* @param string $type |
192
|
|
|
* @param string $name |
193
|
|
|
* @return \Helmut\Forms\Fields\Field |
194
|
|
|
*/ |
195
|
|
|
public function addField($type, $name) |
196
|
|
|
{ |
197
|
|
|
if ( ! isset($this->fields[$name])) { |
198
|
|
|
$class = $this->typeToClass($type); |
199
|
118 |
|
$field = new $class($this, $type, $name); |
200
|
|
|
foreach ($field->buttons() as $button) { |
201
|
118 |
|
$this->addButton($button); |
202
|
118 |
|
} |
203
|
118 |
|
$this->fields[$name] = $field; |
204
|
118 |
|
} |
205
|
19 |
|
return $this->fields[$name]; |
206
|
|
|
} |
207
|
118 |
|
|
208
|
|
|
/** |
209
|
118 |
|
* Set all the field values. |
210
|
|
|
* |
211
|
|
|
* @return void |
212
|
|
|
*/ |
213
|
|
|
public function setValues() |
214
|
|
|
{ |
215
|
|
|
foreach ($this->fields as $field) |
216
|
|
|
{ |
217
|
113 |
|
// Set values from defaults |
218
|
|
|
$field->setFromDefault(); |
219
|
113 |
|
|
220
|
|
|
// Then load values from models |
221
|
|
|
foreach ($this->models as $model) { |
222
|
111 |
|
$field->setFromModel($model); |
223
|
|
|
} |
224
|
|
|
|
225
|
111 |
|
// Then from the current request if submitted |
226
|
9 |
|
if ($this->submitted()) { |
227
|
|
|
$field->setFromRequest($this->request); |
228
|
|
|
} |
229
|
|
|
} |
230
|
111 |
|
} |
231
|
111 |
|
|
232
|
|
|
/** |
233
|
|
|
* Add default models to the form. |
234
|
113 |
|
* |
235
|
|
|
* @return void |
236
|
|
|
*/ |
237
|
|
|
public function defaults() |
238
|
|
|
{ |
239
|
|
|
$models = func_get_args(); |
240
|
|
|
|
241
|
|
|
foreach ($models as $model) { |
242
|
9 |
|
$this->models[] = (object) $model; |
243
|
|
|
} |
244
|
9 |
|
|
245
|
|
|
} |
246
|
9 |
|
|
247
|
9 |
|
/** |
248
|
|
|
* Fetch all fields or just those matching |
249
|
|
|
* the names provided. |
250
|
9 |
|
* |
251
|
|
|
* @param array $names |
252
|
|
|
* @return array |
253
|
|
|
*/ |
254
|
|
|
public function fields($names = null) |
255
|
|
|
{ |
256
|
|
|
if (is_null($names)) { |
257
|
|
|
return $this->fields; |
258
|
|
|
} |
259
|
15 |
|
|
260
|
|
|
return array_filter($this->fields, function($field) use ($names) { |
261
|
15 |
|
return array_key_exists($field->name, $names); |
262
|
|
|
}); |
263
|
|
|
} |
264
|
|
|
|
265
|
|
|
/** |
266
|
|
|
* Get a field matching name. |
267
|
|
|
* |
268
|
|
|
* @param string $name |
269
|
|
|
* @return \Helmut\Forms\Field |
270
|
|
|
*/ |
271
|
|
|
public function field($name) |
272
|
|
|
{ |
273
|
|
|
if (isset($this->fields[$name])) { |
274
|
26 |
|
return $this->fields[$name]; |
275
|
|
|
} |
276
|
26 |
|
} |
277
|
26 |
|
|
278
|
|
|
/** |
279
|
|
|
* Fetch all of the field keys. |
280
|
|
|
* |
281
|
|
|
* @return array |
282
|
|
|
*/ |
283
|
|
|
public function keys() |
284
|
|
|
{ |
285
|
|
|
$keys = []; |
286
|
1 |
|
|
287
|
|
|
foreach ($this->fields as $field) { |
288
|
1 |
|
$keys = array_merge($keys, $field->keys()); |
289
|
|
|
} |
290
|
1 |
|
|
291
|
1 |
|
return $keys; |
292
|
|
|
} |
293
|
|
|
|
294
|
1 |
|
/** |
295
|
|
|
* Fetch all of the field values. |
296
|
|
|
* |
297
|
|
|
* @return array |
298
|
|
|
*/ |
299
|
|
|
public function all() |
300
|
|
|
{ |
301
|
|
|
$this->setValues(); |
302
|
1 |
|
|
303
|
|
|
$values = []; |
304
|
1 |
|
|
305
|
|
|
foreach ($this->fields as $field) { |
306
|
1 |
|
$values = array_merge($values, $field->values()); |
307
|
|
|
} |
308
|
1 |
|
|
309
|
1 |
|
return $values; |
310
|
|
|
} |
311
|
|
|
|
312
|
1 |
|
/** |
313
|
|
|
* Fetch the value of a field. |
314
|
|
|
* |
315
|
|
|
* @param string $name |
316
|
|
|
* @param string $key |
317
|
|
|
* @return array |
318
|
|
|
*/ |
319
|
|
|
public function get($name, $key = null) |
320
|
|
|
{ |
321
|
|
|
$this->setValues(); |
322
|
25 |
|
|
323
|
|
|
$field = $this->field($name); |
324
|
25 |
|
|
325
|
|
|
if ( ! is_null($field)) { |
326
|
25 |
|
return $field->value($key); |
327
|
|
|
} |
328
|
25 |
|
} |
329
|
25 |
|
|
330
|
|
|
/** |
331
|
|
|
* Fill a model with values. |
332
|
|
|
* |
333
|
|
|
* @param object $model |
334
|
|
|
* @param array $names |
335
|
|
|
*/ |
336
|
|
|
public function fill($model, $names = null) |
337
|
|
|
{ |
338
|
|
|
$this->setValues(); |
339
|
13 |
|
|
340
|
|
|
$fields = $this->fields($names); |
341
|
13 |
|
|
342
|
|
|
foreach ($fields as $field) { |
343
|
13 |
|
$field->fillModel($model); |
344
|
|
|
} |
345
|
13 |
|
} |
346
|
13 |
|
|
347
|
|
|
/** |
348
|
13 |
|
* Check if a form has been submitted and valid in one |
349
|
|
|
* quick and easy step. |
350
|
|
|
* |
351
|
|
|
* @param string $name |
352
|
|
|
* @return bool |
353
|
|
|
*/ |
354
|
|
|
public function completed($name = null) |
355
|
|
|
{ |
356
|
|
|
if($this->submitted($name) && $this->valid()) { |
357
|
|
|
$this->broadcast('completed'); |
358
|
|
|
return true; |
359
|
|
|
} |
360
|
|
|
return false; |
361
|
|
|
} |
362
|
|
|
|
363
|
|
|
/** |
364
|
|
|
* Check if a form has been submitted. If no specific name |
365
|
|
|
* is requested check for any button. |
366
|
|
|
* |
367
|
|
|
* @param string $name |
368
|
|
|
* @return bool |
369
|
|
|
*/ |
370
|
|
|
public function submitted($name = null) |
371
|
|
|
{ |
372
|
|
|
$buttons = is_null($name) ? $this->buttons : [$name]; |
373
|
116 |
|
|
374
|
|
|
foreach ($buttons as $button) { |
375
|
116 |
|
if ($this->request->get($button) == true) { |
376
|
|
|
$this->broadcast('submitted'); |
377
|
116 |
|
return true; |
378
|
116 |
|
} |
379
|
9 |
|
} |
380
|
116 |
|
|
381
|
|
|
return false; |
382
|
|
|
} |
383
|
|
|
|
384
|
108 |
|
/** |
385
|
|
|
* Add a button. |
386
|
|
|
* |
387
|
|
|
* @param string $name |
388
|
|
|
* @return void |
389
|
|
|
*/ |
390
|
|
|
public function addButton($name) |
391
|
|
|
{ |
392
|
|
|
$this->buttons[] = $name; |
393
|
134 |
|
} |
394
|
|
|
|
395
|
134 |
|
/** |
396
|
134 |
|
* Add a new namespace. |
397
|
|
|
* |
398
|
|
|
* @param string $namespace |
399
|
|
|
* @return void |
400
|
|
|
*/ |
401
|
|
|
public function addNamespace($namespace) |
402
|
|
|
{ |
403
|
|
|
if ( ! in_array($namespace, $this->namespaces)) { |
404
|
134 |
|
array_unshift($this->namespaces, $namespace); |
405
|
|
|
} |
406
|
134 |
|
} |
407
|
134 |
|
|
408
|
|
|
/** |
409
|
134 |
|
* Get namespaces. |
410
|
|
|
* |
411
|
|
|
* @return array |
412
|
|
|
*/ |
413
|
|
|
public function namespaces() |
414
|
|
|
{ |
415
|
|
|
return $this->namespaces; |
416
|
2 |
|
} |
417
|
|
|
|
418
|
2 |
|
/** |
419
|
|
|
* Add a new autoload path. |
420
|
|
|
* |
421
|
|
|
* @param string $path |
422
|
|
|
* @return void |
423
|
|
|
*/ |
424
|
|
|
public function addPath($path) |
425
|
|
|
{ |
426
|
|
|
$path = rtrim($path, '/').'/'; |
427
|
134 |
|
|
428
|
|
|
if ( ! in_array($path, $this->paths) && is_dir($path)) { |
429
|
134 |
|
array_unshift($this->paths, $path); |
430
|
|
|
} |
431
|
134 |
|
} |
432
|
134 |
|
|
433
|
|
|
/** |
434
|
134 |
|
* Find and add a new namespace using a class. |
435
|
|
|
* |
436
|
|
|
* @param string $class |
437
|
|
|
* @return void |
438
|
|
|
*/ |
439
|
|
|
public function addNamespaceForClass($class) |
440
|
|
|
{ |
441
|
|
|
$namespace = Reflect::getNamespace($class); |
442
|
134 |
|
|
443
|
|
|
if ( ! is_null($namespace)) { |
444
|
134 |
|
$this->addNamespace($namespace); |
445
|
|
|
} |
446
|
134 |
|
|
447
|
|
|
$directory = Reflect::getDirectory($class); |
448
|
134 |
|
|
449
|
134 |
|
if ( ! is_null($directory) && is_dir($directory)) { |
450
|
|
|
$this->addPath($directory); |
451
|
|
|
} |
452
|
134 |
|
} |
453
|
134 |
|
|
454
|
|
|
/** |
455
|
|
|
* Check if a type exists. |
456
|
|
|
* |
457
|
|
|
* @param string $type |
458
|
|
|
* @return string |
459
|
|
|
*/ |
460
|
|
|
public function typeExists($type) |
461
|
118 |
|
{ |
462
|
|
|
return ! is_null($this->typeToClass($type)); |
463
|
118 |
|
} |
464
|
|
|
|
465
|
|
|
/** |
466
|
|
|
* Convert a string type to full class name. |
467
|
|
|
* |
468
|
|
|
* @param string $type |
469
|
|
|
* @return string |
470
|
|
|
*/ |
471
|
|
|
public function typeToClass($type) |
472
|
118 |
|
{ |
473
|
|
|
$class = Str::studly($type); |
474
|
118 |
|
|
475
|
|
|
$class = '\\Fields\\'.ucwords($class).'\\'.ucwords($class); |
476
|
118 |
|
|
477
|
|
|
foreach ($this->namespaces as $namespace) { |
478
|
118 |
|
if (class_exists($namespace.$class)) { |
479
|
118 |
|
return $namespace.$class; |
480
|
|
|
} |
481
|
10 |
|
} |
482
|
|
|
} |
483
|
|
|
|
484
|
|
|
/** |
485
|
|
|
* Get the validation errors. With option to |
486
|
|
|
* get only for a specific field. |
487
|
|
|
* |
488
|
|
|
* @param string $name |
489
|
|
|
* @return array |
490
|
|
|
*/ |
491
|
|
|
public function errors($name = null) |
492
|
|
|
{ |
493
|
|
|
if ($this->submitted()) { |
494
|
|
|
$this->validate(); |
495
|
|
|
} |
496
|
|
|
|
497
|
|
|
if ( ! is_null($name)) { |
498
|
|
|
return $this->field($name)->errors(); |
499
|
|
|
} |
500
|
|
|
|
501
|
|
|
$errors = []; |
502
|
|
|
|
503
|
|
|
foreach ($this->fields as $field) { |
504
|
|
|
foreach($field->errors() as $error) { |
505
|
|
|
$errors[] = $error; |
506
|
|
|
} |
507
|
|
|
} |
508
|
|
|
|
509
|
|
|
return $errors; |
510
|
|
|
} |
511
|
|
|
|
512
|
28 |
|
/** |
513
|
|
|
* Perform validation on the form. |
514
|
28 |
|
* |
515
|
|
|
* @return void |
516
|
28 |
|
*/ |
517
|
|
|
public function validate() |
518
|
28 |
|
{ |
519
|
28 |
|
$this->setValues(); |
520
|
|
|
|
521
|
|
|
$this->broadcast('validate'); |
522
|
28 |
|
|
523
|
28 |
|
foreach ($this->fields as $field) { |
524
|
|
|
$field->runValidations(); |
525
|
|
|
} |
526
|
|
|
|
527
|
|
|
$this->broadcast('validated'); |
528
|
|
|
} |
529
|
|
|
|
530
|
|
|
/** |
531
|
|
|
* Perform validation on the form. With option |
532
|
27 |
|
* to check if a particular field is valid. |
533
|
|
|
* |
534
|
27 |
|
* @param string $name |
535
|
|
|
* @return bool |
536
|
27 |
|
*/ |
537
|
|
|
public function valid($name = null) |
538
|
27 |
|
{ |
539
|
|
|
$this->validate(); |
540
|
27 |
|
|
541
|
26 |
|
$fields = is_null($name) ? $this->fields : [$this->field($name)]; |
542
|
27 |
|
|
543
|
|
|
foreach ($fields as $field) { |
544
|
|
|
|
545
|
|
|
if ($field->isInvalid()) { |
546
|
27 |
|
$this->broadcast('invalid'); |
547
|
27 |
|
return false; |
548
|
|
|
} |
549
|
|
|
} |
550
|
|
|
|
551
|
|
|
$this->broadcast('valid'); |
552
|
|
|
return true; |
553
|
|
|
} |
554
|
|
|
|
555
|
|
|
/** |
556
|
|
|
* The opposite of valid. |
557
|
|
|
* |
558
|
|
|
* @param string $name |
559
|
|
|
* @return bool |
560
|
|
|
*/ |
561
|
|
|
public function invalid($name = null) |
562
|
|
|
{ |
563
|
|
|
return ! $this->valid($name); |
564
|
|
|
} |
565
|
|
|
|
566
|
|
|
/** |
567
|
1 |
|
* Set the active language. |
568
|
|
|
* |
569
|
1 |
|
* @param string $lang |
570
|
1 |
|
* @return void |
571
|
|
|
*/ |
572
|
|
|
public function setLanguage($lang) |
573
|
|
|
{ |
574
|
|
|
$this->lang = $lang; |
575
|
|
|
} |
576
|
|
|
|
577
|
|
|
/** |
578
|
134 |
|
* Set the template package. |
579
|
|
|
* |
580
|
134 |
|
* @param string $template |
581
|
4 |
|
* @return void |
582
|
|
|
*/ |
583
|
|
|
public function setTemplate($template) |
584
|
134 |
|
{ |
585
|
|
|
if ( ! is_null($this->template)) { |
586
|
134 |
|
$this->uninstallTemplate($this->template); |
587
|
134 |
|
} |
588
|
|
|
|
589
|
|
|
$this->installTemplate($template); |
590
|
|
|
|
591
|
|
|
$this->template = $template; |
592
|
|
|
} |
593
|
|
|
|
594
|
|
|
/** |
595
|
134 |
|
* Get template configuration. |
596
|
|
|
* |
597
|
134 |
|
* @param string $template |
598
|
|
|
* @return array |
599
|
134 |
|
*/ |
600
|
|
|
public function templateConfig($template) |
601
|
134 |
|
{ |
602
|
|
|
$paths = $this->paths(null, 'templates/'.$template); |
603
|
134 |
|
|
604
|
134 |
|
foreach ($paths as $path) { |
605
|
|
|
|
606
|
|
|
$config = $path.'config.php'; |
607
|
|
|
|
608
|
4 |
|
if (file_exists($config)) { |
609
|
|
|
return include($config); |
610
|
|
|
} |
611
|
|
|
} |
612
|
|
|
|
613
|
|
|
return []; |
614
|
|
|
} |
615
|
|
|
|
616
|
|
|
/** |
617
|
134 |
|
* Install template configuration. |
618
|
|
|
* |
619
|
134 |
|
* @param string $template |
620
|
|
|
* @return void |
621
|
134 |
|
*/ |
622
|
134 |
|
public function installTemplate($template) |
623
|
134 |
|
{ |
624
|
|
|
$config = $this->templateConfig($template); |
625
|
|
|
|
626
|
134 |
|
if ( isset($config['plugins']) && is_array($config['plugins'])) { |
627
|
|
|
foreach($config['plugins'] as $key => $plugin) { |
628
|
|
|
if (is_array($plugin)) { |
629
|
|
|
$this->addPlugin($key, $plugin); |
630
|
|
|
} else { |
631
|
|
|
$this->addPlugin($plugin); |
632
|
|
|
} |
633
|
|
|
} |
634
|
4 |
|
} |
635
|
|
|
} |
636
|
4 |
|
|
637
|
|
|
/** |
638
|
4 |
|
* Uninstall template configuration. |
639
|
4 |
|
* |
640
|
4 |
|
* @param string $template |
641
|
|
|
* @return void |
642
|
|
|
*/ |
643
|
4 |
|
public function uninstallTemplate($template) |
644
|
|
|
{ |
645
|
|
|
$config = $this->templateConfig($template); |
646
|
|
|
|
647
|
|
|
if ( isset($config['plugins']) && is_array($config['plugins'])) { |
648
|
|
|
foreach($config['plugins'] as $plugin) { |
649
|
|
|
$this->removePlugin($plugin); |
650
|
|
|
} |
651
|
45 |
|
} |
652
|
|
|
} |
653
|
|
|
|
654
|
45 |
|
/** |
655
|
45 |
|
* Fetch a fields properties. |
656
|
45 |
|
* |
657
|
45 |
|
* @param \Helmut\Forms\Field $field |
658
|
45 |
|
* @return string |
659
|
45 |
|
*/ |
660
|
45 |
|
public function fieldProperties($field) |
661
|
45 |
|
{ |
662
|
|
|
$properties = [ |
663
|
|
|
'id' => $field->id, |
664
|
|
|
'form_id' => $this->id, |
665
|
|
|
'type' => $field->type, |
666
|
45 |
|
'name' => $field->name, |
667
|
|
|
'label' => $field->label, |
668
|
45 |
|
'required' => $field->isRequired(), |
669
|
44 |
|
'valid' => $field->isValid(), |
670
|
|
|
'invalid' => $field->isInvalid(), |
671
|
|
|
'errors' => [], |
672
|
45 |
|
'keys' => [], |
673
|
1 |
|
]; |
674
|
|
|
|
675
|
1 |
|
$properties = array_merge($properties, $field->properties()); |
676
|
|
|
|
677
|
|
|
foreach ($field->keys() as $key) { |
678
|
|
|
$properties['keys'][] = $key; |
679
|
|
|
} |
680
|
|
|
|
681
|
1 |
|
if ($field->isInvalid()) { |
682
|
|
|
foreach ($field->errors() as $error => $parameters) { |
683
|
1 |
|
|
684
|
|
|
if ($error == 'userDefined') { |
685
|
1 |
|
foreach($parameters as $message) { |
686
|
1 |
|
$properties['errors'][] = ['error' => $message]; |
687
|
1 |
|
} |
688
|
1 |
|
} else { |
689
|
|
|
|
690
|
1 |
|
$parameters['field'] = str_replace('_', ' ', $field->name); |
691
|
|
|
|
692
|
|
|
$message = $this->translate($error, $field); |
693
|
|
|
|
694
|
|
|
foreach($parameters as $key => $value) { |
695
|
45 |
|
if (is_object($value) && method_exists($value, '__toString')) { |
696
|
|
|
$value = (string) $value; |
697
|
|
|
} |
698
|
|
|
if (is_array($value)) { |
699
|
|
|
$value = implode(', ', $value); |
700
|
|
|
} |
701
|
|
|
$message = str_replace('['.$key.']', $value, $message); |
702
|
|
|
} |
703
|
|
|
|
704
|
|
|
$properties['errors'][] = ['error' => $message]; |
705
|
45 |
|
} |
706
|
|
|
} |
707
|
45 |
|
} |
708
|
|
|
|
709
|
45 |
|
return $properties; |
710
|
|
|
} |
711
|
|
|
|
712
|
|
|
/** |
713
|
|
|
* Render the field using templates. |
714
|
|
|
* |
715
|
|
|
* @param \Helmut\Forms\Field $field |
716
|
|
|
* @param array $properties |
717
|
|
|
* @return string |
718
|
|
|
*/ |
719
|
45 |
View Code Duplication |
public function renderField($field, $properties = null) |
|
|
|
|
720
|
|
|
{ |
721
|
45 |
|
if (is_null($properties)) { |
722
|
|
|
$properties = $this->fieldProperties($field); |
723
|
45 |
|
} |
724
|
|
|
|
725
|
|
|
return $this->renderTemplate($field->type, $properties, $field); |
726
|
|
|
} |
727
|
|
|
|
728
|
|
|
/** |
729
|
|
|
* Render the field using templates. |
730
|
|
|
* |
731
|
|
|
* @param \Helmut\Forms\Field $field |
732
|
47 |
|
* @param array $properties |
733
|
|
|
* @return string |
734
|
47 |
|
*/ |
735
|
|
View Code Duplication |
public function renderFieldErrors($field, $properties = null) |
|
|
|
|
736
|
47 |
|
{ |
737
|
|
|
if (is_null($properties)) { |
738
|
47 |
|
$properties = $this->fieldProperties($field); |
739
|
|
|
} |
740
|
47 |
|
|
741
|
|
|
return $this->renderTemplate('errors', $properties, $field); |
742
|
47 |
|
} |
743
|
47 |
|
|
744
|
47 |
|
/** |
745
|
47 |
|
* Render the form using templates. |
746
|
47 |
|
* |
747
|
|
|
* @param string $template |
748
|
47 |
|
* @return string |
749
|
|
|
*/ |
750
|
47 |
|
public function render($template = null) |
751
|
|
|
{ |
752
|
45 |
|
$this->broadcast('render'); |
753
|
|
|
|
754
|
45 |
|
$this->setValues(); |
755
|
|
|
|
756
|
45 |
|
if ($this->submitted()) { |
757
|
45 |
|
$this->validate(); |
758
|
|
|
} |
759
|
45 |
|
|
760
|
|
|
if ( ! is_null($template)) { |
761
|
45 |
|
$this->setTemplate($template); |
762
|
|
|
} |
763
|
|
|
|
764
|
47 |
|
$properties = []; |
765
|
|
|
$properties['id'] = $this->id; |
766
|
47 |
|
$properties['action'] = $this->action; |
767
|
|
|
$properties['csrf'] = $this->request->csrf(); |
768
|
47 |
|
$properties['fields'] = []; |
769
|
|
|
|
770
|
|
|
$renderered_form = $this->renderTemplate('csrf', $this->request->csrf()); |
771
|
|
|
|
772
|
|
|
foreach ($this->fields as $field) { |
773
|
|
|
|
774
|
|
|
$field_properties = $this->fieldProperties($field); |
775
|
|
|
|
776
|
|
|
$properties['fields'][] = $field_properties; |
777
|
|
|
|
778
|
|
|
$rendered_field = $this->renderField($field, $field_properties); |
779
|
47 |
|
$rendered_field .= $this->renderFieldErrors($field, $field_properties); |
780
|
|
|
|
781
|
47 |
|
$field_properties['field'] = $rendered_field; |
782
|
|
|
|
783
|
47 |
|
$renderered_form .= $this->renderTemplate('field', $field_properties, $field); |
784
|
|
|
} |
785
|
47 |
|
|
786
|
|
|
$properties['form'] = $renderered_form; |
787
|
47 |
|
|
788
|
|
|
$renderered_template = $this->renderTemplate('form', $properties); |
789
|
47 |
|
|
790
|
|
|
return $renderered_template; |
791
|
|
|
} |
792
|
|
|
|
793
|
|
|
/** |
794
|
|
|
* Render a template. |
795
|
47 |
|
* |
796
|
|
|
* @param string $template |
797
|
|
|
* @param array $properties |
798
|
|
|
* @param \Helmut\Forms\Field $field |
799
|
|
|
* @return string |
800
|
|
|
*/ |
801
|
|
|
public function renderTemplate($template, $properties = [], $field = null) |
802
|
|
|
{ |
803
|
|
|
$paths = $this->templatePaths($field); |
804
|
|
|
|
805
|
1 |
|
$properties['lang'] = $this->translations($field); |
806
|
|
|
|
807
|
1 |
|
$rendered_template = $this->renderer->render($template, $properties, $paths); |
808
|
|
|
|
809
|
1 |
|
if (count($this->plugins)) { |
810
|
1 |
|
|
811
|
|
|
$properties[$template] = $rendered_template; |
812
|
|
|
|
813
|
|
|
foreach ($this->plugins as $key=>$plugin) { |
814
|
|
|
if ($this->renderer->has($template, $plugin->templatePaths())) { |
815
|
|
|
$rendered_template = $this->renderer->render($template, $properties, $plugin->templatePaths()); |
816
|
|
|
$properties[$template] = $rendered_template; |
817
|
|
|
} |
818
|
|
|
} |
819
|
|
|
} |
820
|
|
|
|
821
|
|
|
return $rendered_template; |
822
|
47 |
|
} |
823
|
|
|
|
824
|
47 |
|
/** |
825
|
|
|
* Translate a specific key. |
826
|
47 |
|
* |
827
|
47 |
|
* @param string $key |
828
|
47 |
|
* @param \Helmut\Forms\Field $field |
829
|
|
|
* @return string |
830
|
|
|
*/ |
831
|
47 |
|
public function translate($key, $field = null) |
832
|
|
|
{ |
833
|
47 |
|
$translations = $this->translations($field); |
834
|
|
|
|
835
|
45 |
|
if (isset($translations[$key])) { |
836
|
|
|
return $translations[$key]; |
837
|
45 |
|
} |
838
|
45 |
|
|
839
|
|
|
throw new \Exception('No translation found for '.$key); |
840
|
|
|
} |
841
|
45 |
|
|
842
|
|
|
/** |
843
|
45 |
|
* Get translations and cache them if necessary. |
844
|
|
|
* |
845
|
|
|
* @param \Helmut\Forms\Field $field |
846
|
45 |
|
* @return array |
847
|
|
|
*/ |
848
|
|
|
public function translations($field = null) |
849
|
47 |
|
{ |
850
|
|
|
$lang = $this->lang; |
851
|
|
|
|
852
|
|
|
if ( ! isset($this->translations[$lang])) { |
853
|
|
|
$paths = $this->langPaths(); |
854
|
|
|
$this->translations[$lang] = $this->loadTranslations($lang, $paths); |
855
|
|
|
} |
856
|
|
|
|
857
|
|
|
$translations = $this->translations[$lang]; |
858
|
|
|
|
859
|
47 |
|
if ( ! is_null($field)) { |
860
|
|
|
|
861
|
47 |
|
if ( ! isset($this->translations[$field->type][$lang])) { |
862
|
|
|
|
863
|
47 |
|
if ( ! isset($this->translations[$field->type])) { |
864
|
47 |
|
$this->translations[$field->type] = []; |
865
|
47 |
|
} |
866
|
47 |
|
|
867
|
|
|
$paths = $this->langPaths($field); |
868
|
|
|
|
869
|
|
|
$paths = array_reverse($paths); |
870
|
47 |
|
|
871
|
|
|
$this->translations[$field->type][$lang] = $this->loadTranslations($lang, $paths); |
872
|
|
|
} |
873
|
|
|
|
874
|
|
|
$translations = array_merge($translations, $this->translations[$field->type][$lang]); |
875
|
|
|
|
876
|
|
|
} |
877
|
|
|
|
878
|
|
|
return $translations; |
879
|
134 |
|
} |
880
|
|
|
|
881
|
134 |
|
/** |
882
|
|
|
* Load the translations from paths. |
883
|
134 |
|
* |
884
|
|
|
* @param string $lang |
885
|
134 |
|
* @param array $paths |
886
|
|
|
* @return array |
887
|
134 |
|
*/ |
888
|
|
|
public function loadTranslations($lang, $paths) |
889
|
134 |
|
{ |
890
|
134 |
|
$translations = []; |
891
|
134 |
|
|
892
|
|
|
foreach ($paths as $path) { |
893
|
134 |
|
$file = $path.$lang.'.php'; |
894
|
|
|
if (file_exists($file)) { |
895
|
|
|
$translations = array_merge($translations, include($file)); |
896
|
|
|
} |
897
|
|
|
} |
898
|
|
|
|
899
|
|
|
return $translations; |
900
|
|
|
} |
901
|
|
|
|
902
|
|
|
/** |
903
|
|
|
* Add a plugin to a form. |
904
|
134 |
|
* |
905
|
|
|
* @param string $name |
906
|
134 |
|
* @param array $config |
907
|
134 |
|
* @return \Helmut\Forms\Plugin |
908
|
|
|
*/ |
909
|
134 |
|
public function addPlugin($name, $config = []) |
910
|
|
|
{ |
911
|
|
|
$class = Str::studly($name); |
912
|
|
|
|
913
|
|
|
$class = '\\Plugins\\'.ucwords($class).'\\'.ucwords($class); |
914
|
|
|
|
915
|
|
|
foreach ($this->namespaces as $namespace) { |
916
|
134 |
|
|
917
|
|
|
$plugin = $namespace.$class; |
918
|
134 |
|
|
919
|
134 |
|
if (class_exists($plugin)) { |
920
|
|
|
$this->plugins[$name] = new $plugin($config); |
921
|
134 |
|
$this->plugins[$name]->event($this, 'load'); |
922
|
|
|
|
923
|
|
|
return $this->plugins[$name]; |
924
|
|
|
} |
925
|
|
|
} |
926
|
|
|
} |
927
|
|
|
|
928
|
|
|
/** |
929
|
|
|
* Remove a plugin. |
930
|
134 |
|
* |
931
|
|
|
* @param string $name |
932
|
134 |
|
* @return void |
933
|
134 |
|
*/ |
934
|
|
|
public function removePlugin($name) |
935
|
134 |
|
{ |
936
|
|
|
if (array_key_exists($name, $this->plugins)) { |
937
|
|
|
unset($this->plugins[$name]); |
938
|
|
|
} |
939
|
|
|
} |
940
|
|
|
|
941
|
|
|
/** |
942
|
|
|
* Get a plugin instance. |
943
|
|
|
* |
944
|
47 |
|
* @param string $name |
945
|
|
|
* @return \Helmut\Forms\Plugin |
946
|
47 |
|
*/ |
947
|
|
|
public function getPlugin($name) |
948
|
|
|
{ |
949
|
|
|
return $this->plugins[$name]; |
950
|
|
|
} |
951
|
|
|
|
952
|
|
|
/** |
953
|
|
|
* Remove all plugins. |
954
|
|
|
* |
955
|
|
|
* @return void |
956
|
53 |
|
*/ |
957
|
|
|
public function removeAllPlugins() |
958
|
53 |
|
{ |
959
|
|
|
foreach ($this->plugins as $name => $plugin) { |
960
|
|
|
$this->removePlugin($name); |
961
|
|
|
} |
962
|
|
|
} |
963
|
|
|
|
964
|
|
|
/** |
965
|
|
|
* Broadcast an event. |
966
|
|
|
* |
967
|
|
|
* @param string $event |
968
|
|
|
* @param array $params |
969
|
134 |
|
* @return void |
970
|
|
|
*/ |
971
|
134 |
|
private function broadcast($event, $params = []) |
972
|
|
|
{ |
973
|
134 |
|
foreach($this->plugins as $plugin) { |
974
|
45 |
|
$plugin->event($this, $event, $params); |
975
|
|
|
} |
976
|
|
|
} |
977
|
134 |
|
|
978
|
134 |
|
/** |
979
|
134 |
|
* Get all paths to the language files or just to those |
980
|
134 |
|
* for a specific field. |
981
|
|
|
* |
982
|
|
|
* @param \Helmut\Forms\Field $field |
983
|
134 |
|
* @return array |
984
|
|
|
*/ |
985
|
|
|
public function langPaths($field = null) |
986
|
|
|
{ |
987
|
|
|
return $this->paths($field, 'lang'); |
988
|
|
|
} |
989
|
|
|
|
990
|
|
|
/** |
991
|
|
|
* Get all paths to template packages or just to the |
992
|
45 |
|
* packages for a specific field. |
993
|
|
|
* |
994
|
45 |
|
* @param \Helmut\Forms\Field $field |
995
|
|
|
* @return array |
996
|
45 |
|
*/ |
997
|
45 |
|
public function templatePaths($field = null) |
998
|
|
|
{ |
999
|
45 |
|
return $this->paths($field, 'templates/'.$this->template); |
1000
|
|
|
} |
1001
|
|
|
|
1002
|
|
|
/** |
1003
|
|
|
* Get all autoload paths or just the path |
1004
|
|
|
* of a specific resource. |
1005
|
|
|
* |
1006
|
|
|
* @param \Helmut\Forms\Field $field |
1007
|
|
|
* @param string $append |
1008
|
|
|
* @return array |
1009
|
|
|
*/ |
1010
|
|
|
public function paths($field = null, $append = null) |
1011
|
|
|
{ |
1012
|
|
|
$paths = $this->paths; |
1013
|
|
|
|
1014
|
|
|
if ( ! is_null($field)) { |
1015
|
|
|
array_unshift($paths, $this->pathForClass($field)); |
1016
|
|
|
} |
1017
|
|
|
|
1018
|
|
|
if ( ! is_null($append)) { |
1019
|
|
|
$paths = array_map(function($path) use($append) { |
1020
|
|
|
return $path .= ltrim(rtrim($append, '/'), '/').'/'; |
1021
|
|
|
}, $paths); |
1022
|
|
|
} |
1023
|
|
|
|
1024
|
|
|
return array_filter($paths, 'is_dir'); |
1025
|
|
|
} |
1026
|
|
|
|
1027
|
|
|
/** |
1028
|
|
|
* Get autoload path for a class. |
1029
|
1 |
|
* |
1030
|
|
|
* @param string|object $class |
1031
|
1 |
|
* @return string |
1032
|
|
|
*/ |
1033
|
|
|
public function pathForClass($class) |
1034
|
|
|
{ |
1035
|
|
|
$path = Reflect::getDirectory($class); |
1036
|
|
|
|
1037
|
|
|
return rtrim($path, '/').'/'; |
1038
|
|
|
} |
1039
|
|
|
|
1040
|
|
|
|
1041
|
|
|
/** |
1042
|
|
|
* Set the form action. |
1043
|
|
|
* |
1044
|
|
|
* @param string $action |
1045
|
|
|
* @return void |
1046
|
|
|
*/ |
1047
|
|
|
public function setAction($action) |
1048
|
|
|
{ |
1049
|
134 |
|
$this->action = $action; |
1050
|
|
|
} |
1051
|
134 |
|
|
1052
|
134 |
|
/** |
1053
|
|
|
* Get the active template package. |
1054
|
|
|
* |
1055
|
134 |
|
* @return string |
1056
|
|
|
*/ |
1057
|
|
|
public function getTemplate() |
1058
|
|
|
{ |
1059
|
|
|
return $this->template; |
1060
|
|
|
} |
1061
|
|
|
|
1062
|
|
|
/** |
1063
|
|
|
* Get the request implementation. |
1064
|
|
|
* |
1065
|
|
|
* @return \Helmut\Forms\Request |
1066
|
|
|
*/ |
1067
|
|
|
public function getRequest() |
1068
|
|
|
{ |
1069
|
|
|
return $this->request; |
1070
|
|
|
} |
1071
|
|
|
|
1072
|
|
|
/** |
1073
|
|
|
* Get the renderer implementation. |
1074
|
|
|
* |
1075
|
|
|
* @return \Helmut\Forms\Renderer |
1076
|
118 |
|
*/ |
1077
|
|
|
public function getRenderer() |
1078
|
118 |
|
{ |
1079
|
|
|
return $this->renderer; |
1080
|
118 |
|
} |
1081
|
118 |
|
|
1082
|
|
|
/** |
1083
|
|
|
* Get all of the plugins |
1084
|
10 |
|
* |
1085
|
|
|
* @return array |
1086
|
|
|
*/ |
1087
|
|
|
public function getPlugins() |
1088
|
|
|
{ |
1089
|
|
|
return $this->plugins; |
1090
|
|
|
} |
1091
|
|
|
|
1092
|
|
|
/** |
1093
|
|
|
* Get all of the namespaces |
1094
|
|
|
* |
1095
|
|
|
* @return array |
1096
|
|
|
*/ |
1097
|
|
|
public function getNamespaces() |
1098
|
|
|
{ |
1099
|
|
|
return $this->namespaces; |
1100
|
|
|
} |
1101
|
|
|
|
1102
|
|
|
/** |
1103
|
|
|
* Requests directly on the object could be trying to |
1104
|
|
|
* create a field so check if type exists. |
1105
|
|
|
* |
1106
|
|
|
* @param string $method |
1107
|
|
|
* @param array $parameters |
1108
|
|
|
* @return mixed |
1109
|
|
|
*/ |
1110
|
|
|
public function __call($method, $parameters) |
1111
|
|
|
{ |
1112
|
|
|
if ( ! method_exists($this, $method)) |
1113
|
|
|
{ |
1114
|
|
|
if ( $this->typeExists($method)) { |
1115
|
|
|
return $this->addField($method, array_shift($parameters)); |
1116
|
|
|
} |
1117
|
|
|
} |
1118
|
|
|
} |
1119
|
|
|
|
1120
|
|
|
/** |
1121
|
|
|
* Render the form. |
1122
|
|
|
* |
1123
|
|
|
* @return string |
1124
|
|
|
*/ |
1125
|
|
|
public function __toString() |
1126
|
|
|
{ |
1127
|
|
|
return $this->render(); |
1128
|
|
|
} |
1129
|
|
|
|
1130
|
|
|
} |
1131
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.