Completed
Pull Request — master (#422)
by
unknown
03:21
created

Form::addField()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 15
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 4

Importance

Changes 0
Metric Value
cc 4
eloc 7
nc 4
nop 2
dl 0
loc 15
rs 9.2
c 0
b 0
f 0
ccs 9
cts 9
cp 1
crap 4
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
    ];
52
53
    /**
54
     * Additional data which can be used to build fields.
55
     *
56
     * @var array
57
     */
58
    protected $data = [];
59
60
    /**
61
     * Wether errors for each field should be shown when calling form($form) or form_rest($form).
62
     *
63
     * @var bool
64
     */
65
    protected $showFieldErrors = true;
66
67
    /**
68
     * Enable html5 validation.
69
     *
70
     * @var bool
71
     */
72
    protected $clientValidationEnabled = true;
73
74
    /**
75
     * Name of the parent form if any.
76
     *
77
     * @var string|null
78
     */
79
    protected $name = null;
80
81
    /**
82
     * @var FormBuilder
83
     */
84
    protected $formBuilder;
85
86
    /**
87
     * @var ValidatorFactory
88
     */
89
    protected $validatorFactory;
90
91
    /**
92
     * @var Validator
93
     */
94
    protected $validator = null;
95
96
    /**
97
     * @var Request
98
     */
99
    protected $request;
100
101
    /**
102
     * List of fields to not render.
103
     *
104
     * @var array
105
     **/
106
    protected $exclude = [];
107
108
    /**
109
     * Wether the form is beign rebuild.
110
     *
111
     * @var bool
112
     */
113
    protected $rebuilding = false;
114
115
    /**
116
     * @var string
117
     */
118
    protected $templatePrefix;
119
120
    /**
121
     * @var string
122
     */
123
    protected $languageName;
124
125
    /**
126
     * @var string
127
     */
128
    protected $translationTemplate;
129
130
    /**
131
     * To filter and mutate request values or not.
132
     *
133
     * @var bool
134
     */
135
    protected $lockFiltering = false;
136
137
    /**
138
     * Define the error bag name for the form.
139
     *
140
     * @var string
141
     */
142
    protected $errorBag = 'default';
143
144
    /**
145
     * Build the form.
146
     *
147
     * @return mixed
148
     */
149 3
    public function buildForm()
150
    {
151 3
    }
152
153
    /**
154
     * Rebuild the form from scratch.
155
     *
156
     * @return $this
157
     */
158 27
    public function rebuildForm()
159
    {
160 27
        $this->rebuilding = true;
161
        // If form is plain, buildForm method is empty, so we need to take
162
        // existing fields and add them again
163 27
        if (get_class($this) === 'Kris\LaravelFormBuilder\Form') {
164 24
            foreach ($this->fields as $name => $field) {
165
                // Remove any temp variables added in previous instance
166 13
                $options = array_except($field->getOptions(), 'tmp');
167 13
                $this->add($name, $field->getType(), $options);
168 24
            }
169 24
        } else {
170 5
            $this->buildForm();
171
        }
172 27
        $this->rebuilding = false;
173
174 27
        return $this;
175
    }
176
177
    /**
178
     * Create the FormField object.
179
     *
180
     * @param string $name
181
     * @param string $type
182
     * @param array  $options
183
     * @return FormField
184
     */
185 65
    protected function makeField($name, $type = 'text', array $options = [])
186
    {
187 65
        $this->setupFieldOptions($name, $options);
188
189 65
        $fieldName = $this->getFieldName($name);
190
191 65
        $fieldType = $this->getFieldType($type);
192
193 64
        $field = new $fieldType($fieldName, $type, $this, $options);
194
195 61
        $this->eventDispatcher->fire(new AfterFieldCreation($this, $field));
196
197 61
        return $field;
198
    }
199
200
    /**
201
     * Create a new field and add it to the form.
202
     *
203
     * @param string $name
204
     * @param string $type
205
     * @param array  $options
206
     * @param bool   $modify
207
     * @return $this
208
     */
209 67
    public function add($name, $type = 'text', array $options = [], $modify = false)
210
    {
211 67
        $this->formHelper->checkFieldName($name, get_class($this));
212
213 65
        if ($this->rebuilding && !$this->has($name)) {
214
            return $this;
215
        }
216
217 65
        $this->addField($this->makeField($name, $type, $options), $modify);
218
219 61
        return $this;
220
    }
221
222
    /**
223
     * Add a FormField to the form's fields.
224
     *
225
     * @param FormField $field
226
     * @return $this
227
     */
228 61
    protected function addField(FormField $field, $modify = false)
229
    {
230 61
        if (!$modify && !$this->rebuilding) {
231 61
            $this->preventDuplicate($field->getRealName());
232 61
        }
233
234
235 61
        if ($field->getType() == 'file') {
236 3
            $this->formOptions['files'] = true;
237 3
        }
238
239 61
        $this->fields[$field->getRealName()] = $field;
240
241 61
        return $this;
242
    }
243
244
    /**
245
     * Add field before another field.
246
     *
247
     * @param string  $name         Name of the field before which new field is added.
248
     * @param string  $fieldName    Field name which will be added.
249
     * @param string  $type
250
     * @param array   $options
251
     * @param bool $modify
252
     * @return $this
253
     */
254 1
    public function addBefore($name, $fieldName, $type = 'text', $options = [], $modify = false)
255
    {
256 1
        $offset = array_search($name, array_keys($this->fields));
257
258 1
        $beforeFields = array_slice($this->fields, 0, $offset);
259 1
        $afterFields = array_slice($this->fields, $offset);
260
261 1
        $this->fields = $beforeFields;
262
263 1
        $this->add($fieldName, $type, $options, $modify);
264
265 1
        $this->fields += $afterFields;
266
267 1
        return $this;
268
    }
269
270
    /**
271
     * Add field before another field.
272
     *
273
     * @param string  $name         Name of the field after which new field is added.
274
     * @param string  $fieldName    Field name which will be added.
275
     * @param string  $type
276
     * @param array   $options
277
     * @param bool $modify
278
     * @return $this
279
     */
280 1
    public function addAfter($name, $fieldName, $type = 'text', $options = [], $modify = false)
281
    {
282 1
        $offset = array_search($name, array_keys($this->fields));
283
284 1
        $beforeFields = array_slice($this->fields, 0, $offset + 1);
285 1
        $afterFields = array_slice($this->fields, $offset + 1);
286
287 1
        $this->fields = $beforeFields;
288
289 1
        $this->add($fieldName, $type, $options, $modify);
290
291 1
        $this->fields += $afterFields;
292
293 1
        return $this;
294
    }
295
296
    /**
297
     * Take another form and add it's fields directly to this form.
298
     *
299
     * @param mixed   $class        Form to merge.
300
     * @param array   $options
301
     * @param boolean $modify
302
     * @return $this
303
     */
304 1
    public function compose($class, array $options = [], $modify = false)
305
    {
306 1
        $options['class'] = $class;
307
308
        // If we pass a ready made form just extract the fields.
309 1
        if ($class instanceof Form) {
310 1
            $fields = $class->getFields();
311 1
        } elseif ($class instanceof Fields\ChildFormType) {
312
            $fields = $class->getForm()->getFields();
313
        } elseif (is_string($class)) {
314
            // If its a string of a class make it the usual way.
315
            $options['model'] = $this->model;
316
            $options['name'] = $this->name;
317
318
            $form = $this->formBuilder->create($class, $options);
319
            $fields = $form->getFields();
320
        } else {
321
            throw new \InvalidArgumentException(
322
                "[{$class}] is invalid. Please provide either a full class name, Form or ChildFormType"
323
            );
324
        }
325
326 1
        foreach ($fields as $field) {
327 1
            $this->addField($field, $modify);
328 1
        }
329
330 1
        return $this;
331
    }
332
333
    /**
334
     * Remove field with specified name from the form.
335
     *
336
     * @param $name
337
     * @return $this
338
     */
339 2
    public function remove($name)
340
    {
341 2
        if ($this->has($name)) {
342 2
            unset($this->fields[$name]);
343 2
        }
344
345 2
        return $this;
346
    }
347
348
    /**
349
     * Modify existing field. If it doesn't exist, it is added to form.
350
     *
351
     * @param string $name
352
     * @param string $type
353
     * @param array  $options
354
     * @param bool   $overwriteOptions
355
     * @return Form
356
     */
357 1
    public function modify($name, $type = 'text', array $options = [], $overwriteOptions = false)
358
    {
359
        // If we don't want to overwrite options, we merge them with old options.
360 1
        if ($overwriteOptions === false && $this->has($name)) {
361 1
            $options = $this->formHelper->mergeOptions(
362 1
                $this->getField($name)->getOptions(),
363
                $options
364 1
            );
365 1
        }
366
367 1
        return $this->add($name, $type, $options, true);
368
    }
369
370
    /**
371
     * Render full form.
372
     *
373
     * @param array $options
374
     * @param bool  $showStart
375
     * @param bool  $showFields
376
     * @param bool  $showEnd
377
     * @return string
378
     */
379 7
    public function renderForm(array $options = [], $showStart = true, $showFields = true, $showEnd = true)
380
    {
381 7
        return $this->render($options, $this->fields, $showStart, $showFields, $showEnd);
382
    }
383
384
    /**
385
     * Render rest of the form.
386
     *
387
     * @param bool $showFormEnd
388
     * @param bool $showFields
389
     * @return string
390
     */
391 1
    public function renderRest($showFormEnd = true, $showFields = true)
392
    {
393 1
        $fields = $this->getUnrenderedFields();
394
395 1
        return $this->render([], $fields, false, $showFields, $showFormEnd);
396
    }
397
398
    /**
399
     * Renders the rest of the form up until the specified field name.
400
     *
401
     * @param string $field_name
402
     * @param bool   $showFormEnd
403
     * @param bool   $showFields
404
     * @return string
405
     */
406 2
    public function renderUntil($field_name, $showFormEnd = true, $showFields = true)
407
    {
408 2
        if (!$this->has($field_name)) {
409 1
            $this->fieldDoesNotExist($field_name);
410
        }
411
412 1
        $fields = $this->getUnrenderedFields();
413
414 1
        $i = 1;
415 1
        foreach ($fields as $key => $value) {
416 1
            if ($value->getRealName() == $field_name) {
417 1
                break;
418
            }
419 1
            $i++;
420 1
        }
421
422 1
        $fields = array_slice($fields, 0, $i, true);
423
424 1
        return $this->render([], $fields, false, $showFields, $showFormEnd);
425
    }
426
427
    /**
428
     * Get single field instance from form object.
429
     *
430
     * @param string $name
431
     * @return FormField
432
     */
433 34
    public function getField($name)
434
    {
435 34
        if ($this->has($name)) {
436 33
            return $this->fields[$name];
437
        }
438
439 1
        $this->fieldDoesNotExist($name);
440
    }
441
442 101
    public function getErrorBag()
443
    {
444 101
        return $this->errorBag;
445
    }
446
447
    /**
448
     * Check if form has field.
449
     *
450
     * @param string $name
451
     * @return bool
452
     */
453 61
    public function has($name)
454
    {
455 61
        return array_key_exists($name, $this->fields);
456
    }
457
458
    /**
459
     * Get all form options.
460
     *
461
     * @return array
462
     */
463 2
    public function getFormOptions()
464
    {
465 2
        return $this->formOptions;
466
    }
467
468
    /**
469
     * Get single form option.
470
     *
471
     * @param string $option
472
     * @param mixed|null $default
473
     * @return mixed
474
     */
475 129
    public function getFormOption($option, $default = null)
476
    {
477 129
        return array_get($this->formOptions, $option, $default);
478
    }
479
480
    /**
481
     * Set single form option on form.
482
     *
483
     * @param string $option
484
     * @param mixed $value
485
     *
486
     * @return $this
487
     */
488 2
    public function setFormOption($option, $value)
489
    {
490 2
        $this->formOptions[$option] = $value;
491
492 2
        return $this;
493
    }
494
495
    /**
496
     * Set form options.
497
     *
498
     * @param array $formOptions
499
     * @return $this
500
     */
501 129
    public function setFormOptions(array $formOptions)
502
    {
503 129
        $this->formOptions = $this->formHelper->mergeOptions($this->formOptions, $formOptions);
504 129
        $this->checkIfNamedForm();
505 129
        $this->pullFromOptions('data', 'addData');
506 129
        $this->pullFromOptions('model', 'setupModel');
507 129
        $this->pullFromOptions('errors_enabled', 'setErrorsEnabled');
508 129
        $this->pullFromOptions('client_validation', 'setClientValidationEnabled');
509 129
        $this->pullFromOptions('template_prefix', 'setTemplatePrefix');
510 129
        $this->pullFromOptions('language_name', 'setLanguageName');
511 129
        $this->pullFromOptions('translation_template', 'setTranslationTemplate');
512
513 129
        return $this;
514
    }
515
516
    /**
517
     * Get an option from provided options and call method with that value.
518
     *
519
     * @param string $name
520
     * @param string $method
521
     */
522 129
    protected function pullFromOptions($name, $method)
523
    {
524 129
        if (array_get($this->formOptions, $name) !== null) {
525 20
            $this->{$method}(array_pull($this->formOptions, $name));
526 20
        }
527 129
    }
528
529
    /**
530
     * Get form http method.
531
     *
532
     * @return string
533
     */
534 3
    public function getMethod()
535
    {
536 3
        return $this->formOptions['method'];
537
    }
538
539
    /**
540
     * Set form http method.
541
     *
542
     * @param string $method
543
     * @return $this
544
     */
545 1
    public function setMethod($method)
546
    {
547 1
        $this->formOptions['method'] = $method;
548
549 1
        return $this;
550
    }
551
552
    /**
553
     * Get form action url.
554
     *
555
     * @return string
556
     */
557 3
    public function getUrl()
558
    {
559 3
        return $this->formOptions['url'];
560
    }
561
562
    /**
563
     * Set form action url.
564
     *
565
     * @param string $url
566
     * @return $this
567
     */
568 1
    public function setUrl($url)
569
    {
570 1
        $this->formOptions['url'] = $url;
571
572 1
        return $this;
573
    }
574
575
    /**
576
     * Returns the name of the form.
577
     *
578
     * @return string|null
579
     */
580 69
    public function getName()
581
    {
582 69
        return $this->name;
583
    }
584
585
    /**
586
     * Set the name of the form.
587
     *
588
     * @param string $name
589
     * @param bool $rebuild
590
     * @return $this
591
     */
592 12
    public function setName($name, $rebuild = true)
593
    {
594 12
        $this->name = $name;
595
596 12
        if ($rebuild) {
597 12
            $this->rebuildForm();
598 12
        }
599
600 12
        return $this;
601
    }
602
603
    /**
604
     * Get model that is bind to form object.
605
     *
606
     * @return mixed
607
     */
608 96
    public function getModel()
609
    {
610 96
        return $this->model;
611
    }
612
613
    /**
614
     * Set model to form object.
615
     *
616
     * @param mixed $model
617
     * @return $this
618
     * @deprecated deprecated since 1.6.31, will be removed in 1.7 - pass model as option when creating a form
619
     */
620 17
    public function setModel($model)
621
    {
622 17
        $this->model = $model;
623
624 17
        $this->rebuildForm();
625
626 17
        return $this;
627
    }
628
629
    /**
630
     * Setup model for form, add namespace if needed for child forms.
631
     *
632
     * @return $this
633
     */
634 20
    protected function setupModel($model)
635
    {
636 20
        $this->model = $model;
637 20
        $this->setupNamedModel();
638
639 20
        return $this;
640
    }
641
642
    /**
643
     * Get all fields.
644
     *
645
     * @return FormField[]
646
     */
647 129
    public function getFields()
648
    {
649 129
        return $this->fields;
650
    }
651
652
    /**
653
     * Get field dynamically.
654
     *
655
     * @param string $name
656
     * @return FormField
657
     */
658 20
    public function __get($name)
659
    {
660 20
        if ($this->has($name)) {
661 19
            return $this->getField($name);
662
        }
663 3
    }
664
665
    /**
666
     * Check if field exists when fetched using magic methods.
667
     *
668
     * @param string $name
669
     * @return bool
670
     */
671
    public function __isset($name)
672
    {
673
        return $this->has($name);
674
    }
675
676
    /**
677
     * Set the Event Dispatcher to fire Laravel events.
678
     *
679
     * @param EventDispatcher $eventDispatcher
680
     * @return $this
681
     */
682 129
    public function setEventDispatcher(EventDispatcher $eventDispatcher)
683
    {
684 129
        $this->eventDispatcher = $eventDispatcher;
685
686 129
        return $this;
687
    }
688
689
    /**
690
     * Set the form helper only on first instantiation.
691
     *
692
     * @param FormHelper $formHelper
693
     * @return $this
694
     */
695 129
    public function setFormHelper(FormHelper $formHelper)
696
    {
697 129
        $this->formHelper = $formHelper;
698
699 129
        return $this;
700
    }
701
702
    /**
703
     * Get form helper.
704
     *
705
     * @return FormHelper
706
     */
707 101
    public function getFormHelper()
708
    {
709 101
        return $this->formHelper;
710
    }
711
712
    /**
713
     * Add custom field.
714
     *
715
     * @param $name
716
     * @param $class
717
     */
718 2
    public function addCustomField($name, $class)
719
    {
720 2
        if ($this->rebuilding && $this->formHelper->hasCustomField($name)) {
721
            return $this;
722
        }
723
724 2
        $this->formHelper->addCustomField($name, $class);
725 2
    }
726
727
    /**
728
     * Returns wether form errors should be shown under every field.
729
     *
730
     * @return bool
731
     */
732 101
    public function haveErrorsEnabled()
733
    {
734 101
        return $this->showFieldErrors;
735
    }
736
737
    /**
738
     * Enable or disable showing errors under fields
739
     *
740
     * @param bool $enabled
741
     * @return $this
742
     */
743 1
    public function setErrorsEnabled($enabled)
744
    {
745 1
        $this->showFieldErrors = (bool) $enabled;
746
747 1
        return $this;
748
    }
749
750
    /**
751
     * Is client validation enabled?
752
     *
753
     * @return bool
754
     */
755 101
    public function clientValidationEnabled()
756
    {
757 101
        return $this->clientValidationEnabled;
758
    }
759
760
    /**
761
     * Enable/disable client validation.
762
     *
763
     * @param bool $enable
764
     * @return $this
765
     */
766 2
    public function setClientValidationEnabled($enable)
767
    {
768 2
        $this->clientValidationEnabled = (bool) $enable;
769
770 2
        return $this;
771
    }
772
773
    /**
774
     * Add any aditional data that field needs (ex. array of choices).
775
     *
776
     * @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
777
     * will be switched to protected in 1.7.
778
     * @param string $name
779
     * @param mixed $data
780
     */
781 1
    public function setData($name, $data)
782
    {
783 1
        $this->data[$name] = $data;
784 1
    }
785
786
    /**
787
     * Get single additional data.
788
     *
789
     * @param string $name
790
     * @param null   $default
791
     * @return mixed
792
     */
793 20
    public function getData($name = null, $default = null)
794
    {
795 20
        if (is_null($name)) {
796 19
            return $this->data;
797
        }
798
799 1
        return array_get($this->data, $name, $default);
800
    }
801
802
    /**
803
     * Add multiple peices of data at once.
804
     *
805
     * @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
806
     * will be switched to protected in 1.7.
807
     * @param $data
808
     * @return $this
809
     **/
810 129
    public function addData(array $data)
811
    {
812 129
        foreach ($data as $key => $value) {
813 1
            $this->setData($key, $value);
814 129
        }
815
816 129
        return $this;
817
    }
818
819
    /**
820
     * Get current request.
821
     *
822
     * @return \Illuminate\Http\Request
823
     */
824 129
    public function getRequest()
825
    {
826 129
        return $this->request;
827
    }
828
829
    /**
830
     * Set request on form.
831
     *
832
     * @param Request $request
833
     * @return $this
834
     */
835 129
    public function setRequest(Request $request)
836
    {
837 129
        $this->request = $request;
838
839 129
        return $this;
840
    }
841
842
    /**
843
     * Get template prefix that is prepended to all template paths.
844
     *
845
     * @return string
846
     */
847 39
    public function getTemplatePrefix()
848
    {
849 39
        if ($this->templatePrefix !== null) {
850 4
            return $this->templatePrefix;
851
        }
852
853 35
        return $this->formHelper->getConfig('template_prefix');
854
    }
855
856
    /**
857
     * Set a template prefix for the form and its fields.
858
     *
859
     * @param string $prefix
860
     * @return $this
861
     */
862 4
    public function setTemplatePrefix($prefix)
863
    {
864 4
        $this->templatePrefix = (string) $prefix;
865
866 4
        return $this;
867
    }
868
869
    /**
870
     * Get the language name.
871
     *
872
     * @return string
873
     */
874 98
    public function getLanguageName()
875
    {
876 98
        return $this->languageName;
877
    }
878
879
    /**
880
     * Set a language name, used as prefix for translated strings.
881
     *
882
     * @param string $prefix
883
     * @return $this
884
     */
885 13
    public function setLanguageName($prefix)
886
    {
887 13
        $this->languageName = (string) $prefix;
888
889 13
        return $this;
890
    }
891
892
    /**
893
     * Get the translation template.
894
     *
895
     * @return string
896
     */
897 100
    public function getTranslationTemplate()
898
    {
899 100
        return $this->translationTemplate;
900
    }
901
902
    /**
903
     * Set a translation template, used to determine labels for fields.
904
     *
905
     * @param string $template
906
     * @return $this
907
     */
908 11
    public function setTranslationTemplate($template)
909
    {
910 11
        $this->translationTemplate = (string) $template;
911
912 11
        return $this;
913
    }
914
915
    /**
916
     * Render the form.
917
     *
918
     * @param array $options
919
     * @param string $fields
920
     * @param bool $showStart
921
     * @param bool $showFields
922
     * @param bool $showEnd
923
     * @return string
924
     */
925 9
    protected function render($options, $fields, $showStart, $showFields, $showEnd)
926
    {
927 9
        $formOptions = $this->formHelper->mergeOptions($this->formOptions, $options);
928
929 9
        $this->setupNamedModel();
930
931 9
        return $this->formHelper->getView()
932 9
            ->make($this->getTemplate())
933 9
            ->with(compact('showStart', 'showFields', 'showEnd'))
934 9
            ->with('formOptions', $formOptions)
935 9
            ->with('fields', $fields)
936 9
            ->with('model', $this->getModel())
937 9
            ->with('exclude', $this->exclude)
938 9
            ->with('form', $this)
939 9
            ->render();
940
    }
941
942
    /**
943
     * Get template from options if provided, otherwise fallback to config.
944
     *
945
     * @return mixed
946
     */
947 9
    protected function getTemplate()
948
    {
949 9
        return $this->getTemplatePrefix() . $this->getFormOption('template', $this->formHelper->getConfig('form'));
950
    }
951
952
    /**
953
     * Get all fields that are not rendered.
954
     *
955
     * @return array
956
     */
957 2
    protected function getUnrenderedFields()
958
    {
959 2
        $unrenderedFields = [];
960
961 2
        foreach ($this->fields as $field) {
962 2
            if (!$field->isRendered()) {
963 2
                $unrenderedFields[] = $field;
964 2
                continue;
965
            }
966 2
        }
967
968 2
        return $unrenderedFields;
969
    }
970
971
    /**
972
     * Prevent adding fields with same name.
973
     *
974
     * @param string $name
975
     * @throws \InvalidArgumentException
976
     * @return void
977
     */
978 61
    protected function preventDuplicate($name)
979
    {
980 61
        if ($this->has($name)) {
981 1
            throw new \InvalidArgumentException('Field ['.$name.'] already exists in the form '.get_class($this));
982
        }
983 61
    }
984
985
    /**
986
     * Returns and checks the type of the field.
987
     *
988
     * @param string $type
989
     * @return string
990
     */
991 65
    protected function getFieldType($type)
992
    {
993 65
        $fieldType = $this->formHelper->getFieldType($type);
994
995 64
        return $fieldType;
996
    }
997
998
    /**
999
     * Check if form is named form.
1000
     *
1001
     * @return void
1002
     */
1003 129
    protected function checkIfNamedForm()
1004
    {
1005 129
        if ($this->getFormOption('name')) {
1006 8
            $this->name = array_pull($this->formOptions, 'name', $this->name);
1007 8
        }
1008 129
    }
1009
1010
    /**
1011
     * Set up options on single field depending on form options.
1012
     *
1013
     * @param string $name
1014
     * @param $options
1015
     */
1016 65
    protected function setupFieldOptions($name, &$options)
1017
    {
1018 65
        $options['real_name'] = $name;
1019 65
    }
1020
1021
    /**
1022
     * Set namespace to model if form is named so the data is bound properly.
1023
     * Returns true if model is changed, otherwise false.
1024
     *
1025
     * @return bool
1026
     */
1027 29
    protected function setupNamedModel()
1028
    {
1029 29
        if (!$this->getModel() || !$this->getName()) {
1030 27
            return false;
1031
        }
1032
1033 4
        $dotName = $this->formHelper->transformToDotSyntax($this->getName());
1034 4
        $model = $this->formHelper->convertModelToArray($this->getModel());
1035 4
        $isCollectionFormModel = preg_match('/^.*\.\d$/', $dotName);
1036 4
        $isCollectionPrototype = strpos($dotName, '__NAME__') !== false;
1037
1038 4
        if (!array_get($model, $dotName) && !$isCollectionFormModel && !$isCollectionPrototype) {
1039 2
            $newModel = [];
1040 2
            array_set($newModel, $dotName, $model);
1041 2
            $this->model = $newModel;
1042
1043 2
            return true;
1044
        }
1045
1046 2
        return false;
1047
    }
1048
1049
    /**
1050
     * Set form builder instance on helper so we can use it later.
1051
     *
1052
     * @param FormBuilder $formBuilder
1053
     * @return $this
1054
     */
1055 129
    public function setFormBuilder(FormBuilder $formBuilder)
1056
    {
1057 129
        $this->formBuilder = $formBuilder;
1058
1059 129
        return $this;
1060
    }
1061
1062
    /**
1063
     * Returns the instance of the FormBuilder.
1064
     *
1065
     * @return FormBuilder
1066
     */
1067 12
    public function getFormBuilder()
1068
    {
1069 12
        return $this->formBuilder;
1070
    }
1071
1072
    /**
1073
     * Set the Validator instance on this so we can use it later.
1074
     *
1075
     * @param ValidatorFactory $validator
1076
     * @return $this
1077
     */
1078 129
    public function setValidator(ValidatorFactory $validator)
1079
    {
1080 129
        $this->validatorFactory = $validator;
1081
1082 129
        return $this;
1083
    }
1084
1085
    /**
1086
     * Returns the validator instance.
1087
     *
1088
     * @return Validator
1089
     */
1090 1
    public function getValidator()
1091
    {
1092 1
        return $this->validator;
1093
    }
1094
1095
    /**
1096
     * Exclude some fields from rendering.
1097
     *
1098
     * @return $this
1099
     */
1100
    public function exclude(array $fields)
1101
    {
1102
        $this->exclude = array_merge($this->exclude, $fields);
1103
1104
        return $this;
1105
    }
1106
1107
    /**
1108
     * If form is named form, modify names to be contained in single key (parent[child_field_name]).
1109
     *
1110
     * @param string $name
1111
     * @return string
1112
     */
1113 65
    protected function getFieldName($name)
1114
    {
1115 65
        $formName = $this->getName();
1116 65
        if ($formName !== null) {
1117 14
            if (strpos($formName, '[') !== false || strpos($formName, ']') !== false) {
1118 6
                return $this->formHelper->transformToBracketSyntax(
1119 6
                    $this->formHelper->transformToDotSyntax(
1120 6
                        $formName . '[' . $name . ']'
1121 6
                    )
1122 6
                );
1123
            }
1124
1125 11
            return $formName . '[' . $name . ']';
1126
        }
1127
1128 65
        return $name;
1129
    }
1130
1131
    /**
1132
     * Disable all fields in a form.
1133
     */
1134 1
    public function disableFields()
1135
    {
1136 1
        foreach ($this->fields as $field) {
1137 1
            $field->disable();
1138 1
        }
1139 1
    }
1140
1141
    /**
1142
     * Enable all fields in a form.
1143
     */
1144 1
    public function enableFields()
1145
    {
1146 1
        foreach ($this->fields as $field) {
1147 1
            $field->enable();
1148 1
        }
1149 1
    }
1150
1151
    /**
1152
     * Validate the form.
1153
     *
1154
     * @param array $validationRules
1155
     * @param array $messages
1156
     * @return Validator
1157
     */
1158 9
    public function validate($validationRules = [], $messages = [])
1159
    {
1160 9
        $this->setupModel($this->getRequest()->all());
1161 9
        $this->rebuildForm();
1162 9
        $fieldRules = $this->formHelper->mergeFieldsRules($this->fields);
1163 9
        $rules = array_merge($fieldRules['rules'], $validationRules);
1164 9
        $messages = array_merge($fieldRules['error_messages'], $messages);
1165
1166 9
        $this->validator = $this->validatorFactory->make($this->getRequest()->all(), $rules, $messages);
1167 9
        $this->validator->setAttributeNames($fieldRules['attributes']);
1168
1169 9
        $this->eventDispatcher->fire(new BeforeFormValidation($this, $this->validator));
1170
1171 9
        return $this->validator;
1172
    }
1173
1174
    /**
1175
     * Get validation rules for the form.
1176
     *
1177
     * @param array $overrideRules
1178
     * @return array
1179
     */
1180 1
    public function getRules($overrideRules = [])
1181
    {
1182 1
        $fieldRules = $this->formHelper->mergeFieldsRules($this->fields);
1183
1184 1
        return array_merge($fieldRules['rules'], $overrideRules);
1185
    }
1186
1187
    /**
1188
     * Redirects to a destination when form is invalid.
1189
     *
1190
     * @param  string|null $destination The target url.
1191
     * @return HttpResponseException
1192
     */
1193 3
    public function redirectIfNotValid($destination = null)
1194
    {
1195 3
        if (! $this->isValid()) {
1196 3
            $response = redirect($destination);
1197
1198 3
            if (is_null($destination)) {
1199 2
                $response = $response->back();
1200 2
            }
1201
1202 3
            $response = $response->withErrors($this->getErrors(), $this->getErrorBag())->withInput();
1203
1204 3
            throw new HttpResponseException($response);
1205
        }
1206
    }
1207
1208
    /**
1209
     * Get all form field attributes, including child forms, in a flat array.
1210
     *
1211
     * @return array
1212
     */
1213 3
    public function getAllAttributes()
1214
    {
1215 3
        return $this->formHelper->mergeAttributes($this->fields);
1216
    }
1217
1218
    /**
1219
     * Check if the form is valid.
1220
     *
1221
     * @return bool
1222
     */
1223 9
    public function isValid()
1224
    {
1225 9
        if (!$this->validator) {
1226 8
            $this->validate();
1227 8
        }
1228
1229 9
        $isValid = !$this->validator->fails();
1230
1231 9
        $this->formHelper->alterValid($this, $this, $isValid);
1232
1233 9
        $this->eventDispatcher->fire(new AfterFormValidation($this, $this->validator, $isValid));
1234
1235 9
        return $isValid;
1236
    }
1237
1238
    /**
1239
     * Optionally change the validation result, and/or add error messages.
1240
     *
1241
     * @param Form $mainForm
1242
     * @param bool $isValid
1243
     * @return void|array
1244
     */
1245 9
    public function alterValid(Form $mainForm, &$isValid)
1246
    {
1247
        // return ['name' => ['Some other error about the Name field.']];
1248 9
    }
1249
1250
    /**
1251
     * Get validation errors.
1252
     *
1253
     * @return array
1254
     */
1255 8
    public function getErrors()
1256
    {
1257 8
        if (!$this->validator || !$this->validator instanceof Validator) {
1258 1
            throw new \InvalidArgumentException(
1259 1
                sprintf(
1260 1
                    'Form %s was not validated. To validate it, call "isValid" method before retrieving the errors',
1261 1
                    get_class($this)
1262 1
                )
1263 1
            );
1264
        }
1265
1266 7
        return $this->validator->getMessageBag()->getMessages();
1267
    }
1268
1269
    /**
1270
     * Get all Request values from all fields, and nothing else.
1271
     *
1272
     * @param bool $with_nulls
1273
     * @return array
1274
     */
1275 3
    public function getFieldValues($with_nulls = true)
1276
    {
1277 3
        $request_values = $this->getRequest()->all();
1278
1279 3
        $values = [];
1280 3
        foreach ($this->getAllAttributes() as $attribute) {
1281 3
            $value = Arr::get($request_values, $attribute);
1282 3
            if ($with_nulls || $value !== null) {
1283 3
                Arr::set($values, $attribute, $value);
1284 3
            }
1285 3
        }
1286
1287
        // If this form is a child form, cherry pick a part
1288 3
        if ($prefix = $this->getName()) {
1289 1
            $prefix = $this->formHelper->transformToDotSyntax($prefix);
1290 1
            $values = Arr::get($values, $prefix);
1291 1
        }
1292
1293
        // Allow form-specific value alters
1294 3
        $this->formHelper->alterFieldValues($this, $values);
1295
1296 3
        return $values;
1297
    }
1298
1299
    /**
1300
     * Optionally mess with this form's $values before it's returned from getFieldValues().
1301
     *
1302
     * @param array $values
1303
     * @return void
1304
     */
1305 3
    public function alterFieldValues(array &$values)
1306
    {
1307 3
        foreach ($this->getFields() as $name => $field) {
1308
            if (method_exists($field, 'transformValue')) {
1309
                $fullName = $this->formHelper->transformToDotSyntax($name);
1310
                $subValues = Arr::get($values, $fullName);
1311
                Arr::set($values, $fullName, $field->transformValue($subValues));
1312
            }
1313
        }
1314
    }
1315
    }
1316 2
1317
    /**
1318 2
     * Throw an exception indicating a field does not exist on the class.
1319
     *
1320
     * @param string $name
1321
     * @throws \InvalidArgumentException
1322
     * @return void
1323
     */
1324
    protected function fieldDoesNotExist($name)
0 ignored issues
show
Bug introduced by
This code did not parse for me. Apparently, there is an error somewhere around this line:

Syntax error, unexpected T_PROTECTED
Loading history...
1325
    {
1326
        throw new \InvalidArgumentException('Field ['.$name.'] does not exist in '.get_class($this));
1327 129
    }
1328
1329
    /**
1330 129
     * Method filterFields used as *Main* method for starting
1331
     * filtering and request field mutating process.
1332 129
     *
1333 129
     * @return \Kris\LaravelFormBuilder\Form
1334
     */
1335 129
    public function filterFields()
1336 16
    {
1337
        // If filtering is unlocked/allowed we can start with filtering process.
1338
        if (!$this->isFilteringLocked()) {
1339 16
            // Init required vars.
1340
            $filters = $this->getFilters();
1341 1
            $request = $this->getRequest();
1342 1
1343 1
            if (!empty($filters)) {
1344 1
                foreach ($filters as $field => $fieldFilters) {
1345 1
                    // If field exist in request object, try to mutate/filter
1346 1
                    // it to filtered value if there is one.
1347 16
                    if (array_key_exists($field, $request->all())) {
1348 16
                        // Assign current Raw/Unmutated value from request.
1349 129
                        $this->fields[$field]->setRawValue($request[$field]);
1350
                        foreach ($fieldFilters as $filter) {
1351 129
                            $filterObj = FilterResolver::instance($filter);
1352
                            $request[$field] = $filterObj->filter($request[$field]);
1353
                        }
1354
                    }
1355
                }
1356
            }
1357
        }
1358
1359 129
        return $this;
1360
    }
1361 129
1362 129
    /**
1363 17
     * Method getFilters used to return array of all binded filters to form fields.
1364 129
     *
1365
     * @return array
1366 129
     */
1367
    public function getFilters()
1368
    {
1369
        $filters = [];
1370
        foreach ($this->getFields() as $field) {
1371
            $filters[$field->getName()] = $field->getFilters();
1372
        }
1373
1374
        return $filters;
1375 1
    }
1376
1377 1
    /**
1378 1
     * If lockFiltering is set to true then we will not
1379
     * filter fields and mutate request data binded to fields.
1380
     *
1381
     * @return \Kris\LaravelFormBuilder\Form
1382
     */
1383
    public function lockFiltering()
1384
    {
1385
        $this->lockFiltering = true;
1386
        return $this;
1387
    }
1388
1389
    /**
1390
     * Unlock fields filtering/mutating.
1391
     *
1392
     * @return \Kris\LaravelFormBuilder\Form
1393
     */
1394
    public function unlockFiltering()
1395
    {
1396
        $this->lockFiltering = false;
1397
        return $this;
1398 129
    }
1399
1400 129
    /**
1401
     * Method isFilteringLocked used to check
1402
     * if current filteringLocked property status is set to true.
1403
     *
1404
     * @return bool
1405
     */
1406
    public function isFilteringLocked()
1407
    {
1408
        return !$this->lockFiltering ? false : true;
1409
    }
1410
1411
    /**
1412
     * Method getRawValues returns Unfiltered/Unmutated fields -> values.
1413
     *
1414
     * @return array
1415
     */
1416
    public function getRawValues()
1417
    {
1418
        $rawValues = [];
1419
        foreach ($this->getFields() as $field) {
1420
            $rawValues[$field->getName()] = $field->getRawValue();
1421
        }
1422
1423
        return $rawValues;
1424
    }
1425
}
1426