true, 'autocomplete' => true, 'autofocus' => true, 'disabled' => true, 'form' => true, 'multiple' => true, 'required' => true, 'size' => true ); /** * Attributes valid for options * * @var array */ protected $validOptionAttributes = array( 'disabled' => true, 'selected' => true, 'label' => true, 'value' => true, ); /** * Attributes valid for option groups * * @var array */ protected $validOptgroupAttributes = array( 'disabled' => true, 'label' => true, ); protected $translatableAttributes = array( 'label' => true, ); /** * @var FormHidden|null */ protected $formHiddenHelper; /** * Invoke helper as functor * * Proxies to {@link render()}. * * @param ElementInterface|null $element * @return string|FormSelect */ public function __invoke(ElementInterface $element = null) { if (!$element) { return $this; } return $this->render($element); } /** * Render a form %s', $this->createAttributesString($attributes), $this->renderOptions($options, $value) ); // Render hidden element $useHiddenElement = method_exists($element, 'useHiddenElement') && method_exists($element, 'getUnselectedValue') && $element->useHiddenElement(); if ($useHiddenElement) { $rendered = $this->renderHiddenElement($element) . $rendered; } return $rendered; } /** * Render an array of options * * Individual options should be of the form: * * * array( * 'value' => 'value', * 'label' => 'label', * 'disabled' => $booleanFlag, * 'selected' => $booleanFlag, * ) * * * @param array $options * @param array $selectedOptions Option values that should be marked as selected * @return string */ public function renderOptions(array $options, array $selectedOptions = array()) { $template = ''; $optionStrings = array(); $escapeHtml = $this->getEscapeHtmlHelper(); foreach ($options as $key => $optionSpec) { $value = ''; $label = ''; $selected = false; $disabled = false; if (is_scalar($optionSpec)) { $optionSpec = array( 'label' => $optionSpec, 'value' => $key ); } if (isset($optionSpec['options']) && is_array($optionSpec['options'])) { $optionStrings[] = $this->renderOptgroup($optionSpec, $selectedOptions); continue; } if (isset($optionSpec['value'])) { $value = $optionSpec['value']; } if (isset($optionSpec['label'])) { $label = $optionSpec['label']; } if (isset($optionSpec['selected'])) { $selected = $optionSpec['selected']; } if (isset($optionSpec['disabled'])) { $disabled = $optionSpec['disabled']; } if (ArrayUtils::inArray($value, $selectedOptions)) { $selected = true; } if (null !== ($translator = $this->getTranslator())) { $label = $translator->translate( $label, $this->getTranslatorTextDomain() ); } $attributes = compact('value', 'selected', 'disabled'); if (isset($optionSpec['attributes']) && is_array($optionSpec['attributes'])) { $attributes = array_merge($attributes, $optionSpec['attributes']); } $this->validTagAttributes = $this->validOptionAttributes; $optionStrings[] = sprintf( $template, $this->createAttributesString($attributes), $escapeHtml($label) ); } return implode("\n", $optionStrings); } /** * Render an optgroup * * See {@link renderOptions()} for the options specification. Basically, * an optgroup is simply an option that has an additional "options" key * with an array following the specification for renderOptions(). * * @param array $optgroup * @param array $selectedOptions * @return string */ public function renderOptgroup(array $optgroup, array $selectedOptions = array()) { $template = '%s'; $options = array(); if (isset($optgroup['options']) && is_array($optgroup['options'])) { $options = $optgroup['options']; unset($optgroup['options']); } $this->validTagAttributes = $this->validOptgroupAttributes; $attributes = $this->createAttributesString($optgroup); if (!empty($attributes)) { $attributes = ' ' . $attributes; } return sprintf( $template, $attributes, $this->renderOptions($options, $selectedOptions) ); } /** * Ensure that the value is set appropriately * * If the element's value attribute is an array, but there is no multiple * attribute, or that attribute does not evaluate to true, then we have * a domain issue -- you cannot have multiple options selected unless the * multiple attribute is present and enabled. * * @param mixed $value * @param array $attributes * @return array * @throws Exception\DomainException */ protected function validateMultiValue($value, array $attributes) { if (null === $value) { return array(); } if (!is_array($value)) { return (array) $value; } if (!isset($attributes['multiple']) || !$attributes['multiple']) { throw new Exception\DomainException(sprintf( '%s does not allow specifying multiple selected values when the element does not have a multiple attribute set to a boolean true', __CLASS__ )); } return $value; } protected function renderHiddenElement(ElementInterface $element) { $hiddenElement = new Hidden($element->getName()); $hiddenElement->setValue($element->getUnselectedValue()); return $this->getFormHiddenHelper()->__invoke($hiddenElement); } /** * @return FormHidden */ protected function getFormHiddenHelper() { if (!$this->formHiddenHelper) { if (method_exists($this->view, 'plugin')) { $this->formHiddenHelper = $this->view->plugin('formhidden'); } if (!$this->formHiddenHelper instanceof FormHidden) { $this->formHiddenHelper = new FormHidden(); } } return $this->formHiddenHelper; } }