You are here

protected function WebformSubmissionConditionsValidator::getBuildElementsRecursive in Webform 6.x

Same name and namespace in other branches
  1. 8.5 src/WebformSubmissionConditionsValidator.php \Drupal\webform\WebformSubmissionConditionsValidator::getBuildElementsRecursive()

Recurse through a form, review #states/#required, and get visible elements.

Parameters

array $elements: Visible elements with #states property.

array $form: An associative array containing the structure of the form.

array $parent_states: An associative array containing 'required'/'optional' states from parent container to be set on the element.

1 call to WebformSubmissionConditionsValidator::getBuildElementsRecursive()
WebformSubmissionConditionsValidator::getBuildElements in src/WebformSubmissionConditionsValidator.php
Build and get visible elements from a form.

File

src/WebformSubmissionConditionsValidator.php, line 913

Class

WebformSubmissionConditionsValidator
Webform submission conditions (#states) validator.

Namespace

Drupal\webform

Code

protected function getBuildElementsRecursive(array &$elements, array &$form, array $parent_states = []) {
  foreach ($form as $key => &$element) {
    if (!WebformElementHelper::isElement($element, $key)) {
      continue;
    }

    // Pass parent states to sub-element states.
    $subelement_states = $parent_states;
    if (!empty($element['#states']) || !empty($parent_states)) {
      if (!empty($element['#required'])) {

        // If element has #states and is #required there may be a conflict where
        // visibly hidden elements are required. The solution is to convert
        // #required into corresponding 'required/optional' states based on
        // 'visible/invisible' states.
        if (!isset($element['#states']['required']) && !isset($element['#states']['optional'])) {
          if (isset($element['#states']['visible'])) {
            $element['#states']['required'] = $element['#states']['visible'];
          }
          elseif (isset($element['#states']['visible-slide'])) {
            $element['#states']['required'] = $element['#states']['visible-slide'];
          }
          elseif (isset($element['#states']['invisible'])) {
            $element['#states']['optional'] = $element['#states']['invisible'];
          }
          elseif (isset($element['#states']['invisible-slide'])) {
            $element['#states']['optional'] = $element['#states']['invisible-slide'];
          }
          elseif ($parent_states) {
            $element += [
              '#states' => [],
            ];
            $element['#states'] += $parent_states;
          }
        }
        if (isset($element['#states']['optional']) || isset($element['#states']['required'])) {
          unset($element['#required']);
        }

        // Store a reference to the original #required value so that
        // form alter hooks know if the element's required/optional #states
        // are based the 'visible/invisible' states or the parent states.
        if (!isset($element['#required'])) {
          $element['#_required'] = TRUE;
        }
      }

      // If this container element has a visibility state, make its
      // sub-elements required/optional based on this state.
      if (isset($element['#states']['visible'])) {
        $subelement_states = [
          'required' => $element['#states']['visible'],
        ];
      }
      elseif (isset($element['#states']['visible-slide'])) {
        $subelement_states = [
          'required' => $element['#states']['visible-slide'],
        ];
      }
      elseif (isset($element['#states']['invisible'])) {
        $subelement_states = [
          'optional' => $element['#states']['invisible'],
        ];
      }
      elseif (isset($element['#states']['invisible-slide'])) {
        $subelement_states = [
          'optional' => $element['#states']['invisible-slide'],
        ];
      }
    }

    // Store original #access in #_webform_access for all elements.
    // @see \Drupal\webform\WebformSubmissionConditionsValidator::submitFormRecursive
    if (isset($element['#access'])) {
      $element['#_webform_access'] = $element['#access'];
    }

    // Skip if element is not accessible.
    if (!WebformElementHelper::isAccessibleElement($element)) {
      continue;
    }
    $elements[$key] =& $element;
    $this
      ->getBuildElementsRecursive($elements, $element, $subelement_states);
    $element_plugin = $this->elementManager
      ->getElementInstance($element);
    if ($element_plugin instanceof WebformCompositeBase && !$element_plugin
      ->hasMultipleValues($element)) {

      // Handle composite with single item.
      if ($subelement_states) {
        $composite_elements = $element_plugin
          ->getCompositeElements();
        foreach ($composite_elements as $composite_key => $composite_element) {

          // Skip if #access is set to FALSE.
          if (isset($element['#' . $composite_key . '__access']) && $element['#' . $composite_key . '__access'] === FALSE) {
            continue;
          }

          // Move #composite__required to #composite___required which triggers
          // conditional #_required handling.
          if (!empty($element['#' . $composite_key . '__required'])) {
            unset($element['#' . $composite_key . '__required']);
            $element['#' . $composite_key . '___required'] = TRUE;
            $element['#' . $composite_key . '__states'] = $subelement_states;
          }
        }
      }
    }
    elseif (isset($element['#element']) && isset($element['#webform_composite_elements'])) {

      // Handle composite with multiple items and custom composite elements.
      //
      // For $element['#elements'] ...
      // @see \Drupal\webform\Plugin\WebformElement\WebformCompositeBase::prepareMultipleWrapper
      //
      // For $element['#webform_composite_elements'] ...
      // @see \Drupal\webform\Plugin\WebformElement\WebformCompositeBase::initializeCompositeElements
      // @see \Drupal\webform_composite\Plugin\WebformElement\WebformComposite::initializeCompositeElements
      //
      // Recurse through a composite's sub elements.
      $this
        ->getBuildElementsRecursive($elements, $element['#element'], $subelement_states);
    }
  }
}