diff --git a/src/Kris/LaravelFormBuilder/Fields/ChoiceType.php b/src/Kris/LaravelFormBuilder/Fields/ChoiceType.php index 85ee0fcf..41b55925 100644 --- a/src/Kris/LaravelFormBuilder/Fields/ChoiceType.php +++ b/src/Kris/LaravelFormBuilder/Fields/ChoiceType.php @@ -1,6 +1,6 @@ options['expanded']; - $multiple = $this->options['multiple']; + parent::prepareOptions($options); + + $expanded = $this->getOption('expanded', false); + $multiple = $this->getOption('multiple', false); + + Arr::forget($this->options, ['attr.multiple']); if (!$expanded && $multiple) { - $this->options['attr']['multiple'] = true; + $this->setOption('attr.multiple', true); } - if ($expanded && !$multiple) { - return $this->choiceType = 'radio'; + if ($this->getOption('attr.multiple') && !$this->getOption('tmp.multipleBracesSet')) { + $this->name = $this->name . '[]'; + $this->setOption('tmp.multipleBracesSet', true); } - if ($expanded && $multiple) { - return $this->choiceType = 'checkbox'; + if (!$this->getOption('attr.multiple') && $this->getOption('tmp.multipleBracesSet')) { + $this->name = preg_replace('/\[\]$/u', '', $this->name); + $this->setOption('tmp.multipleBracesSet', false); + } + + return $this->options; + } + + /** + * Determine which choice type to use. + * + * @return string + */ + protected function determineChoiceField() + { + $expanded = $this->getOption('expanded', false); + $multiple = $this->getOption('multiple', false); + + if ($expanded) { + return $this->choiceType = $multiple ? 'checkbox' : 'radio'; } return $this->choiceType = 'select'; @@ -107,7 +128,7 @@ protected function buildCheckableChildren($fieldType) { $multiple = $this->getOption('multiple') ? '[]' : ''; - $attr = $this->options['attr']?? []; + $attr = $this->options['attr'] ?? []; $attr = Arr::except($attr, ['class', 'multiple', 'id', 'name']); foreach ((array)$this->options['choices'] as $key => $choice) { $id = str_replace('.', '_', $this->getNameKey()) . '_' . $key; diff --git a/src/Kris/LaravelFormBuilder/Fields/FormField.php b/src/Kris/LaravelFormBuilder/Fields/FormField.php index b5032140..c101deec 100644 --- a/src/Kris/LaravelFormBuilder/Fields/FormField.php +++ b/src/Kris/LaravelFormBuilder/Fields/FormField.php @@ -306,6 +306,11 @@ protected function prepareOptions(array $options = []) $this->setOption('tmp.multipleBracesSet', true); } + if (!$this->getOption('attr.multiple') && $this->getOption('tmp.multipleBracesSet')) { + $this->name = preg_replace('/\[\]$/u', '', $this->name); + $this->setOption('tmp.multipleBracesSet', false); + } + if ($this->parent->haveErrorsEnabled()) { $this->addErrorClass(); } @@ -406,7 +411,7 @@ protected function normalizeRules($rules) * * @param array $first first set of rules * @param array $second second set of rules - * @return array merged set of rules without duplicates + * @return array merged set of rules without duplicates */ protected function mergeRules($first, $second) { diff --git a/src/Kris/LaravelFormBuilder/Fields/ParentType.php b/src/Kris/LaravelFormBuilder/Fields/ParentType.php index 1b92d18d..102d8fe6 100644 --- a/src/Kris/LaravelFormBuilder/Fields/ParentType.php +++ b/src/Kris/LaravelFormBuilder/Fields/ParentType.php @@ -60,7 +60,14 @@ public function setValue($val) */ public function render(array $options = [], $showLabel = true, $showField = true, $showError = true) { - $options['children'] = $this->children; + if (!empty($options)) { + $this->prepareOptions($options); + + $this->createChildren(); + } + + $this->setOption('children', $this->getChildren()); + return parent::render($options, $showLabel, $showField, $showError); } diff --git a/src/Kris/LaravelFormBuilder/Form.php b/src/Kris/LaravelFormBuilder/Form.php index 7644f587..b6d6bbed 100644 --- a/src/Kris/LaravelFormBuilder/Form.php +++ b/src/Kris/LaravelFormBuilder/Form.php @@ -171,7 +171,7 @@ public function rebuildForm() if ($this->isPlain()) { foreach ($this->fields as $name => $field) { // Remove any temp variables added in previous instance - $options = Arr::except($field->getOptions(), 'tmp'); + $options = Arr::except($field->getOptions(), 'tmp'); $this->add($name, $field->getType(), $options); } } else { @@ -187,7 +187,7 @@ public function rebuildForm() */ protected function isPlain() { - if($this->formBuilder === null) { + if ($this->formBuilder === null) { throw new \RuntimeException('FormBuilder is not set'); } @@ -251,7 +251,6 @@ protected function addField(FormField $field, $modify = false) $this->preventDuplicate($field->getRealName()); } - if ($field->getType() == 'file') { $this->formOptions['files'] = true; } @@ -399,8 +398,12 @@ public function modify($name, $type = 'text', array $options = [], $overwriteOpt { // If we don't want to overwrite options, we merge them with old options. if ($overwriteOptions === false && $this->has($name)) { + $fieldOptions = $this->getField($name)->getOptions(); + + Arr::forget($fieldOptions, ['tmp']); + $options = $this->formHelper->mergeOptions( - $this->getField($name)->getOptions(), + $fieldOptions, $options ); } @@ -878,7 +881,7 @@ public function getData($name = null, $default = null) * will be switched to protected in 1.7. * @param $data * @return $this - **/ + */ public function addData(array $data) { foreach ($data as $key => $value) { @@ -988,7 +991,7 @@ public function setTranslationTemplate($template) * Render the form. * * @param array $options - * @param string $fields + * @param string[] $fields * @param bool $showStart * @param bool $showFields * @param bool $showEnd @@ -1052,16 +1055,9 @@ protected function getTemplate() */ protected function getUnrenderedFields() { - $unrenderedFields = []; - - foreach ($this->fields as $field) { - if (!$field->isRendered()) { - $unrenderedFields[] = $field; - continue; - } - } - - return $unrenderedFields; + return array_filter($this->fields, function ($field) { + return !$field->isRendered(); + }); } /** @@ -1086,9 +1082,7 @@ protected function preventDuplicate($name) */ protected function getFieldType($type) { - $fieldType = $this->formHelper->getFieldType($type); - - return $fieldType; + return $this->formHelper->getFieldType($type); } /** @@ -1480,6 +1474,7 @@ public function getFilters() public function lockFiltering() { $this->lockFiltering = true; + return $this; } @@ -1491,6 +1486,7 @@ public function lockFiltering() public function unlockFiltering() { $this->lockFiltering = false; + return $this; } @@ -1502,7 +1498,7 @@ public function unlockFiltering() */ public function isFilteringLocked() { - return !$this->lockFiltering ? false : true; + return (bool)$this->lockFiltering; } /** diff --git a/tests/Fields/ChoiceTypeTest.php b/tests/Fields/ChoiceTypeTest.php index 62c1c96f..46d306ac 100644 --- a/tests/Fields/ChoiceTypeTest.php +++ b/tests/Fields/ChoiceTypeTest.php @@ -117,7 +117,7 @@ public function it_disables_select() ]; $field = new ChoiceType('some_choice', 'choice', $this->plainForm, $options); $children = $field->getChildren(); - + // there shall be no 'disabled' attribute set beforehand $this->assertArrayNotHasKey('disabled', $field->getOption('attr')); foreach ($children as $child) { @@ -165,7 +165,7 @@ public function it_disables_checkbox_list() $this->assertEquals('disabled', $child->getOption('attr')['disabled']); } } - + /** @test */ public function it_disables_radios_list() { @@ -195,7 +195,7 @@ public function it_disables_radios_list() $this->assertEquals('disabled', $child->getOption('attr')['disabled']); } } - + /** @test */ public function it_enables_select() { @@ -256,7 +256,7 @@ public function it_enables_checkbox_list() $this->assertArrayNotHasKey('disabled', $child->getOption('attr')); } } - + /** @test */ public function it_enables_radios_list() { @@ -287,4 +287,312 @@ public function it_enables_radios_list() $this->assertArrayNotHasKey('disabled', $child->getOption('attr')); } } + + /** + * @test + * @dataProvider dataForFieldRendering + */ + public function it_render(array $options, string $expectedHtml) + { + $this->plainForm->add('users', 'choice', $options); + + $this->plainForm->renderForm(); + + $renderedView = $this->plainForm->users->render(); + + $this->assertStringContainsString($expectedHtml, $renderedView); + } + + /** + * @return array{options: array, expected_html: string} + */ + public static function dataForFieldRendering(): array + { + return [ + 'select field' => [ + 'options' => [ + 'choices' => [1 => 'user1', 2 => 'user2'], + 'expanded' => false, + 'multiple' => false, + ], + 'expected_html' => '', + ], + 'radio field' => [ + 'options' => [ + 'choices' => [1 => 'user1', 2 => 'user2'], + 'expanded' => true, + 'multiple' => false, + ], + 'expected_html' => '', + ], + 'checkbox field' => [ + 'options' => [ + 'choices' => [1 => 'user1', 2 => 'user2'], + 'expanded' => true, + 'multiple' => true, + ], + 'expected_html' => '', + ], + ]; + } + + /** + * @test + * @dataProvider dataForModifiedFieldRendering + */ + public function it_render_modified_field(array $options, array $modifyOptions, string $expectedHtml) + { + $this->plainForm->add('users', 'choice', $options); + + $this->plainForm->modify('users', 'choice', $modifyOptions); + + $this->plainForm->renderForm(); + + $renderedView = $this->plainForm->users->render(); + + $this->assertStringContainsString($expectedHtml, $renderedView); + } + + /** + * @return array{options: array, modify_options: array, expected_html: string} + */ + public static function dataForModifiedFieldRendering(): array + { + return [ + 'modified select field' => [ + 'options' => [ + 'choices' => [1 => 'user1', 2 => 'user2'], + 'expanded' => false, + 'multiple' => false, + ], + 'modify_options' => [ + 'rules' => 'required', + ], + 'expected_html' => '', + ], + 'modified radio field' => [ + 'options' => [ + 'choices' => [1 => 'user1', 2 => 'user2'], + 'expanded' => true, + 'multiple' => false, + ], + 'modify_options' => [ + 'rules' => 'required', + ], + 'expected_html' => '', + ], + 'modified checkbox field' => [ + 'options' => [ + 'choices' => [1 => 'user1', 2 => 'user2'], + 'expanded' => true, + 'multiple' => true, + ], + 'modify_options' => [ + 'rules' => 'required', + ], + 'expected_html' => '', + ], + 'modified select to checkbox field' => [ + 'options' => [ + 'choices' => [1 => 'user1', 2 => 'user2'], + 'expanded' => false, + 'multiple' => false, + ], + 'modify_options' => [ + 'expanded' => true, + 'multiple' => true, + ], + 'expected_html' => '', + ], + 'modified multiple select to select field' => [ + 'options' => [ + 'choices' => [1 => 'user1', 2 => 'user2'], + 'expanded' => false, + 'multiple' => true, + ], + 'modify_options' => [ + 'expanded' => false, + 'multiple' => false, + ], + 'expected_html' => '', + ], + 'modified multiple select to checkbox field' => [ + 'options' => [ + 'choices' => [1 => 'user1', 2 => 'user2'], + 'expanded' => false, + 'multiple' => true, + ], + 'modify_options' => [ + 'expanded' => true, + 'multiple' => true, + ], + 'expected_html' => '', + ], + ]; + } + + /** + * @test + * @dataProvider dataForFieldRenderingWithOptions + */ + public function it_render_field_with_options(array $options, array $renderOptions, string $expectedHtml) + { + $this->plainForm->add('users', 'choice', $options); + + $this->plainForm->renderForm(); + + $renderedView = $this->plainForm->users->render($renderOptions); + + $this->assertStringContainsString($expectedHtml, $renderedView); + } + + /** + * @return array{options: array, render_options: array, expected_html: string} + */ + public static function dataForFieldRenderingWithOptions(): array + { + return [ + 'make select to checkbox field' => [ + 'options' => [ + 'choices' => [1 => 'user1', 2 => 'user2'], + 'expanded' => false, + 'multiple' => false, + ], + 'render_options' => [ + 'expanded' => true, + 'multiple' => true, + ], + 'expected_html' => '', + ], + 'make multiple select to radio field' => [ + 'options' => [ + 'choices' => [1 => 'user1', 2 => 'user2'], + 'expanded' => false, + 'multiple' => true, + ], + 'render_options' => [ + 'expanded' => true, + 'multiple' => false, + ], + 'expected_html' => '', + ], + ]; + } + + /** + * @test + * @dataProvider dataForChildrenFormRendering + */ + public function it_render_children_form(array $options, string $expectedHtml) + { + $this->plainForm->add('form', 'form', [ + 'class' => $this->formBuilder->plain()->modify('users', 'choice', $options), + ]); + + $this->plainForm->renderForm(); + + $renderedView = $this->plainForm->form->users->render(); + + $this->assertStringContainsString($expectedHtml, $renderedView); + } + + /** + * @return array{options: array, expected_html: string} + */ + public static function dataForChildrenFormRendering(): array + { + return [ + 'children select field' => [ + 'options' => [ + 'choices' => [1 => 'user1', 2 => 'user2'], + 'expanded' => false, + 'multiple' => false, + ], + 'expected_html' => '', + ], + 'children radio field' => [ + 'options' => [ + 'choices' => [1 => 'user1', 2 => 'user2'], + 'expanded' => true, + 'multiple' => false, + ], + 'expected_html' => '', + ], + 'children checkbox field' => [ + 'options' => [ + 'choices' => [1 => 'user1', 2 => 'user2'], + 'expanded' => true, + 'multiple' => true, + ], + 'expected_html' => '', + ], + ]; + } + + /** @test */ + public function it_render_and_modify_multiple_times_with_multiple_option() + { + $expectedHtml = ''; + + $this->plainForm->modify('users', 'choice', [ + 'rules' => 'required', + ]); + + $this->assertStringContainsString($expectedHtml, $this->plainForm->users->render()); + + $this->plainForm->modify('users', 'choice', [ + 'rules' => 'required', + ]); + + $this->assertStringContainsString($expectedHtml, $this->plainForm->users->render()); + } }