123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175 |
- <?php
- namespace Drupal\Core\Render\Element;
- use Drupal\Core\Form\FormStateInterface;
- use Drupal\Core\Render\Element;
- /**
- * Provides a form element for a drop-down menu or scrolling selection box.
- *
- * Properties:
- * - #options: An associative array, where the keys are the values for each
- * option, and the values are the option labels to be shown in the drop-down
- * list. If a value is an array, it will be rendered similarly, but as an
- * optgroup. The key of the sub-array will be used as the label for the
- * optgroup. Nesting optgroups is not allowed.
- * - #empty_option: (optional) The label to show for the first default option.
- * By default, the label is automatically set to "- Select -" for a required
- * field and "- None -" for an optional field.
- * - #empty_value: (optional) The value for the first default option, which is
- * used to determine whether the user submitted a value or not.
- * - If #required is TRUE, this defaults to '' (an empty string).
- * - If #required is not TRUE and this value isn't set, then no extra option
- * is added to the select control, leaving the control in a slightly
- * illogical state, because there's no way for the user to select nothing,
- * since all user agents automatically preselect the first available
- * option. But people are used to this being the behavior of select
- * controls.
- * @todo Address the above issue in Drupal 8.
- * - If #required is not TRUE and this value is set (most commonly to an
- * empty string), then an extra option (see #empty_option above)
- * representing a "non-selection" is added with this as its value.
- * - #multiple: (optional) Indicates whether one or more options can be
- * selected. Defaults to FALSE.
- * - #default_value: Must be NULL or not set in case there is no value for the
- * element yet, in which case a first default option is inserted by default.
- * Whether this first option is a valid option depends on whether the field
- * is #required or not.
- * - #required: (optional) Whether the user needs to select an option (TRUE)
- * or not (FALSE). Defaults to FALSE.
- * - #size: The size of the input element in characters.
- *
- * Usage example:
- * @code
- * $form['example_select'] = [
- * '#type' => 'select',
- * '#title' => $this->t('Select element'),
- * '#options' => [
- * '1' => $this->t('One'),
- * '2' => [
- * '2.1' => $this->t('Two point one'),
- * '2.2' => $this->t('Two point two'),
- * ],
- * '3' => $this->t('Three'),
- * ],
- * ];
- * @endcode
- *
- * @FormElement("select")
- */
- class Select extends FormElement {
- /**
- * {@inheritdoc}
- */
- public function getInfo() {
- $class = get_class($this);
- return [
- '#input' => TRUE,
- '#multiple' => FALSE,
- '#process' => [
- [$class, 'processSelect'],
- [$class, 'processAjaxForm'],
- ],
- '#pre_render' => [
- [$class, 'preRenderSelect'],
- ],
- '#theme' => 'select',
- '#theme_wrappers' => ['form_element'],
- '#options' => [],
- ];
- }
- /**
- * Processes a select list form element.
- *
- * This process callback is mandatory for select fields, since all user agents
- * automatically preselect the first available option of single (non-multiple)
- * select lists.
- *
- * @param array $element
- * The form element to process.
- * @param \Drupal\Core\Form\FormStateInterface $form_state
- * The current state of the form.
- * @param array $complete_form
- * The complete form structure.
- *
- * @return array
- * The processed element.
- *
- * @see _form_validate()
- */
- public static function processSelect(&$element, FormStateInterface $form_state, &$complete_form) {
- // #multiple select fields need a special #name.
- if ($element['#multiple']) {
- $element['#attributes']['multiple'] = 'multiple';
- $element['#attributes']['name'] = $element['#name'] . '[]';
- }
- // A non-#multiple select needs special handling to prevent user agents from
- // preselecting the first option without intention. #multiple select lists do
- // not get an empty option, as it would not make sense, user interface-wise.
- else {
- // If the element is set to #required through #states, override the
- // element's #required setting.
- $required = isset($element['#states']['required']) ? TRUE : $element['#required'];
- // If the element is required and there is no #default_value, then add an
- // empty option that will fail validation, so that the user is required to
- // make a choice. Also, if there's a value for #empty_value or
- // #empty_option, then add an option that represents emptiness.
- if (($required && !isset($element['#default_value'])) || isset($element['#empty_value']) || isset($element['#empty_option'])) {
- $element += [
- '#empty_value' => '',
- '#empty_option' => $required ? t('- Select -') : t('- None -'),
- ];
- // The empty option is prepended to #options and purposively not merged
- // to prevent another option in #options mistakenly using the same value
- // as #empty_value.
- $empty_option = [$element['#empty_value'] => $element['#empty_option']];
- $element['#options'] = $empty_option + $element['#options'];
- }
- }
- return $element;
- }
- /**
- * {@inheritdoc}
- */
- public static function valueCallback(&$element, $input, FormStateInterface $form_state) {
- if ($input !== FALSE) {
- if (isset($element['#multiple']) && $element['#multiple']) {
- // If an enabled multi-select submits NULL, it means all items are
- // unselected. A disabled multi-select always submits NULL, and the
- // default value should be used.
- if (empty($element['#disabled'])) {
- return (is_array($input)) ? array_combine($input, $input) : [];
- }
- else {
- return (isset($element['#default_value']) && is_array($element['#default_value'])) ? $element['#default_value'] : [];
- }
- }
- // Non-multiple select elements may have an empty option prepended to them
- // (see \Drupal\Core\Render\Element\Select::processSelect()). When this
- // occurs, usually #empty_value is an empty string, but some forms set
- // #empty_value to integer 0 or some other non-string constant. PHP
- // receives all submitted form input as strings, but if the empty option
- // is selected, set the value to match the empty value exactly.
- elseif (isset($element['#empty_value']) && $input === (string) $element['#empty_value']) {
- return $element['#empty_value'];
- }
- else {
- return $input;
- }
- }
- }
- /**
- * Prepares a select render element.
- */
- public static function preRenderSelect($element) {
- Element::setAttributes($element, ['id', 'name', 'size']);
- static::setAttributes($element, ['form-select']);
- return $element;
- }
- }
|