Completed
Push — master ( 442463...d4b8b4 )
by Kristijan
18:40 queued 17:26
created

Form::buildFormOptionsForFormBuilder()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 4

Importance

Changes 0
Metric Value
cc 4
nc 3
nop 1
dl 0
loc 16
ccs 8
cts 8
cp 1
crap 4
rs 9.7333
c 0
b 0
f 0
1
<?php
2
3
namespace Kris\LaravelFormBuilder;
4
5
use Illuminate\Contracts\Events\Dispatcher as EventDispatcher;
6
use Illuminate\Contracts\Validation\Factory as ValidatorFactory;
7
use Illuminate\Contracts\Validation\Validator;
8
use Illuminate\Http\Exceptions\HttpResponseException;
9
use Illuminate\Http\Request;
10
use Illuminate\Support\Arr;
11
use Kris\LaravelFormBuilder\Events\AfterFieldCreation;
12
use Kris\LaravelFormBuilder\Events\AfterFormValidation;
13
use Kris\LaravelFormBuilder\Events\BeforeFormValidation;
14
use Kris\LaravelFormBuilder\Fields\FormField;
15
use Kris\LaravelFormBuilder\Filters\FilterResolver;
16
17
class Form
18
{
19
    /**
20
     * All fields that are added.
21
     *
22
     * @var array
23
     */
24
    protected $fields = [];
25
26
    /**
27
     * Model to use.
28
     *
29
     * @var mixed
30
     */
31
    protected $model = [];
32
33
    /**
34
     * @var EventDispatcher
35
     */
36
    protected $eventDispatcher;
37
38
    /**
39
     * @var FormHelper
40
     */
41
    protected $formHelper;
42
43
    /**
44
     * Form options.
45
     *
46
     * @var array
47
     */
48
    protected $formOptions = [
49
        'method' => 'GET',
50
        'url' => null,
51
        'attr' => [],
52
    ];
53
54
    /**
55
     * Form specific configuration.
56
     *
57
     * @var array
58
     */
59
    protected $formConfig = [];
60
61
    /**
62
     * Additional data which can be used to build fields.
63
     *
64
     * @var array
65
     */
66
    protected $data = [];
67
68
    /**
69
     * Wether errors for each field should be shown when calling form($form) or form_rest($form).
70
     *
71
     * @var bool
72
     */
73
    protected $showFieldErrors = true;
74
75
    /**
76
     * Enable html5 validation.
77
     *
78
     * @var bool
79
     */
80
    protected $clientValidationEnabled = true;
81
82
    /**
83
     * Name of the parent form if any.
84
     *
85
     * @var string|null
86
     */
87
    protected $name = null;
88
89
    /**
90
     * @var FormBuilder
91
     */
92
    protected $formBuilder;
93
94
    /**
95
     * @var ValidatorFactory
96
     */
97
    protected $validatorFactory;
98
99
    /**
100
     * @var Validator
101
     */
102
    protected $validator = null;
103
104
    /**
105
     * @var Request
106
     */
107
    protected $request;
108
109
    /**
110
     * List of fields to not render.
111
     *
112
     * @var array
113
     **/
114
    protected $exclude = [];
115
116
    /**
117
     * Wether the form is beign rebuild.
118
     *
119
     * @var bool
120
     */
121
    protected $rebuilding = false;
122
123
    /**
124
     * @var string
125
     */
126
    protected $templatePrefix;
127
128
    /**
129
     * @var string
130
     */
131
    protected $languageName;
132
133
    /**
134
     * @var string
135
     */
136
    protected $translationTemplate;
137
138
    /**
139
     * To filter and mutate request values or not.
140
     *
141
     * @var bool
142
     */
143
    protected $lockFiltering = false;
144
145
    /**
146
     * Define the error bag name for the form.
147
     *
148
     * @var string
149
     */
150
    protected $errorBag = 'default';
151
152
    /**
153
     * Build the form.
154
     *
155
     * @return mixed
156
     */
157 3
    public function buildForm()
158
    {
159 3
    }
160
161
    /**
162
     * Rebuild the form from scratch.
163
     *
164
     * @return $this
165
     */
166 19
    public function rebuildForm()
167
    {
168 19
        $this->rebuilding = true;
169
        // If form is plain, buildForm method is empty, so we need to take
170
        // existing fields and add them again
171 19
        if (get_class($this) === 'Kris\LaravelFormBuilder\Form') {
172 18
            foreach ($this->fields as $name => $field) {
173
                // Remove any temp variables added in previous instance
174 7
                $options =  Arr::except($field->getOptions(), 'tmp');
175 7
                $this->add($name, $field->getType(), $options);
176
            }
177
        } else {
178 3
            $this->buildForm();
179
        }
180 19
        $this->rebuilding = false;
181
182 19
        return $this;
183
    }
184
185
    /**
186
     * Create the FormField object.
187
     *
188
     * @param string $name
189
     * @param string $type
190
     * @param array  $options
191
     * @return FormField
192
     */
193 66
    protected function makeField($name, $type = 'text', array $options = [])
194
    {
195 66
        $this->setupFieldOptions($name, $options);
196
197 66
        $fieldName = $this->getFieldName($name);
198
199 66
        $fieldType = $this->getFieldType($type);
200
201 65
        $field = new $fieldType($fieldName, $type, $this, $options);
202
203 62
        $this->eventDispatcher->dispatch(new AfterFieldCreation($this, $field));
204
205 62
        return $field;
206
    }
207
208
    /**
209
     * Create a new field and add it to the form.
210
     *
211
     * @param string $name
212
     * @param string $type
213
     * @param array  $options
214
     * @param bool   $modify
215
     * @return $this
216
     */
217 68
    public function add($name, $type = 'text', array $options = [], $modify = false)
218
    {
219 68
        $this->formHelper->checkFieldName($name, get_class($this));
220
221 66
        if ($this->rebuilding && !$this->has($name)) {
222
            return $this;
223
        }
224
225 66
        $this->addField($this->makeField($name, $type, $options), $modify);
226
227 62
        return $this;
228
    }
229
230
    /**
231
     * Add a FormField to the form's fields.
232
     *
233
     * @param FormField $field
234
     * @return $this
235
     */
236 62
    protected function addField(FormField $field, $modify = false)
237
    {
238 62
        if (!$modify && !$this->rebuilding) {
239 62
            $this->preventDuplicate($field->getRealName());
240
        }
241
242
243 62
        if ($field->getType() == 'file') {
244 3
            $this->formOptions['files'] = true;
245
        }
246
247 62
        $this->fields[$field->getRealName()] = $field;
248
249 62
        return $this;
250
    }
251
252
    /**
253
     * Add field before another field.
254
     *
255
     * @param string  $name         Name of the field before which new field is added.
256
     * @param string  $fieldName    Field name which will be added.
257
     * @param string  $type
258
     * @param array   $options
259
     * @param bool $modify
260
     * @return $this
261
     */
262 1
    public function addBefore($name, $fieldName, $type = 'text', $options = [], $modify = false)
263
    {
264 1
        $offset = array_search($name, array_keys($this->fields));
265
266 1
        $beforeFields = array_slice($this->fields, 0, $offset);
267 1
        $afterFields = array_slice($this->fields, $offset);
268
269 1
        $this->fields = $beforeFields;
270
271 1
        $this->add($fieldName, $type, $options, $modify);
272
273 1
        $this->fields += $afterFields;
274
275 1
        return $this;
276
    }
277
278
    /**
279
     * Add field before another field.
280
     *
281
     * @param string  $name         Name of the field after which new field is added.
282
     * @param string  $fieldName    Field name which will be added.
283
     * @param string  $type
284
     * @param array   $options
285
     * @param bool $modify
286
     * @return $this
287
     */
288 1
    public function addAfter($name, $fieldName, $type = 'text', $options = [], $modify = false)
289
    {
290 1
        $offset = array_search($name, array_keys($this->fields));
291
292 1
        $beforeFields = array_slice($this->fields, 0, $offset + 1);
293 1
        $afterFields = array_slice($this->fields, $offset + 1);
294
295 1
        $this->fields = $beforeFields;
296
297 1
        $this->add($fieldName, $type, $options, $modify);
298
299 1
        $this->fields += $afterFields;
300
301 1
        return $this;
302
    }
303
304
    /**
305
     * Take another form and add it's fields directly to this form.
306
     *
307
     * @param mixed   $class        Form to merge.
308
     * @param array   $options
309
     * @param boolean $modify
310
     * @return $this
311
     */
312 1
    public function compose($class, array $options = [], $modify = false)
313
    {
314 1
        $options['class'] = $class;
315
316
        // If we pass a ready made form just extract the fields.
317 1
        if ($class instanceof Form) {
318 1
            $fields = $class->getFields();
319
        } elseif ($class instanceof Fields\ChildFormType) {
320
            $fields = $class->getForm()->getFields();
321
        } elseif (is_string($class)) {
322
            // If its a string of a class make it the usual way.
323
            $options['model'] = $this->model;
324
            $options['name'] = $this->name;
325
326
            $form = $this->formBuilder->create($class, $options);
327
            $fields = $form->getFields();
328
        } else {
329
            throw new \InvalidArgumentException(
330
                "[{$class}] is invalid. Please provide either a full class name, Form or ChildFormType"
331
            );
332
        }
333
334 1
        foreach ($fields as $field) {
335 1
            $this->addField($field, $modify);
336
        }
337
338 1
        return $this;
339
    }
340
341
    /**
342
     * Remove field with specified name from the form.
343
     *
344
     * @param string|string[] $names
345
     * @return $this
346
     */
347 2
    public function remove($names)
348
    {
349 2
        foreach (is_array($names) ? $names : func_get_args() as $name) {
350 2
            if ($this->has($name)) {
351 2
                unset($this->fields[$name]);
352
            }
353
        }
354
355 2
        return $this;
356
    }
357
358
    /**
359
     * Take only the given fields from the form.
360
     *
361
     * @param string|string[] $fieldNames
362
     * @return $this
363
     */
364 1
    public function only($fieldNames)
365
    {
366 1
        $newFields = [];
367
368 1
        foreach (is_array($fieldNames) ? $fieldNames : func_get_args() as $fieldName) {
369 1
            $newFields[$fieldName] = $this->getField($fieldName);
370
        }
371
372 1
        $this->fields = $newFields;
373
374 1
        return $this;
375
    }
376
377
    /**
378
     * Modify existing field. If it doesn't exist, it is added to form.
379
     *
380
     * @param string $name
381
     * @param string $type
382
     * @param array  $options
383
     * @param bool   $overwriteOptions
384
     * @return Form
385
     */
386 1
    public function modify($name, $type = 'text', array $options = [], $overwriteOptions = false)
387
    {
388
        // If we don't want to overwrite options, we merge them with old options.
389 1
        if ($overwriteOptions === false && $this->has($name)) {
390 1
            $options = $this->formHelper->mergeOptions(
391 1
                $this->getField($name)->getOptions(),
392 1
                $options
393
            );
394
        }
395
396 1
        return $this->add($name, $type, $options, true);
397
    }
398
399
    /**
400
     * Render full form.
401
     *
402
     * @param array $options
403
     * @param bool  $showStart
404
     * @param bool  $showFields
405
     * @param bool  $showEnd
406
     * @return string
407
     */
408 7
    public function renderForm(array $options = [], $showStart = true, $showFields = true, $showEnd = true)
409
    {
410 7
        return $this->render($options, $this->fields, $showStart, $showFields, $showEnd);
0 ignored issues
show
Documentation introduced by
$this->fields is of type array, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
411
    }
412
413
    /**
414
     * Render rest of the form.
415
     *
416
     * @param bool $showFormEnd
417
     * @param bool $showFields
418
     * @return string
419
     */
420 1
    public function renderRest($showFormEnd = true, $showFields = true)
421
    {
422 1
        $fields = $this->getUnrenderedFields();
423
424 1
        return $this->render([], $fields, false, $showFields, $showFormEnd);
0 ignored issues
show
Documentation introduced by
$fields is of type array, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
425
    }
426
427
    /**
428
     * Renders the rest of the form up until the specified field name.
429
     *
430
     * @param string $field_name
431
     * @param bool   $showFormEnd
432
     * @param bool   $showFields
433
     * @return string
434
     */
435 2
    public function renderUntil($field_name, $showFormEnd = true, $showFields = true)
436
    {
437 2
        if (!$this->has($field_name)) {
438 1
            $this->fieldDoesNotExist($field_name);
439
        }
440
441 1
        $fields = $this->getUnrenderedFields();
442
443 1
        $i = 1;
444 1
        foreach ($fields as $key => $value) {
445 1
            if ($value->getRealName() == $field_name) {
446 1
                break;
447
            }
448 1
            $i++;
449
        }
450
451 1
        $fields = array_slice($fields, 0, $i, true);
452
453 1
        return $this->render([], $fields, false, $showFields, $showFormEnd);
0 ignored issues
show
Documentation introduced by
$fields is of type array, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
454
    }
455
456
    /**
457
     * Get single field instance from form object.
458
     *
459
     * @param string $name
460
     * @return FormField
461
     */
462 36
    public function getField($name)
463
    {
464 36
        if ($this->has($name)) {
465 35
            return $this->fields[$name];
466
        }
467
468 1
        $this->fieldDoesNotExist($name);
469
    }
470
471 102
    public function getErrorBag()
472
    {
473 102
        return $this->errorBag;
474
    }
475
476
    /**
477
     * Check if form has field.
478
     *
479
     * @param string $name
480
     * @return bool
481
     */
482 62
    public function has($name)
483
    {
484 62
        return array_key_exists($name, $this->fields);
485
    }
486
487
    /**
488
     * Get all form options.
489
     *
490
     * @return array
491
     */
492 2
    public function getFormOptions()
493
    {
494 2
        return $this->formOptions;
495
    }
496
497
    /**
498
     * Get single form option.
499
     *
500
     * @param string $option
501
     * @param mixed|null $default
502
     * @return mixed
503
     */
504 130
    public function getFormOption($option, $default = null)
505
    {
506 130
        return Arr::get($this->formOptions, $option, $default);
507
    }
508
509
    /**
510
     * Set single form option on form.
511
     *
512
     * @param string $option
513
     * @param mixed $value
514
     *
515
     * @return $this
516
     */
517 2
    public function setFormOption($option, $value)
518
    {
519 2
        $this->formOptions[$option] = $value;
520
521 2
        return $this;
522
    }
523
524
    /**
525
     * Get the passed config key using the custom
526
     * form config, if any.
527
     *
528
     * @param string $key
529
     * @param mixed $default
530
     *
531
     * @return mixed
532
     */
533 104
    public function getConfig($key = null, $default = null)
534
    {
535 104
        return $this->formHelper->getConfig($key, $default, $this->formConfig);
536
    }
537
538
    /**
539
     * Set form options.
540
     *
541
     * @param array $formOptions
542
     * @return $this
543
     */
544 130
    public function setFormOptions(array $formOptions)
545
    {
546 130
        $this->formOptions = $this->formHelper->mergeOptions($this->formOptions, $formOptions);
547 130
        $this->checkIfNamedForm();
548 130
        $this->pullFromOptions('data', 'addData');
549 130
        $this->pullFromOptions('model', 'setupModel');
550 130
        $this->pullFromOptions('errors_enabled', 'setErrorsEnabled');
551 130
        $this->pullFromOptions('client_validation', 'setClientValidationEnabled');
552 130
        $this->pullFromOptions('template_prefix', 'setTemplatePrefix');
553 130
        $this->pullFromOptions('language_name', 'setLanguageName');
554 130
        $this->pullFromOptions('translation_template', 'setTranslationTemplate');
555
556 130
        return $this;
557
    }
558
559
    /**
560
     * Get an option from provided options and call method with that value.
561
     *
562
     * @param string $name
563
     * @param string $method
564
     */
565 130
    protected function pullFromOptions($name, $method)
566
    {
567 130
        if (Arr::get($this->formOptions, $name) !== null) {
568 20
            $this->{$method}(Arr::pull($this->formOptions, $name));
569
        }
570 130
    }
571
572
    /**
573
     * Get form http method.
574
     *
575
     * @return string
576
     */
577 3
    public function getMethod()
578
    {
579 3
        return $this->formOptions['method'];
580
    }
581
582
    /**
583
     * Set form http method.
584
     *
585
     * @param string $method
586
     * @return $this
587
     */
588 1
    public function setMethod($method)
589
    {
590 1
        $this->formOptions['method'] = $method;
591
592 1
        return $this;
593
    }
594
595
    /**
596
     * Get form action url.
597
     *
598
     * @return string
599
     */
600 3
    public function getUrl()
601
    {
602 3
        return $this->formOptions['url'];
603
    }
604
605
    /**
606
     * Set form action url.
607
     *
608
     * @param string $url
609
     * @return $this
610
     */
611 1
    public function setUrl($url)
612
    {
613 1
        $this->formOptions['url'] = $url;
614
615 1
        return $this;
616
    }
617
618
    /**
619
     * Returns the name of the form.
620
     *
621
     * @return string|null
622
     */
623 70
    public function getName()
624
    {
625 70
        return $this->name;
626
    }
627
628
    /**
629
     * Get dot notation key for the form.
630
     *
631
     * @return string
632
     **/
633 13
    public function getNameKey()
634
    {
635 13
        return $this->formHelper->transformToDotSyntax($this->name);
636
    }
637
638
    /**
639
     * Set the name of the form.
640
     *
641
     * @param string $name
642
     * @param bool $rebuild
643
     * @return $this
644
     */
645 12
    public function setName($name, $rebuild = true)
646
    {
647 12
        $this->name = $name;
648
649 12
        if ($rebuild) {
650 12
            $this->rebuildForm();
651
        }
652
653 12
        return $this;
654
    }
655
656
    /**
657
     * Get model that is bind to form object.
658
     *
659
     * @return mixed
660
     */
661 97
    public function getModel()
662
    {
663 97
        return $this->model;
664
    }
665
666
    /**
667
     * Set model to form object.
668
     *
669
     * @param mixed $model
670
     * @return $this
671
     * @deprecated deprecated since 1.6.31, will be removed in 1.7 - pass model as option when creating a form
672
     */
673 17
    public function setModel($model)
674
    {
675 17
        $this->model = $model;
676
677 17
        $this->rebuildForm();
678
679 17
        return $this;
680
    }
681
682
    /**
683
     * Setup model for form, add namespace if needed for child forms.
684
     *
685
     * @return $this
686
     */
687 12
    protected function setupModel($model)
688
    {
689 12
        $this->model = $model;
690 12
        $this->setupNamedModel();
691
692 12
        return $this;
693
    }
694
695
    /**
696
     * Get all fields.
697
     *
698
     * @return FormField[]
699
     */
700 130
    public function getFields()
701
    {
702 130
        return $this->fields;
703
    }
704
705
    /**
706
     * Get field dynamically.
707
     *
708
     * @param string $name
709
     * @return FormField
710
     */
711 20
    public function __get($name)
712
    {
713 20
        if ($this->has($name)) {
714 19
            return $this->getField($name);
715
        }
716 3
    }
717
718
    /**
719
     * Check if field exists when fetched using magic methods.
720
     *
721
     * @param string $name
722
     * @return bool
723
     */
724
    public function __isset($name)
725
    {
726
        return $this->has($name);
727
    }
728
729
    /**
730
     * Set the Event Dispatcher to fire Laravel events.
731
     *
732
     * @param EventDispatcher $eventDispatcher
733
     * @return $this
734
     */
735 130
    public function setEventDispatcher(EventDispatcher $eventDispatcher)
736
    {
737 130
        $this->eventDispatcher = $eventDispatcher;
738
739 130
        return $this;
740
    }
741
742
    /**
743
     * Set the form helper only on first instantiation.
744
     *
745
     * @param FormHelper $formHelper
746
     * @return $this
747
     */
748 130
    public function setFormHelper(FormHelper $formHelper)
749
    {
750 130
        $this->formHelper = $formHelper;
751
752 130
        return $this;
753
    }
754
755
    /**
756
     * Get form helper.
757
     *
758
     * @return FormHelper
759
     */
760 102
    public function getFormHelper()
761
    {
762 102
        return $this->formHelper;
763
    }
764
765
    /**
766
     * Add custom field.
767
     *
768
     * @param $name
769
     * @param $class
770
     */
771 2
    public function addCustomField($name, $class)
772
    {
773 2
        if ($this->rebuilding && $this->formHelper->hasCustomField($name)) {
774
            return $this;
775
        }
776
777 2
        $this->formHelper->addCustomField($name, $class);
778 2
    }
779
780
    /**
781
     * Returns wether form errors should be shown under every field.
782
     *
783
     * @return bool
784
     */
785 102
    public function haveErrorsEnabled()
786
    {
787 102
        return $this->showFieldErrors;
788
    }
789
790
    /**
791
     * Enable or disable showing errors under fields
792
     *
793
     * @param bool $enabled
794
     * @return $this
795
     */
796 1
    public function setErrorsEnabled($enabled)
797
    {
798 1
        $this->showFieldErrors = (bool) $enabled;
799
800 1
        return $this;
801
    }
802
803
    /**
804
     * Is client validation enabled?
805
     *
806
     * @return bool
807
     */
808 102
    public function clientValidationEnabled()
809
    {
810 102
        return $this->clientValidationEnabled;
811
    }
812
813
    /**
814
     * Enable/disable client validation.
815
     *
816
     * @param bool $enable
817
     * @return $this
818
     */
819 2
    public function setClientValidationEnabled($enable)
820
    {
821 2
        $this->clientValidationEnabled = (bool) $enable;
822
823 2
        return $this;
824
    }
825
826
    /**
827
     * Add any aditional data that field needs (ex. array of choices).
828
     *
829
     * @deprecated deprecated since 1.6.20, will be removed in 1.7 - use 3rd param on create, or 2nd on plain method to pass data
830
     * will be switched to protected in 1.7.
831
     * @param string $name
832
     * @param mixed $data
833
     */
834 1
    public function setData($name, $data)
835
    {
836 1
        $this->data[$name] = $data;
837 1
    }
838
839
    /**
840
     * Get single additional data.
841
     *
842
     * @param string $name
843
     * @param null   $default
844
     * @return mixed
845
     */
846 20
    public function getData($name = null, $default = null)
847
    {
848 20
        if (is_null($name)) {
849 19
            return $this->data;
850
        }
851
852 1
        return Arr::get($this->data, $name, $default);
853
    }
854
855
    /**
856
     * Add multiple peices of data at once.
857
     *
858
     * @deprecated deprecated since 1.6.12, will be removed in 1.7 - use 3rd param on create, or 2nd on plain method to pass data
859
     * will be switched to protected in 1.7.
860
     * @param $data
861
     * @return $this
862
     **/
863 130
    public function addData(array $data)
864
    {
865 130
        foreach ($data as $key => $value) {
866 1
            $this->setData($key, $value);
0 ignored issues
show
Deprecated Code introduced by
The method Kris\LaravelFormBuilder\Form::setData() has been deprecated with message: deprecated since 1.6.20, will be removed in 1.7 - use 3rd param on create, or 2nd on plain method to pass data
will be switched to protected in 1.7.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
867
        }
868
869 130
        return $this;
870
    }
871
872
    /**
873
     * Get current request.
874
     *
875
     * @return \Illuminate\Http\Request
876
     */
877 102
    public function getRequest()
878
    {
879 102
        return $this->request;
880
    }
881
882
    /**
883
     * Set request on form.
884
     *
885
     * @param Request $request
886
     * @return $this
887
     */
888 130
    public function setRequest(Request $request)
889
    {
890 130
        $this->request = $request;
891
892 130
        return $this;
893
    }
894
895
    /**
896
     * Get template prefix that is prepended to all template paths.
897
     *
898
     * @return string
899
     */
900 39
    public function getTemplatePrefix()
901
    {
902 39
        if ($this->templatePrefix !== null) {
903 4
            return $this->templatePrefix;
904
        }
905
906 35
        return $this->getConfig('template_prefix');
907
    }
908
909
    /**
910
     * Set a template prefix for the form and its fields.
911
     *
912
     * @param string $prefix
913
     * @return $this
914
     */
915 4
    public function setTemplatePrefix($prefix)
916
    {
917 4
        $this->templatePrefix = (string) $prefix;
918
919 4
        return $this;
920
    }
921
922
    /**
923
     * Get the language name.
924
     *
925
     * @return string
926
     */
927 99
    public function getLanguageName()
928
    {
929 99
        return $this->languageName;
930
    }
931
932
    /**
933
     * Set a language name, used as prefix for translated strings.
934
     *
935
     * @param string $prefix
936
     * @return $this
937
     */
938 13
    public function setLanguageName($prefix)
939
    {
940 13
        $this->languageName = (string) $prefix;
941
942 13
        return $this;
943
    }
944
945
    /**
946
     * Get the translation template.
947
     *
948
     * @return string
949
     */
950 101
    public function getTranslationTemplate()
951
    {
952 101
        return $this->translationTemplate;
953
    }
954
955
    /**
956
     * Set a translation template, used to determine labels for fields.
957
     *
958
     * @param string $template
959
     * @return $this
960
     */
961 11
    public function setTranslationTemplate($template)
962
    {
963 11
        $this->translationTemplate = (string) $template;
964
965 11
        return $this;
966
    }
967
968
    /**
969
     * Render the form.
970
     *
971
     * @param array $options
972
     * @param string $fields
973
     * @param bool $showStart
974
     * @param bool $showFields
975
     * @param bool $showEnd
976
     * @return string
977
     */
978 9
    protected function render($options, $fields, $showStart, $showFields, $showEnd)
979
    {
980 9
        $formOptions = $this->buildFormOptionsForFormBuilder(
981 9
            $this->formHelper->mergeOptions($this->formOptions, $options)
982
        );
983
984 9
        $this->setupNamedModel();
985
986 9
        return $this->formHelper->getView()
987 9
            ->make($this->getTemplate())
988 9
            ->with(compact('showStart', 'showFields', 'showEnd'))
989 9
            ->with('formOptions', $formOptions)
990 9
            ->with('fields', $fields)
991 9
            ->with('model', $this->getModel())
992 9
            ->with('exclude', $this->exclude)
993 9
            ->with('form', $this)
994 9
            ->render();
995
    }
996
997
    /**
998
     * @param $formOptions
999
     * @return array
1000
     */
1001 9
    protected function buildFormOptionsForFormBuilder($formOptions)
1002
    {
1003 9
        $reserved = ['method', 'url', 'route', 'action', 'files'];
1004 9
        $formAttributes = Arr::get($formOptions, 'attr', []);
1005
1006
        // move string value to `attr` to maintain backward compatibility
1007 9
        foreach ($formOptions as $key => $formOption) {
1008 9
            if (!in_array($formOption, $reserved) && is_string($formOption)) {
1009 9
                $formAttributes[$key] = $formOption;
1010
            }
1011
        }
1012
1013 9
        return array_merge(
1014 9
            $formAttributes, Arr::only($formOptions, $reserved)
1015
        );
1016
    }
1017
1018
1019
    /**
1020
     * Get template from options if provided, otherwise fallback to config.
1021
     *
1022
     * @return mixed
1023
     */
1024 9
    protected function getTemplate()
1025
    {
1026 9
        return $this->getTemplatePrefix() . $this->getFormOption('template', $this->getConfig('form'));
1027
    }
1028
1029
    /**
1030
     * Get all fields that are not rendered.
1031
     *
1032
     * @return array
1033
     */
1034 2
    protected function getUnrenderedFields()
1035
    {
1036 2
        $unrenderedFields = [];
1037
1038 2
        foreach ($this->fields as $field) {
1039 2
            if (!$field->isRendered()) {
1040 2
                $unrenderedFields[] = $field;
1041 2
                continue;
1042
            }
1043
        }
1044
1045 2
        return $unrenderedFields;
1046
    }
1047
1048
    /**
1049
     * Prevent adding fields with same name.
1050
     *
1051
     * @param string $name
1052
     * @throws \InvalidArgumentException
1053
     * @return void
1054
     */
1055 62
    protected function preventDuplicate($name)
1056
    {
1057 62
        if ($this->has($name)) {
1058 1
            throw new \InvalidArgumentException('Field ['.$name.'] already exists in the form '.get_class($this));
1059
        }
1060 62
    }
1061
1062
    /**
1063
     * Returns and checks the type of the field.
1064
     *
1065
     * @param string $type
1066
     * @return string
1067
     */
1068 66
    protected function getFieldType($type)
1069
    {
1070 66
        $fieldType = $this->formHelper->getFieldType($type);
1071
1072 65
        return $fieldType;
1073
    }
1074
1075
    /**
1076
     * Check if form is named form.
1077
     *
1078
     * @return void
1079
     */
1080 130
    protected function checkIfNamedForm()
1081
    {
1082 130
        if ($this->getFormOption('name')) {
1083 8
            $this->name = Arr::pull($this->formOptions, 'name', $this->name);
1084
        }
1085 130
    }
1086
1087
    /**
1088
     * Set up options on single field depending on form options.
1089
     *
1090
     * @param string $name
1091
     * @param $options
1092
     */
1093 66
    protected function setupFieldOptions($name, &$options)
1094
    {
1095 66
        $options['real_name'] = $name;
1096 66
    }
1097
1098
    /**
1099
     * Set namespace to model if form is named so the data is bound properly.
1100
     * Returns true if model is changed, otherwise false.
1101
     *
1102
     * @return bool
1103
     */
1104 21
    protected function setupNamedModel()
1105
    {
1106 21
        if (!$this->getModel() || !$this->getName()) {
1107 19
            return false;
1108
        }
1109
1110 3
        $dotName = $this->getNameKey();
1111 3
        $model = $this->formHelper->convertModelToArray($this->getModel());
1112 3
        $isCollectionFormModel = (bool) preg_match('/^.*\.\d+$/', $dotName);
1113 3
        $isCollectionPrototype = strpos($dotName, '__NAME__') !== false;
1114
1115 3
        if (!Arr::get($model, $dotName) && !$isCollectionFormModel && !$isCollectionPrototype) {
0 ignored issues
show
Bug introduced by
It seems like $model defined by $this->formHelper->conve...rray($this->getModel()) on line 1111 can also be of type null or object; however, Illuminate\Support\Arr::get() does only seem to accept object<ArrayAccess>|array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
1116 1
            $newModel = [];
1117 1
            Arr::set($newModel, $dotName, $model);
1118 1
            $this->model = $newModel;
1119
1120 1
            return true;
1121
        }
1122
1123 2
        return false;
1124
    }
1125
1126
    /**
1127
     * Set form builder instance on helper so we can use it later.
1128
     *
1129
     * @param FormBuilder $formBuilder
1130
     * @return $this
1131
     */
1132 130
    public function setFormBuilder(FormBuilder $formBuilder)
1133
    {
1134 130
        $this->formBuilder = $formBuilder;
1135
1136 130
        return $this;
1137
    }
1138
1139
    /**
1140
     * Returns the instance of the FormBuilder.
1141
     *
1142
     * @return FormBuilder
1143
     */
1144 20
    public function getFormBuilder()
1145
    {
1146 20
        return $this->formBuilder;
1147
    }
1148
1149
    /**
1150
     * Set the Validator instance on this so we can use it later.
1151
     *
1152
     * @param ValidatorFactory $validator
1153
     * @return $this
1154
     */
1155 130
    public function setValidator(ValidatorFactory $validator)
1156
    {
1157 130
        $this->validatorFactory = $validator;
1158
1159 130
        return $this;
1160
    }
1161
1162
    /**
1163
     * Returns the validator instance.
1164
     *
1165
     * @return Validator
1166
     */
1167 1
    public function getValidator()
1168
    {
1169 1
        return $this->validator;
1170
    }
1171
1172
    /**
1173
     * Exclude some fields from rendering.
1174
     *
1175
     * @return $this
1176
     */
1177
    public function exclude(array $fields)
1178
    {
1179
        $this->exclude = array_merge($this->exclude, $fields);
1180
1181
        return $this;
1182
    }
1183
1184
    /**
1185
     * If form is named form, modify names to be contained in single key (parent[child_field_name]).
1186
     *
1187
     * @param string $name
1188
     * @return string
1189
     */
1190 66
    protected function getFieldName($name)
1191
    {
1192 66
        $formName = $this->getName();
1193 66
        if ($formName !== null) {
1194 14
            if (strpos($formName, '[') !== false || strpos($formName, ']') !== false) {
1195 6
                return $this->formHelper->transformToBracketSyntax(
1196 6
                    $this->formHelper->transformToDotSyntax(
1197 6
                        $formName . '[' . $name . ']'
1198
                    )
1199
                );
1200
            }
1201
1202 11
            return $formName . '[' . $name . ']';
1203
        }
1204
1205 66
        return $name;
1206
    }
1207
1208
    /**
1209
     * Disable all fields in a form.
1210
     */
1211 1
    public function disableFields()
1212
    {
1213 1
        foreach ($this->fields as $field) {
1214 1
            $field->disable();
1215
        }
1216 1
    }
1217
1218
    /**
1219
     * Enable all fields in a form.
1220
     */
1221 1
    public function enableFields()
1222
    {
1223 1
        foreach ($this->fields as $field) {
1224 1
            $field->enable();
1225
        }
1226 1
    }
1227
1228
    /**
1229
     * Validate the form.
1230
     *
1231
     * @param array $validationRules
1232
     * @param array $messages
1233
     * @return Validator
1234
     */
1235 9
    public function validate($validationRules = [], $messages = [])
1236
    {
1237 9
        $fieldRules = $this->formHelper->mergeFieldsRules($this->fields);
1238 9
        $rules = array_merge($fieldRules->getRules(), $validationRules);
1239 9
        $messages = array_merge($fieldRules->getMessages(), $messages);
1240
1241 9
        $this->validator = $this->validatorFactory->make($this->getRequest()->all(), $rules, $messages);
1242 9
        $this->validator->setAttributeNames($fieldRules->getAttributes());
1243
1244 9
        $this->eventDispatcher->dispatch(new BeforeFormValidation($this, $this->validator));
1245
1246 9
        return $this->validator;
1247
    }
1248
1249
    /**
1250
     * Get validation rules for the form.
1251
     *
1252
     * @param array $overrideRules
1253
     * @return array
1254
     */
1255 1
    public function getRules($overrideRules = [])
1256
    {
1257 1
        $fieldRules = $this->formHelper->mergeFieldsRules($this->fields);
1258
1259 1
        return array_merge($fieldRules->getRules(), $overrideRules);
1260
    }
1261
1262
    /**
1263
     * Redirects to a destination when form is invalid.
1264
     *
1265
     * @param  string|null $destination The target url.
1266
     * @return HttpResponseException
1267
     */
1268 3
    public function redirectIfNotValid($destination = null)
1269
    {
1270 3
        if (! $this->isValid()) {
1271 3
            $response = redirect($destination);
1272
1273 3
            if (is_null($destination)) {
1274 2
                $response = $response->back();
1275
            }
1276
1277 3
            $response = $response->withErrors($this->getErrors(), $this->getErrorBag())->withInput();
1278
1279 3
            throw new HttpResponseException($response);
1280
        }
1281
    }
1282
1283
    /**
1284
     * Get all form field attributes, including child forms, in a flat array.
1285
     *
1286
     * @return array
1287
     */
1288 3
    public function getAllAttributes()
1289
    {
1290 3
        return $this->formHelper->mergeAttributes($this->fields);
1291
    }
1292
1293
    /**
1294
     * Check if the form is valid.
1295
     *
1296
     * @return bool
1297
     */
1298 9
    public function isValid()
1299
    {
1300 9
        if (!$this->validator) {
1301 8
            $this->validate();
1302
        }
1303
1304 9
        $isValid = !$this->validator->fails();
1305
1306 9
        $this->formHelper->alterValid($this, $this, $isValid);
1307
1308 9
        $this->eventDispatcher->dispatch(new AfterFormValidation($this, $this->validator, $isValid));
1309
1310 9
        return $isValid;
1311
    }
1312
1313
    /**
1314
     * Optionally change the validation result, and/or add error messages.
1315
     *
1316
     * @param Form $mainForm
1317
     * @param bool $isValid
1318
     * @return void|array
1319
     */
1320 9
    public function alterValid(Form $mainForm, &$isValid)
0 ignored issues
show
Unused Code introduced by
The parameter $mainForm is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $isValid is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1321
    {
1322
        // return ['name' => ['Some other error about the Name field.']];
1323 9
    }
1324
1325
    /**
1326
     * Get validation errors.
1327
     *
1328
     * @return array
1329
     */
1330 8
    public function getErrors()
1331
    {
1332 8
        if (!$this->validator || !$this->validator instanceof Validator) {
1333 1
            throw new \InvalidArgumentException(
1334 1
                sprintf(
1335 1
                    'Form %s was not validated. To validate it, call "isValid" method before retrieving the errors',
1336 1
                    get_class($this)
1337
                )
1338
            );
1339
        }
1340
1341 7
        return $this->validator->getMessageBag()->getMessages();
1342
    }
1343
1344
    /**
1345
     * Get all Request values from all fields, and nothing else.
1346
     *
1347
     * @param bool $with_nulls
1348
     * @return array
1349
     */
1350 3
    public function getFieldValues($with_nulls = true)
1351
    {
1352 3
        $request_values = $this->getRequest()->all();
1353
1354 3
        $values = [];
1355 3
        foreach ($this->getAllAttributes() as $attribute) {
1356 3
            $value = Arr::get($request_values, $attribute);
1357 3
            if ($with_nulls || $value !== null) {
1358 3
                Arr::set($values, $attribute, $value);
1359
            }
1360
        }
1361
1362
        // If this form is a child form, cherry pick a part
1363 3
        if ($this->getName()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->getName() of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
1364 1
            $prefix = $this->getNameKey();
1365 1
            $values = Arr::get($values, $prefix);
1366
        }
1367
1368
        // Allow form-specific value alters
1369 3
        $this->formHelper->alterFieldValues($this, $values);
1370
1371 3
        return $values;
1372
    }
1373
1374
    /**
1375
     * Optionally mess with this form's $values before it's returned from getFieldValues().
1376
     *
1377
     * @param array $values
1378
     * @return void
1379
     */
1380 3
    public function alterFieldValues(array &$values)
0 ignored issues
show
Unused Code introduced by
The parameter $values is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1381
    {
1382 3
    }
1383
1384
    /**
1385
     * Throw an exception indicating a field does not exist on the class.
1386
     *
1387
     * @param string $name
1388
     * @throws \InvalidArgumentException
1389
     * @return void
1390
     */
1391 2
    protected function fieldDoesNotExist($name)
1392
    {
1393 2
        throw new \InvalidArgumentException('Field ['.$name.'] does not exist in '.get_class($this));
1394
    }
1395
1396
    /**
1397
     * Method filterFields used as *Main* method for starting
1398
     * filtering and request field mutating process.
1399
     *
1400
     * @return \Kris\LaravelFormBuilder\Form
1401
     */
1402 130
    public function filterFields()
1403
    {
1404
        // If filtering is unlocked/allowed we can start with filtering process.
1405 130
        if (!$this->isFilteringLocked()) {
1406 130
            $filters = array_filter($this->getFilters());
1407
1408 130
            if (count($filters)) {
1409 1
                $dotForm = $this->getNameKey();
1410
1411 1
                $request = $this->getRequest();
1412 1
                $requestData = $request->all();
1413
1414 1
                foreach ($filters as $field => $fieldFilters) {
1415 1
                    $dotField = $this->formHelper->transformToDotSyntax($field);
1416 1
                    $fieldData = Arr::get($requestData, $dotField);
1417 1
                    if ($fieldData !== null) {
1418
                        // Assign current Raw/Unmutated value from request.
1419 1
                        $localDotField = preg_replace('#^' . preg_quote("$dotForm.", '#') . '#', '', $dotField);
1420 1
                        $localBracketField = $this->formHelper->transformToBracketSyntax($localDotField);
1421 1
                        $this->getField($localBracketField)->setRawValue($fieldData);
1422 1
                        foreach ($fieldFilters as $filter) {
1423 1
                            $filterObj = FilterResolver::instance($filter);
1424 1
                            $fieldData = $filterObj->filter($fieldData);
1425
                        }
1426 1
                        Arr::set($requestData, $dotField, $fieldData);
1427
                    }
1428
                }
1429
1430 1
                foreach ($requestData as $name => $value) {
1431 1
                    $request[$name] = $value;
1432
                }
1433
            }
1434
        }
1435
1436 130
        return $this;
1437
    }
1438
1439
    /**
1440
     * Method getFilters used to return array of all binded filters to form fields.
1441
     *
1442
     * @return array
1443
     */
1444 130
    public function getFilters()
1445
    {
1446 130
        $filters = [];
1447 130
        foreach ($this->getFields() as $field) {
1448 17
            $filters[$field->getName()] = $field->getFilters();
1449
        }
1450
1451 130
        return $filters;
1452
    }
1453
1454
    /**
1455
     * If lockFiltering is set to true then we will not
1456
     * filter fields and mutate request data binded to fields.
1457
     *
1458
     * @return \Kris\LaravelFormBuilder\Form
1459
     */
1460 1
    public function lockFiltering()
1461
    {
1462 1
        $this->lockFiltering = true;
1463 1
        return $this;
1464
    }
1465
1466
    /**
1467
     * Unlock fields filtering/mutating.
1468
     *
1469
     * @return \Kris\LaravelFormBuilder\Form
1470
     */
1471
    public function unlockFiltering()
1472
    {
1473
        $this->lockFiltering = false;
1474
        return $this;
1475
    }
1476
1477
    /**
1478
     * Method isFilteringLocked used to check
1479
     * if current filteringLocked property status is set to true.
1480
     *
1481
     * @return bool
1482
     */
1483 130
    public function isFilteringLocked()
1484
    {
1485 130
        return !$this->lockFiltering ? false : true;
1486
    }
1487
1488
    /**
1489
     * Method getRawValues returns Unfiltered/Unmutated fields -> values.
1490
     *
1491
     * @return array
1492
     */
1493
    public function getRawValues()
1494
    {
1495
        $rawValues = [];
1496
        foreach ($this->getFields() as $field) {
1497
            $rawValues[$field->getName()] = $field->getRawValue();
1498
        }
1499
1500
        return $rawValues;
1501
    }
1502
}
1503