diff --git a/src/FormElement/RadioElement.php b/src/FormElement/RadioElement.php new file mode 100644 index 00000000..5ef65dc8 --- /dev/null +++ b/src/FormElement/RadioElement.php @@ -0,0 +1,168 @@ +setName($name); + + $this->getAttributes()->registerAttributeCallback( + 'options', + null, + [$this, 'setOptions'] + ); + + // ZF1 compatibility: + $this->getAttributes()->registerAttributeCallback( + 'multiOptions', + null, + [$this, 'setOptions'] + ); + + $this->getAttributes()->registerAttributeCallback( + 'disabled', + null, + [$this, 'disableOptions'] + ); + + parent::__construct($name, $attributes); + } + + /** + * Disable radio button that contains given value + * + * @param string $value + * + * @return $this + */ + public function disableOption(string $value): RadioElement + { + if ($option = $this->getOption($value)) { + $option->getAttributes()->add('disabled', true); + } + + if ($this->getValue() == $value) { + $this->valid = false; + $this->addMessage("'$value' is not allowed here"); + } + + return $this; + } + + /** + * Disable radio buttons that contain given values + * + * @param mixed $values array or string + * + * @return $this + */ + public function disableOptions($values): RadioElement + { + $values = (array) $values; + foreach ($values as $value) { + $this->disableOption($value); + } + + return $this; + } + + /** + * Get radio button with given value + * + * @param string $value + * + * @return InputElement|null + */ + public function getOption(string $value): ?InputElement + { + foreach ($this->optionContent as $radio) { + if ($radio->getValueAttribute() === $value) { + return $radio; + } + } + + return null; + } + + public function setValue($value) + { + parent::setValue($value); + + foreach ($this->optionContent as $radio) { + $radio->getAttributes()->remove('checked'); + if ($radio->getValueAttribute() === $value) { + $radio->addAttributes(['checked' => true]); + } + } + } + + public function validate() + { + $value = $this->getValue(); + if ( + $value !== null + && (! ($option = $this->getOption($value)) || $option->getAttributes()->has('disabled')) + ) { + $this->valid = false; + $this->addMessage(sprintf($this->translate("'%s' is not allowed here"), $value)); + + return $this; + } + + return parent::validate(); + } + + /** + * Prepare options + * + * @param array $options + * + * @return $this + */ + public function setOptions(array $options): RadioElement + { + foreach ($options as $value => $label) { + $input = new InputElement($this->getName(), [ + 'name' => $this->getName(), + 'type' => $this->type, + 'value' => $value + ]); + + $this->optionContent[$label] = $input; + } + + return $this; + } + + public function renderUnwrapped() + { + return $this->renderContent(); + } + + protected function assemble() + { + foreach ($this->optionContent as $label => $radioElm) { + $labelElm = new HtmlElement( + 'label', + Attributes::create(['class' => 'radio-label']), + $radioElm, + HtmlString::create($label) + ); + + $this->addHtml($labelElm); + } + } +} diff --git a/tests/FormElement/RadioElementTest.php b/tests/FormElement/RadioElementTest.php new file mode 100644 index 00000000..0166fd5d --- /dev/null +++ b/tests/FormElement/RadioElementTest.php @@ -0,0 +1,150 @@ + 'Test', + 'multiOptions' => [ + 'foo' => 'Foo', + 'bar' => 'Bar', + 'yes' => 'Yes' + ] + ]); + + $this->assertHtml( + '' + . '' + . '', + $radio + ); + } + + public function testCheckCorrectRadio() + { + $radio = new RadioElement('test', [ + 'label' => 'Test', + 'multiOptions' => [ + 'foo' => 'Foo', + 'bar' => 'Bar', + 'yes' => 'Yes' + ], + 'value' => 'bar' + ]); + + $this->assertHtml( + '' + . '' + . '', + $radio + ); + + $radio->setValue('yes'); + + $this->assertHtml( + '' + . '' + . '', + $radio + ); + + $radio->setValue('no'); + + $this->assertHtml( + '' + . '' + . '', + $radio + ); + } + + public function testDisableRadio() + { + $radio = new RadioElement('test', [ + 'label' => 'Test', + 'multiOptions' => [ + 'foo' => 'Foo', + 'bar' => 'Bar', + 'yes' => 'Yes', + 'no' => 'No' + ], + 'value' => 'bar', + 'disabled' => 'yes' + ]); + + $this->assertHtml( + '' + . '' + . '' + . '', + $radio + ); + + $radio->disableOptions(['yes', 'no', 'foo']); + + $this->assertHtml( + '' + . '' + . '' + . '', + $radio + ); + } + + public function testRadioNotValidIfCheckedValueIsInvalid() + { + StaticTranslator::$instance = new NoopTranslator(); + $radio = new RadioElement('test', [ + 'label' => 'Test', + 'multiOptions' => [ + 'foo' => 'Foo', + 'bar' => 'Bar', + 'yes' => 'Yes' + ], + 'value' => 'bar', + 'disabled' => 'yes' + ]); + + $this->assertTrue($radio->isValid()); + + $radio->setValue('no'); + $this->assertFalse($radio->isValid()); + + $radio->setValue('tom'); + $this->assertFalse($radio->isValid()); + } + + public function testRadioNotValidIfCheckedValueIsDisabled() + { + StaticTranslator::$instance = new NoopTranslator(); + $radio = new RadioElement('test', [ + 'label' => 'Test', + 'multiOptions' => [ + 'foo' => 'Foo', + 'bar' => 'Bar', + 'yes' => 'Yes' + ], + 'value' => 'bar', + 'disabled' => 'yes' + ]); + + $this->assertTrue($radio->isValid()); + + $radio->setValue('yes'); + $this->assertFalse($radio->isValid()); + + $radio->setValue('bar'); + $this->assertTrue($radio->isValid()); + + $radio->disableOptions(['bar', 'yes', 'foo']); + $this->assertFalse($radio->isValid()); + } +}