diff --git a/core/lib/Drupal/Core/Render/Element/OptGroup.php b/core/lib/Drupal/Core/Render/Element/OptGroup.php index a46e2a8..a2f7cab 100644 --- a/core/lib/Drupal/Core/Render/Element/OptGroup.php +++ b/core/lib/Drupal/Core/Render/Element/OptGroup.php @@ -62,40 +62,37 @@ public function getInfo() { * @see _form_validate() */ public static function processOptGroup(&$element, FormStateInterface $form_state, &$complete_form) { - // @TODO This check is taken over from \Drupal\Core\Render\Element\Radios - // and \Drupal\Core\Render\Element\Checkboxes, but I do not understand why - // it is necessary. - if (count($element['#options']) > 0) { - // @TODO This is ported from form_select_options(). But instead of - // checking #value, #default_value should be used here and below, right? - $value_valid = isset($element['#value']) || array_key_exists('#value', $element); - $value_is_array = $value_valid && is_array($element['#value']); - // Check if the element is multiple select and no value has been selected. - $empty_value = (empty($element['#value']) && !empty($element['#multiple'])); - - $weight = 0; - foreach ($element['#options'] as $key => $choice) { - $key = (string) $key; - - // Maintain order of options as defined in #options, in case the element - // defines custom option sub-elements, but does not define all option - // sub-elements. - $weight += 0.001; - - $empty_choice = $empty_value && $key == '_none'; - - $element += array($key => array()); - $element[$key] += array( - '#type' => 'option', - '#title' => $choice, - // The key is sanitized in Drupal\Core\Template\Attribute during output - // from the theme function. - '#return_value' => $key, - '#selected' => $value_valid && ((!$value_is_array && (string) $element['#value'] === $key || ($value_is_array && in_array($key, $element['#value']))) || $empty_choice), - '#ajax' => isset($element['#ajax']) ? $element['#ajax'] : NULL, - '#weight' => $weight, - ); - } + // @TODO This is ported from form_select_options(). But instead of + // checking #value, #default_value should be used here and below, right? + $value_valid = isset($element['#value']) || array_key_exists('#value', $element); + $value_is_array = $value_valid && is_array($element['#value']); + // Check if the element is multiple select and no value has been selected. + $empty_value = (empty($element['#value']) && !empty($element['#multiple'])); + + $weight = 0; + foreach ($element['#options'] as $original_key => $choice) { + $key = (string) $original_key; + + // Maintain order of options as defined in #options, in case the element + // defines custom option sub-elements, but does not define all option + // sub-elements. + $weight += 0.001; + + $empty_choice = $empty_value && $key == '_none'; + + $element += array($key => array()); + $element[$key] += array( + '#type' => 'option', + '#title' => $choice, + // The key is sanitized in Drupal\Core\Template\Attribute during output + // from the theme function. + '#return_value' => $key, + '#selected' => $value_valid && ((!$value_is_array && (string) $element['#value'] === $key || ($value_is_array && in_array($key, $element['#value']))) || $empty_choice), + '#ajax' => isset($element['#ajax']) ? $element['#ajax'] : NULL, + '#weight' => $weight, + ); + + unset($element['#options'][$original_key]); } return $element; @@ -118,8 +115,7 @@ public static function preRenderOptGroup($element) { public static function getOptions($element) { $options = []; - // @TODO This does not respect weights. - foreach (Element::children($element) as $child) { + foreach (Element::children($element, TRUE) as $child) { if ($element[$child]['#type'] == 'option') { $options[$child] = $element[$child]['#title']; } diff --git a/core/lib/Drupal/Core/Render/Element/Option.php b/core/lib/Drupal/Core/Render/Element/Option.php index 036a10e..0c6b6b6 100644 --- a/core/lib/Drupal/Core/Render/Element/Option.php +++ b/core/lib/Drupal/Core/Render/Element/Option.php @@ -33,6 +33,7 @@ public function getInfo() { ), '#theme' => 'option', '#selected' => FALSE, + '#return_value' => NULL, ); } diff --git a/core/lib/Drupal/Core/Render/Element/Select.php b/core/lib/Drupal/Core/Render/Element/Select.php index ad3f13d..91fd942 100644 --- a/core/lib/Drupal/Core/Render/Element/Select.php +++ b/core/lib/Drupal/Core/Render/Element/Select.php @@ -129,8 +129,8 @@ public static function processSelect(&$element, FormStateInterface $form_state, // Process options and option groups. $weight = 0; - foreach ($element['#options'] as $key => $choice) { - $key = (string) $key; + foreach ($element['#options'] as $original_key => $choice) { + $key = (string) $original_key; // Maintain order of options as defined in #options, in case both // options and option groups are defined or the element defines custom @@ -152,12 +152,13 @@ public static function processSelect(&$element, FormStateInterface $form_state, ); OptGroup::processOptGroup($element[$key], $form_state, $complete_form); - unset($element['#options'][$key]); + unset($element['#options'][$original_key]); } } // Process the '#options' element in the same way that option groups do. OptGroup::processOptGroup($element, $form_state, $complete_form); + // At this point '#options' should be an empty array. return $element; } @@ -211,14 +212,14 @@ public static function preRenderSelect($element) { public static function getFlattenedOptions($element) { $options = []; - // @TODO This does not respect weights. - foreach (Element::children($element) as $child) { + foreach (Element::children($element, TRUE) as $child) { if ($element[$child]['#type'] == 'optgroup') { $options += OptGroup::getOptions($element[$child]); - unset($element[$child]); + } + elseif ($element[$child]['#type'] == 'option') { + $options[$child] = $element[$child]['#title']; } } - $options += OptGroup::getOptions($element); return $options; }