You are here

WebformSame.php in Webform 6.x

File

src/Plugin/WebformElement/WebformSame.php
View source
<?php

namespace Drupal\webform\Plugin\WebformElement;

use Drupal\Core\Form\FormStateInterface;
use Drupal\webform\Utility\WebformElementHelper;
use Drupal\webform\WebformSubmissionInterface;

/**
 * Provides a 'webform_same' element.
 *
 * @WebformElement(
 *   id = "webform_same",
 *   label = @Translation("Same as…"),
 *   description = @Translation("Provides a form element for syncing the value of two elements."),
 *   category = @Translation("Advanced elements"),
 * )
 */
class WebformSame extends Checkbox {

  /**
   * {@inheritdoc}
   */
  protected function defineDefaultProperties() {
    $properties = [
      'source' => '',
      'destination' => '',
      'destination_state' => 'visible',
    ] + parent::defineDefaultProperties();
    unset($properties['required'], $properties['required_error']);
    return $properties;
  }

  /****************************************************************************/

  /**
   * {@inheritdoc}
   */
  public function form(array $form, FormStateInterface $form_state) {

    /** @var \Drupal\webform\WebformInterface $webform */
    $webform = $form_state
      ->getFormObject()
      ->getWebform();
    $form = parent::form($form, $form_state);

    // Get element's as options.
    $ignored_types = [
      'webform_same',
    ];
    $flattened_elements = $webform
      ->getElementsInitializedFlattenedAndHasValue();
    $options = [];
    foreach ($flattened_elements as $element_key => $element) {
      $element_plugin = $this->elementManager
        ->getElementInstance($element);
      if (!in_array($element_plugin
        ->getPluginId(), $ignored_types)) {
        $options[(string) $element_plugin
          ->getPluginLabel()][$element_key] = $element['#admin_title'];
      }
    }
    ksort($options);
    $form['same'] = [
      '#type' => 'fieldset',
      '#title' => $this
        ->t('Same as settings'),
      '#destination' => $this
        ->t('Please note, the source and destination element must be the same element types.'),
    ];
    $form['same']['source'] = [
      '#type' => 'select',
      '#title' => $this
        ->t('Source element'),
      '#options' => $options,
      '#required' => TRUE,
    ];
    $form['same']['destination'] = [
      '#type' => 'select',
      '#title' => $this
        ->t('Destination element'),
      '#options' => $options,
      '#required' => TRUE,
    ];
    $form['same']['destination_state'] = [
      '#type' => 'select',
      '#title' => $this
        ->t('Destination state'),
      '#description' => $this
        ->t("Determine how the destination element's state is toggled when 'Same as' is checked"),
      '#options' => [
        'visible' => $this
          ->t('Show/hide'),
        'visible-slide' => $this
          ->t('Slide in/out'),
      ],
      '#required' => TRUE,
    ];
    $form['same']['destination']['#element_validate'] = [
      [
        get_called_class(),
        'validateDestination',
      ],
    ];
    return $form;
  }

  /**
   * Form API callback. Validate webform same as destination.
   */
  public static function validateDestination(array &$element, FormStateInterface $form_state) {

    /** @var \Drupal\webform\WebformInterface $webform */
    $webform = $form_state
      ->getFormObject()
      ->getWebform();

    // Get source and destination elements.
    $values = $form_state
      ->getValues();
    $source = $values['properties']['source'];
    $source_element = $webform
      ->getElement($source);
    $destination = $values['properties']['destination'];
    $destination_element = $webform
      ->getElement($destination);
    if ($destination === $source) {
      $form_state
        ->setError($element, t('The source and destination can not be the same element.'));
    }
    elseif ($destination_element['#type'] !== $source_element['#type']) {

      /** @var \Drupal\webform\Plugin\WebformElementManagerInterface $element_manager */
      $element_manager = \Drupal::service('plugin.manager.webform.element');
      $element_plugin = $element_manager
        ->getElementInstance($element);
      $t_args = [
        '@type' => $element_plugin
          ->getPluginLabel(),
        '@source' => $source_element['#admin_title'],
        '@destination' => $destination_element['#admin_title'],
      ];
      $form_state
        ->setError($element, t('The destination element (@destination) must be the same element type (@type) as source element (@source).', $t_args));
    }
  }

  /**
   * {@inheritdoc}
   */
  public function alterForm(array &$element, array &$form, FormStateInterface $form_state) {
    if (!isset($element['#source']) || !isset($element['#destination'])) {
      return;
    }

    // Get source element.
    $source = $element['#source'];
    $source_element = WebformElementHelper::getElement($form, $source);
    if (!$source_element) {
      return;
    }

    // Get destination element.
    $destination = $element['#destination'];
    $destination_element =& WebformElementHelper::getElement($form, $destination);
    if (!$destination_element) {
      return;
    }

    // Add #states to destination element.
    $selector = ':input[name="' . $element['#webform_key'] . '"]';
    $state = !empty($element['#destination_state']) ? $element['#destination_state'] : 'visible';
    $destination_element['#states'][$state][$selector] = [
      'checked' => FALSE,
    ];
    $destination_element['#states_clear'] = FALSE;

    // Track webform same elements and add validation callback used
    // to sync source to destination.
    $form += [
      '#webform_same' => [],
    ];
    $form['#webform_same'][$element['#webform_key']] = [
      'source' => $source,
      'destination' => $destination,
    ];
    $form['#validate'][] = [
      get_called_class(),
      'validateForm',
    ];
  }

  /**
   * Webform validation handler for webform same element.
   */
  public static function validateForm(&$form, FormStateInterface $form_state) {
    foreach ($form['#webform_same'] as $element_key => $settings) {
      if ($form_state
        ->getValue($element_key)) {
        $form_state
          ->setValue($settings['destination'], $form_state
          ->getValue($settings['source']));
      }
    }
  }

  /**
   * {@inheritdoc}
   */
  public function preSave(array &$element, WebformSubmissionInterface $webform_submission) {

    // Make sure destination is always sync'd when a webform submission
    // is saved.
    $webform = $webform_submission
      ->getWebform();
    $same = $webform_submission
      ->getElementData($element['#webform_key']);
    if (!$same) {
      return;
    }
    $source = $element['#source'];
    $destination = $element['#destination'];

    // Make sure source and destination elements exist.
    if (!$webform
      ->getElement($source) || !$webform
      ->getElement($destination)) {
      return;
    }

    // Sync source data with destination data.
    $source_data = $webform_submission
      ->getElementData($source);
    $webform_submission
      ->setElementData($destination, $source_data);
  }

  /**
   * {@inheritdoc}
   */
  public function preview() {
    return [
      '#type' => $this
        ->getTypeName(),
      '#title' => $this
        ->t('Billing address is the same as the shipping address'),
    ];
  }

}

Classes

Namesort descending Description
WebformSame Provides a 'webform_same' element.