You are here

protected function FormErrorHandler::setElementErrorsFromFormState in Drupal 8

Same name and namespace in other branches
  1. 9 core/lib/Drupal/Core/Form/FormErrorHandler.php \Drupal\Core\Form\FormErrorHandler::setElementErrorsFromFormState()

Stores errors and a list of child element errors directly on each element.

Grouping elements like containers, details, fieldgroups and fieldsets may need error info from their child elements to be able to accessibly show form error messages to a user. For example, a details element should be opened when child elements have errors.

Grouping example: Assume you have a 'street' element somewhere in a form, which is displayed in a details element 'address'. It might be:

$form['street'] = [
  '#type' => 'textfield',
  '#title' => $this
    ->t('Street'),
  '#group' => 'address',
  '#required' => TRUE,
];
$form['address'] = [
  '#type' => 'details',
  '#title' => $this
    ->t('Address'),
];

When submitting an empty street field, the generated error is available to the different render elements like so:


// The street textfield element.
$element = [
  '#errors' => {Drupal\Core\StringTranslation\TranslatableMarkup},
  '#children_errors' => [],
];
// The address detail element.
$element = [
  '#errors' => null,
  '#children_errors' => [
     'street' => {Drupal\Core\StringTranslation\TranslatableMarkup}
  ],
];

The list of child element errors of an element is an associative array. A child element error is keyed with the #array_parents value of the respective element. The key is formed by imploding this value with '][' as glue. For example, a value ['contact_info', 'name'] becomes 'contact_info][name'.

Parameters

array $form: An associative array containing a reference to the complete structure of the form.

\Drupal\Core\Form\FormStateInterface $form_state: The current state of the form.

array $elements: An associative array containing the part of the form structure that will be processed while traversing up the tree. For recursion only; leave empty when calling this method.

1 call to FormErrorHandler::setElementErrorsFromFormState()
FormErrorHandler::handleFormErrors in core/lib/Drupal/Core/Form/FormErrorHandler.php
Handles form errors after form validation.

File

core/lib/Drupal/Core/Form/FormErrorHandler.php, line 107

Class

FormErrorHandler
Handles form errors.

Namespace

Drupal\Core\Form

Code

protected function setElementErrorsFromFormState(array &$form, FormStateInterface $form_state, array &$elements = []) {

  // At the start of traversing up the form tree set the to be processed
  // elements to the complete form structure by reference so that we can
  // modify the original form. When processing grouped elements a reference to
  // the complete form is needed.
  if (empty($elements)) {
    $elements =& $form;
  }

  // Recurse through all element children.
  foreach (Element::children($elements) as $key) {
    if (!empty($elements[$key])) {

      // Get the child by reference so that we can update the original form.
      $child =& $elements[$key];

      // Call self to traverse up the form tree. The current element's child
      // contains the next elements to be processed.
      $this
        ->setElementErrorsFromFormState($form, $form_state, $child);
      $children_errors = [];

      // Inherit all recorded "children errors" of the direct child.
      if (!empty($child['#children_errors'])) {
        $children_errors = $child['#children_errors'];
      }

      // Additionally store the errors of the direct child itself, keyed by
      // its parent elements structure.
      if (!empty($child['#errors'])) {
        $child_parents = implode('][', $child['#array_parents']);
        $children_errors[$child_parents] = $child['#errors'];
      }
      if (!empty($elements['#children_errors'])) {
        $elements['#children_errors'] += $children_errors;
      }
      else {
        $elements['#children_errors'] = $children_errors;
      }

      // If this direct child belongs to a group populate the grouping element
      // with the children errors.
      if (!empty($child['#group'])) {
        $parents = explode('][', $child['#group']);
        $group_element = NestedArray::getValue($form, $parents);
        if (isset($group_element['#children_errors'])) {
          $group_element['#children_errors'] = $group_element['#children_errors'] + $children_errors;
        }
        else {
          $group_element['#children_errors'] = $children_errors;
        }
        NestedArray::setValue($form, $parents, $group_element);
      }
    }
  }

  // Store the errors for this element on the element directly.
  $elements['#errors'] = $form_state
    ->getError($elements);
}