Version 1
[yaffs-website] / web / core / lib / Drupal / Core / Render / Element / Select.php
1 <?php
2
3 namespace Drupal\Core\Render\Element;
4
5 use Drupal\Core\Form\FormStateInterface;
6 use Drupal\Core\Render\Element;
7
8 /**
9  * Provides a form element for a drop-down menu or scrolling selection box.
10  *
11  * Properties:
12  * - #options: An associative array, where the keys are the values for each
13  *   option, and the values are the option labels to be shown in the drop-down
14  *   list. If a value is an array, it will be rendered similarly, but as an
15  *   optgroup. The key of the sub-array will be used as the label for the
16  *   optgroup. Nesting optgroups is not allowed.
17  * - #empty_option: (optional) The label to show for the first default option.
18  *   By default, the label is automatically set to "- Select -" for a required
19  *   field and "- None -" for an optional field.
20  * - #empty_value: (optional) The value for the first default option, which is
21  *   used to determine whether the user submitted a value or not.
22  *   - If #required is TRUE, this defaults to '' (an empty string).
23  *   - If #required is not TRUE and this value isn't set, then no extra option
24  *     is added to the select control, leaving the control in a slightly
25  *     illogical state, because there's no way for the user to select nothing,
26  *     since all user agents automatically preselect the first available
27  *     option. But people are used to this being the behavior of select
28  *     controls.
29  *     @todo Address the above issue in Drupal 8.
30  *   - If #required is not TRUE and this value is set (most commonly to an
31  *     empty string), then an extra option (see #empty_option above)
32  *     representing a "non-selection" is added with this as its value.
33  * - #multiple: (optional) Indicates whether one or more options can be
34  *   selected. Defaults to FALSE.
35  * - #default_value: Must be NULL or not set in case there is no value for the
36  *   element yet, in which case a first default option is inserted by default.
37  *   Whether this first option is a valid option depends on whether the field
38  *   is #required or not.
39  * - #required: (optional) Whether the user needs to select an option (TRUE)
40  *   or not (FALSE). Defaults to FALSE.
41  * - #size: The size of the input element in characters.
42  *
43  * Usage example:
44  * @code
45  * $form['example_select'] = [
46  *   '#type' => 'select',
47  *   '#title' => $this->t('Select element'),
48  *   '#options' => [
49  *     '1' => $this->t('One'),
50  *     '2' => [
51  *       '2.1' => $this->t('Two point one'),
52  *       '2.2' => $this->t('Two point two'),
53  *     ],
54  *     '3' => $this->t('Three'),
55  *   ],
56  * ];
57  * @endcode
58  *
59  * @FormElement("select")
60  */
61 class Select extends FormElement {
62
63   /**
64    * {@inheritdoc}
65    */
66   public function getInfo() {
67     $class = get_class($this);
68     return [
69       '#input' => TRUE,
70       '#multiple' => FALSE,
71       '#process' => [
72         [$class, 'processSelect'],
73         [$class, 'processAjaxForm'],
74       ],
75       '#pre_render' => [
76         [$class, 'preRenderSelect'],
77       ],
78       '#theme' => 'select',
79       '#theme_wrappers' => ['form_element'],
80       '#options' => [],
81     ];
82   }
83
84   /**
85    * Processes a select list form element.
86    *
87    * This process callback is mandatory for select fields, since all user agents
88    * automatically preselect the first available option of single (non-multiple)
89    * select lists.
90    *
91    * @param array $element
92    *   The form element to process.
93    * @param \Drupal\Core\Form\FormStateInterface $form_state
94    *   The current state of the form.
95    * @param array $complete_form
96    *   The complete form structure.
97    *
98    * @return array
99    *   The processed element.
100    *
101    * @see _form_validate()
102    */
103   public static function processSelect(&$element, FormStateInterface $form_state, &$complete_form) {
104     // #multiple select fields need a special #name.
105     if ($element['#multiple']) {
106       $element['#attributes']['multiple'] = 'multiple';
107       $element['#attributes']['name'] = $element['#name'] . '[]';
108     }
109     // A non-#multiple select needs special handling to prevent user agents from
110     // preselecting the first option without intention. #multiple select lists do
111     // not get an empty option, as it would not make sense, user interface-wise.
112     else {
113       // If the element is set to #required through #states, override the
114       // element's #required setting.
115       $required = isset($element['#states']['required']) ? TRUE : $element['#required'];
116       // If the element is required and there is no #default_value, then add an
117       // empty option that will fail validation, so that the user is required to
118       // make a choice. Also, if there's a value for #empty_value or
119       // #empty_option, then add an option that represents emptiness.
120       if (($required && !isset($element['#default_value'])) || isset($element['#empty_value']) || isset($element['#empty_option'])) {
121         $element += [
122           '#empty_value' => '',
123           '#empty_option' => $required ? t('- Select -') : t('- None -'),
124         ];
125         // The empty option is prepended to #options and purposively not merged
126         // to prevent another option in #options mistakenly using the same value
127         // as #empty_value.
128         $empty_option = [$element['#empty_value'] => $element['#empty_option']];
129         $element['#options'] = $empty_option + $element['#options'];
130       }
131     }
132     return $element;
133   }
134
135   /**
136    * {@inheritdoc}
137    */
138   public static function valueCallback(&$element, $input, FormStateInterface $form_state) {
139     if ($input !== FALSE) {
140       if (isset($element['#multiple']) && $element['#multiple']) {
141         // If an enabled multi-select submits NULL, it means all items are
142         // unselected. A disabled multi-select always submits NULL, and the
143         // default value should be used.
144         if (empty($element['#disabled'])) {
145           return (is_array($input)) ? array_combine($input, $input) : [];
146         }
147         else {
148           return (isset($element['#default_value']) && is_array($element['#default_value'])) ? $element['#default_value'] : [];
149         }
150       }
151       // Non-multiple select elements may have an empty option prepended to them
152       // (see \Drupal\Core\Render\Element\Select::processSelect()). When this
153       // occurs, usually #empty_value is an empty string, but some forms set
154       // #empty_value to integer 0 or some other non-string constant. PHP
155       // receives all submitted form input as strings, but if the empty option
156       // is selected, set the value to match the empty value exactly.
157       elseif (isset($element['#empty_value']) && $input === (string) $element['#empty_value']) {
158         return $element['#empty_value'];
159       }
160       else {
161         return $input;
162       }
163     }
164   }
165
166   /**
167    * Prepares a select render element.
168    */
169   public static function preRenderSelect($element) {
170     Element::setAttributes($element, ['id', 'name', 'size']);
171     static::setAttributes($element, ['form-select']);
172     return $element;
173   }
174
175 }