options[$value] ?? null;
}
/**
* Set the options from specified values
*
* @param array $options
*
* @return $this
*/
public function setOptions(array $options): self
{
$this->options = [];
$this->optionContent = [];
foreach ($options as $value => $label) {
$this->optionContent[$value] = $this->makeOption($value, $label);
}
return $this;
}
/**
* Set the specified options as disable
*
* @param array $disabledOptions
*
* @return $this
*/
public function setDisabledOptions(array $disabledOptions): self
{
if (! empty($this->options)) {
foreach ($this->options as $option) {
$optionValue = $option->getValue();
$option->setAttribute(
'disabled',
in_array($optionValue, $disabledOptions, ! is_int($optionValue))
|| ($optionValue === null && in_array('', $disabledOptions, true))
);
}
$this->disabledOptions = [];
} else {
$this->disabledOptions = $disabledOptions;
}
return $this;
}
/**
* Get the value of the element
*
* Returns `array` when the attribute `multiple` is set to `true`, `string` or `null` otherwise
*
* @return array|string|null
*/
public function getValue()
{
if ($this->isMultiple()) {
return parent::getValue() ?? [];
}
return parent::getValue();
}
public function getValueAttribute()
{
// select elements don't have a value attribute
return null;
}
public function getNameAttribute()
{
$name = $this->getName();
return $this->isMultiple() ? ($name . '[]') : $name;
}
/**
* Make the selectOption for the specified value and the label
*
* @param string|int|null $value Value of the option
* @param string|array $label Label of the option
*
* @return SelectOption|HtmlElement
*/
protected function makeOption($value, $label)
{
if (is_array($label)) {
$grp = Html::tag('optgroup', ['label' => $value]);
foreach ($label as $option => $val) {
$grp->addHtml($this->makeOption($option, $val));
}
return $grp;
}
$option = (new SelectOption($value, $label))
->setAttribute('disabled', in_array($value, $this->disabledOptions, ! is_int($value)));
$option->getAttributes()->registerAttributeCallback('selected', function () use ($option) {
return $this->isSelectedOption($option->getValue());
});
$this->options[$value] = $option;
return $this->options[$value];
}
/**
* Get whether the given option is selected
*
* @param int|string|null $optionValue
*
* @return bool
*/
protected function isSelectedOption($optionValue): bool
{
$value = $this->getValue();
if ($optionValue === '') {
$optionValue = null;
}
if ($this->isMultiple()) {
if (! is_array($value)) {
throw new UnexpectedValueException(
'Value must be an array when the `multiple` attribute is set to `true`'
);
}
return in_array($optionValue, $this->getValue(), ! is_int($optionValue))
|| ($optionValue === null && in_array('', $this->getValue(), true));
}
if (is_array($value)) {
throw new UnexpectedValueException(
'Value cannot be an array without setting the `multiple` attribute to `true`'
);
}
return is_int($optionValue)
// The loose comparison is required because PHP casts
// numeric strings to integers if used as array keys
? $value == $optionValue
: $value === $optionValue;
}
protected function addDefaultValidators(ValidatorChain $chain): void
{
$chain->add(
new DeferredInArrayValidator(function (): array {
$possibleValues = [];
foreach ($this->options as $option) {
if ($option->getAttributes()->get('disabled')->getValue()) {
continue;
}
$possibleValues[] = $option->getValue();
}
return $possibleValues;
})
);
}
protected function assemble()
{
$this->addHtml(...array_values($this->optionContent));
}
protected function registerAttributeCallbacks(Attributes $attributes)
{
parent::registerAttributeCallbacks($attributes);
$attributes->registerAttributeCallback(
'options',
null,
[$this, 'setOptions']
);
$attributes->registerAttributeCallback(
'disabledOptions',
null,
[$this, 'setDisabledOptions']
);
// ZF1 compatibility:
$this->getAttributes()->registerAttributeCallback(
'multiOptions',
null,
[$this, 'setOptions']
);
$this->registerMultipleAttributeCallback($attributes);
}
}