commerce_conditions_plus-1.0.x-dev/src/Element/ConditionsTable.php

src/Element/ConditionsTable.php
<?php

declare(strict_types=1);

namespace Drupal\commerce_conditions_plus\Element;

use Drupal\commerce\Element\CommerceElementTrait;
use Drupal\commerce\Plugin\Commerce\Condition\ConditionInterface;
use Drupal\commerce\Plugin\Commerce\InlineForm\PluginConfiguration;
use Drupal\Component\Utility\Html;
use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Element\FormElementBase;

/**
 * Extends the base class for form element plugins.
 *
 * @FormElement("commerce_conditions_table")
 */
class ConditionsTable extends FormElementBase {

  use CommerceElementTrait;

  /**
   * {@inheritdoc}
   */
  public function getInfo() {
    $class = get_class($this);
    return [
      '#input' => TRUE,
      '#tree' => TRUE,
      '#parent_entity_type' => NULL,
      '#entity_types' => [],
      '#default_value' => [],
      '#title' => '',

      '#process' => [
        [$class, 'attachElementSubmit'],
        [$class, 'processConditions'],
        [$class, 'processAjaxForm'],
      ],
      '#element_validate' => [
        [$class, 'validateElementSubmit'],
      ],
      '#commerce_element_submit' => [
        [$class, 'submitConditions'],
      ],
      '#theme_wrappers' => ['container'],
    ];
  }

  /**
   * Processes the conditions form element.
   *
   * @param array $element
   *   The form element to process.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The current state of the form.
   * @param array $complete_form
   *   The complete form structure.
   *
   * @return array
   *   The processed element.
   *
   * @throws \InvalidArgumentException
   *   Thrown for missing or malformed #parent_entity_type, #entity_types,
   *   #default_value properties.
   */
  public static function processConditions(array &$element, FormStateInterface $form_state, array &$complete_form) {
    if (empty($element['#parent_entity_type'])) {
      throw new \InvalidArgumentException('The commerce_conditions_table element requires the #parent_entity_type property.');
    }
    if (empty($element['#entity_types'])) {
      throw new \InvalidArgumentException('The commerce_conditions_table element requires the #entity_types property.');
    }
    if (!is_array($element['#entity_types'])) {
      throw new \InvalidArgumentException('The commerce_conditions_table #entity_types property must be an array.');
    }
    if (!is_array($element['#default_value'])) {
      throw new \InvalidArgumentException('The commerce_conditions_table #default_value property must be an array.');
    }
    /** @var \Drupal\commerce\ConditionManagerInterface $plugin_manager */
    $plugin_manager = \Drupal::service('plugin.manager.commerce_condition');
    $definitions = $plugin_manager->getFilteredDefinitions($element['#parent_entity_type'], $element['#entity_types']);
    $grouped_definitions = [];
    foreach ($definitions as $plugin_id => $definition) {
      $category = (string) $definition['category'];
      $grouped_definitions[$category][$plugin_id] = $definition['label'];
    }
    ksort($grouped_definitions);

    $ajax_wrapper_id = Html::getUniqueId('ajax-wrapper-conditions-table');
    $element['#prefix'] = '<div id="' . $ajax_wrapper_id . '">';
    $element['#suffix'] = '</div>';

    $conditions = $form_state->get('condition_plugins');
    // On first load, make sure we have the default value respected and stored
    // in the form state for further AJAX operations.
    if ($conditions === NULL) {
      $conditions = $element['#default_value'];
      $form_state->set('condition_plugins', $conditions);
    }
    $element['condition_plugins'] = [
      '#type' => 'table',
      '#header' => [
        t('Condition'),
        t('Settings'),
        t('Negate'),
        t('Operations'),
        t('Weight'),
      ],
      '#tabledrag' => [
        [
          'action' => 'match',
          'relationship' => 'parent',
          'group' => 'plugin-parent',
          'subgroup' => 'plugin-parent',
          // @todo each condition needs its own unique UUID.
          'source' => 'plugin-id',
          'hidden' => FALSE,
        ],
        [
          'action' => 'depth',
          'relationship' => 'group',
          'group' => 'plugin-depth',
          'hidden' => FALSE,
        ],
        [
          'action' => 'order',
          'relationship' => 'sibling',
          'group' => 'plugin-weight',
        ],
      ],
      '#rows' => [],
      '#empty' => 'No conditions will be applied',
      // #input defaults to TRUE, which breaks file fields on the value form.
      // This table is used for visual grouping only, the element itself
      // doesn't have any values of its own that need processing.
      '#input' => FALSE,
    ];

    $inline_form_manager = \Drupal::service('plugin.manager.commerce_inline_form');

    $max_weight = count($conditions);
    $renderer = \Drupal::getContainer()->get('renderer');
    foreach ($conditions as $index => $condition) {
      $element['condition_plugins'][$index] = [];
      $condition_form = &$element['condition_plugins'][$index];

      // The tabledrag element is always added to the first cell in the row,
      // so we add an empty cell to guide it there, for better styling.
      $condition_form['#attributes']['class'][] = 'draggable';
      $inline_form = $inline_form_manager->createInstance('plugin_configuration', [
        'plugin_type' => 'commerce_condition',
        'plugin_id' => $condition['plugin'],
        'plugin_configuration' => $condition['configuration'],
        'enforce_unique_parents' => FALSE,
      ]);
      assert($inline_form instanceof PluginConfiguration);

      $indentation = [];
      if (isset($condition['configuration']['depth']) && $condition['configuration']['depth'] > 0) {
        $indentation = [
          '#theme' => 'indentation',
          '#size' => $condition['configuration']['depth'],
        ];
      }
      $condition_form['label'] = [
        '#prefix' => !empty($indentation) ? $renderer->renderInIsolation($indentation) : '',
        '#markup' => $definitions[$condition['plugin']]['display_label'],
      ];
      $condition_form['label']['plugin'] = [
        '#type' => 'hidden',
        '#value' => $condition['plugin'],
        '#parents' => array_merge($element['#parents'], [$index, 'plugin']),
        '#attributes' => [
          'class' => ['plugin-id'],
        ],
      ];
      $condition_form['label']['parent'] = [
        '#type' => 'hidden',
        '#default_value' => $condition['configuration']['parent'] ?? '',
        '#parents' => array_merge($element['#parents'], [$index, 'parent']),
        '#attributes' => [
          'class' => ['plugin-parent'],
        ],
      ];
      $condition_form['label']['depth'] = [
        '#type' => 'hidden',
        '#default_value' => $condition['configuration']['depth'] ?? '',
        '#parents' => array_merge($element['#parents'], [$index, 'depth']),
        '#attributes' => [
          'class' => ['plugin-depth'],
        ],
      ];
      $condition_form['configuration'] = [
        '#inline_form' => $inline_form,
        '#parents' => array_merge(
          $element['#parents'],
          [$index, 'configuration']
        ),
      ];
      $condition_form['configuration'] = $inline_form->buildInlineForm($condition_form['configuration'], $form_state);

      // If the plugin provides its own negation, hide our negate checkbox.
      $condition_form['negate_condition'] = [
        '#type' => 'checkbox',
        '#title' => t('Negate'),
        '#parents' => array_merge(
          $element['#parents'],
          [$index, 'negate_condition']
        ),
        '#default_value' => $condition['configuration']['negate_condition'] ?? 0,
        '#access' => !isset($condition_form['configuration']['form']['negate']),
      ];

      $condition_form['operations'] = [
        '#type' => 'submit',
        '#name' => 'remove_value' . $index,
        '#value' => t('Remove'),
        '#limit_validation_errors' => [],
        '#submit' => [
          [static::class, 'removeConditionSubmit'],
        ],
        '#value_index' => $index,
        '#ajax' => [
          'callback' => [static::class, 'ajaxConditionsRefresh'],
          'wrapper' => $ajax_wrapper_id,
        ],
        '#parents' => array_merge($element['#parents'], [$index, 'remove']),
      ];

      // @todo need to add #weight value.
      // @see ProductAttributeForm for reading from user input.
      $condition_form['#weight'] = $index;
      $condition_form['sort_weight'] = [
        '#type' => 'weight',
        '#title' => t('Weight'),
        '#title_display' => 'invisible',
        '#delta' => $max_weight,
        '#default_value' => $condition['configuration']['sort_weight'] ?? $index,
        '#parents' => array_merge($element['#parents'], [$index, 'sort_weight']),
        '#attributes' => [
          'class' => ['plugin-weight'],
        ],
      ];
    }

    $element['add_new'] = [
      '#type' => 'container',
      '#attributes' => [
        'class' => ['container-inline'],
      ],
    ];
    $element['add_new']['conditions_id'] = [
      '#title' => 'Select a condition',
      '#title_display' => 'invisible',
      '#type' => 'select',
      '#default_value' => '',
      '#empty_option' => '- Choose -',
      '#options' => $grouped_definitions,
    ];

    // @todo if there are multiple of this element, we need a unique name.
    // The form state can pick the wrong button, as they are all [name=op].
    $element['add_new']['add_condition'] = [
      '#type' => 'submit',
      '#value' => 'Add',
      '#ajax' => [
        'callback' => [static::class, 'ajaxRefresh'],
        'wrapper' => $ajax_wrapper_id,
      ],
      // @todo add a validation on the selected condition plugin ID.
      '#validate' => [],
      '#limit_validation_errors' => [$element['#parents']],
      '#submit' => [
        [static::class, 'addNewCondition'],
      ],
      '#states' => [
        'disabled' => [
          'select[name="conditions[form][add_new][conditions_id]"]' => ['value' => ''],
        ],
      ],
    ];

    return $element;
  }

  /**
   * Submits the conditions.
   *
   * @param array $element
   *   An associative array containing the properties of the element.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The current state of the form.
   */
  public static function submitConditions(array &$element, FormStateInterface $form_state) {
    $values = $form_state->getValue($element['#parents']);
    $values = array_filter($values, static function ($key) {
      return is_int($key);
    }, ARRAY_FILTER_USE_KEY);
    $values = array_map(static function (array $condition) {
      $condition['configuration']['parent'] = $condition['parent'];
      $condition['configuration']['depth'] = (int) $condition['depth'];
      $condition['configuration']['sort_weight'] = $condition['sort_weight'];
      $condition['configuration']['negate_condition'] = $condition['negate_condition'];
      unset($condition['parent'], $condition['depth'], $condition['sort_weight'], $condition['negate_condition']);
      return $condition;
    }, $values);
    $form_state->setValueForElement($element, $values);
  }

  /**
   * {@inheritdoc}
   */
  public static function addNewCondition(&$form, FormStateInterface $form_state) {
    $element_parents = array_slice($form_state->getTriggeringElement()['#parents'], 0, -1);
    $values = $form_state->getValue($element_parents);

    $conditions = $form_state->get('condition_plugins');

    /** @var \Drupal\commerce\ConditionManagerInterface $plugin_manager */
    $plugin_manager = \Drupal::service('plugin.manager.commerce_condition');
    $instance = $plugin_manager->createInstance($values['conditions_id']);
    assert($instance instanceof ConditionInterface);

    $conditions[] = [
      'plugin' => $instance->getPluginId(),
      'configuration' => $instance->getConfiguration(),
    ];
    $form_state->set('condition_plugins', $conditions);
    $form_state->setRebuild();
  }

  /**
   * {@inheritdoc}
   */
  public static function removeConditionSubmit(&$form, FormStateInterface $form_state) {
    $value_index = $form_state->getTriggeringElement()['#value_index'];
    $conditions = $form_state->get('condition_plugins');
    unset($conditions[$value_index]);
    $form_state->set('condition_plugins', $conditions);
    $form_state->setRebuild();
  }

  /**
   * Ajax callback.
   */
  public static function ajaxRefresh(&$form, FormStateInterface $form_state) {
    $element_parents = array_slice($form_state->getTriggeringElement()['#array_parents'], 0, -2);
    return NestedArray::getValue($form, $element_parents);
  }

  /**
   * Ajax callback.
   */
  public static function ajaxConditionsRefresh(&$form, FormStateInterface $form_state) {
    // @todo merge above, but check the triggering element for slice depth.
    $element_parents = array_slice($form_state->getTriggeringElement()['#array_parents'], 0, -3);
    return NestedArray::getValue($form, $element_parents);
  }

}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc