You are here

public static function Dazzler::addSuggestions in Formdazzle! 2.x

Adds theme suggestions to form elements.

Parameters

array $element: The form or form element to give a template suggestion to.

string $form_id: The ID of the form the element belongs to.

string $form_id_suggestion: A suggestion to use based on the form ID.

2 calls to Dazzler::addSuggestions()
Dazzler::traverse in src/Dazzler.php
A recursive helper function to traverse all form element children.
DazzlerTest::testAddSuggestions in tests/src/Unit/DazzlerTest.php
@covers ::addSuggestions

File

src/Dazzler.php, line 212

Class

Dazzler
A class providing methods to modify Drupal form elements.

Namespace

Drupal\formdazzle

Code

public static function addSuggestions(array &$element, $form_id, $form_id_suggestion) {
  $needs_theme_suggestion = isset($element['#theme']);
  $needs_theme_wrapper_suggestion = isset($element['#theme_wrappers']);

  // #theme can be a string or an array. To ease processing, we ensure it is
  // an array and flatten single-item arrays before we leave this function.
  $flatten_theme_array = FALSE;
  if ($needs_theme_suggestion && !is_array($element['#theme'])) {
    $element['#theme'] = [
      $element['#theme'],
    ];
    $flatten_theme_array = TRUE;
  }
  $last_suggestion_key = $needs_theme_suggestion ? array_key_last($element['#theme']) : NULL;
  $last_suggestion = !is_null($last_suggestion_key) ? $element['#theme'][$last_suggestion_key] : '';

  // Find the form element name.
  $name = '';
  if (isset($element['#name'])) {
    $name = $element['#name'];
  }
  elseif (isset($element['#webform_key'])) {
    $name = $element['#webform_key'];
  }
  elseif (isset($element['#parents']) && count($element['#parents'])) {

    // Based on Drupal\Core\Form\FormBuilder::handleInputElement().
    if (isset($element['#type']) && $element['#type'] === 'file') {
      $name = 'files_' . $element['#parents'][0];
    }
    else {
      $name = implode('_', $element['#parents']);
    }
  }
  $name_suggestion = '';
  if ($name) {

    // Ensure the name is a proper suggestion string.
    // Based on regex in Drupal\Component\Utility\Html::getId().
    $name = strtolower(strtr($name, [
      ' ' => '_',
      '.' => '_',
      '-' => '_',
      '/' => '_',
      '[' => '_',
      ']' => '',
    ]));
    $name = preg_replace('/[^a-z0-9_]/', '', $name);
    $name_suggestion = '__' . $name;
  }

  // Most Drupal\Core\Render\Element's have generic suggestions, but some
  // don't have them when they should.
  $type = isset($element['#type']) ? $element['#type'] : '';
  $type_suggestion = '';
  switch ($type) {
    case 'actions':
    case 'more_link':
    case 'password_confirm':
    case 'system_compact_link':
      $type_suggestion = '__' . $type;
      break;
  }

  // Don't add name suggestions that are redundant with type suggestions.
  if ($type_suggestion && $type_suggestion === $name_suggestion) {
    $name_suggestion = '';
  }

  // Use the same theme suggestions for each theme hook.
  $suggestion_suffix = $type_suggestion . '__' . $form_id_suggestion . $name_suggestion;

  // We want to ensure that all form elements have a generic theme suggestion.
  // But form_element doesn't create a label element until after form_alter
  // hooks have run, so we have to add suggestion data so that later hooks can
  // add suggestions to the form.
  $is_form_element = $needs_theme_wrapper_suggestion && in_array('form_element', $element['#theme_wrappers']);
  if ($is_form_element) {
    $element['#formdazzle'] = [
      'suggestion_suffix' => $suggestion_suffix,
      'form_id' => $form_id,
      'form_id_suggestion' => $form_id_suggestion,
      'form_element_name' => $name,
    ];
  }

  // Add theme suggestions to #theme.
  // In Drupal\Core\Theme\ThemeManager::render(), if an array of hook
  // candidates is passed, use the first exact match. If there are no exact
  // matches, the last hook candidate is checked for more generic fallbacks by
  // stripping off successive __[suggestion] parts from the end of the string
  // until a match is made.
  if ($needs_theme_suggestion) {
    if ($type === 'form') {

      // This is technically a duplicate of an existing suggestion, but Twig
      // debugging doesn't show the suggestions unless we add this duplicate.
      // @see https://www.drupal.org/project/drupal/issues/2118743
      if ($last_suggestion === 'views_exposed_form') {
        $element['#theme'][$last_suggestion_key] .= str_replace('__views__', '__', $suggestion_suffix);
      }
    }
    else {
      $element['#theme'][$last_suggestion_key] .= $suggestion_suffix;
    }

    // If we converted a #theme string into an array, convert it back.
    if ($flatten_theme_array && count($element['#theme']) === 1) {
      $element['#theme'] = $element['#theme'][0];
    }
  }

  // Add theme suggestions to #theme_wrappers.
  // In Drupal\Core\Theme\ThemeManager::render(), #theme wrappers is an array
  // of theme hooks. The keys of the array are either numeric keys (with the
  // theme hook as the value) or strings containing the theme hook (and the
  // value is an array of data not important to FormDazzle.) e.g.
  // [ 0 => 'theme_hook', 'theme_hook' => ['some' => 'data']].
  if ($needs_theme_wrapper_suggestion) {
    $add_suggestions_to_keys = FALSE;
    foreach ($element['#theme_wrappers'] as $key => $value) {
      if (is_string($key)) {
        $add_suggestions_to_keys = TRUE;
      }
      elseif (strpos($value, $suggestion_suffix) === FALSE) {
        $element['#theme_wrappers'][$key] = $value . $suggestion_suffix;
      }
    }

    // In order to add suggestions to array keys, we have to rebuild the array
    // to ensure that the order of the array isn't disturbed.
    if ($add_suggestions_to_keys) {
      $theme_wrappers = $element['#theme_wrappers'];
      $element['#theme_wrappers'] = [];
      foreach ($theme_wrappers as $key => $value) {
        if (is_string($key)) {
          $element['#theme_wrappers'][$key . $suggestion_suffix] = $value;
        }
        else {
          $element['#theme_wrappers'][] = $value;
        }
      }
    }
  }
}