Skip to content

Commit

Permalink
Issue #2837777 by jsacksick: Provide a commerce_plugin_radios field w…
Browse files Browse the repository at this point in the history
…idget
  • Loading branch information
jsacksick authored and bojanz committed Jan 6, 2017
1 parent e771612 commit 1578f7f
Show file tree
Hide file tree
Showing 7 changed files with 216 additions and 23 deletions.
19 changes: 19 additions & 0 deletions commerce.module
Expand Up @@ -17,6 +17,25 @@ function commerce_toolbar_alter(&$items) {
$items['administration']['#attached']['library'][] = 'commerce/toolbar';
}

/**
* Implements hook_field_widget_info_alter().
*
* Exposes the commerce_plugin_item widgets for each of the field type's
* derivatives, since core does not do it automatically.
*/
function commerce_field_widget_info_alter(array &$info) {
foreach (['commerce_plugin_select', 'commerce_plugin_radios'] as $widget) {
if (isset($info[$widget])) {
$field_type_manager = \Drupal::service('plugin.manager.field.field_type');
foreach ($field_type_manager->getDefinitions() as $key => $definition) {
if ($definition['id'] == 'commerce_plugin_item') {
$info[$widget]['field_types'][] = $key;
}
}
}
}
}

/**
* Implements hook_field_widget_form_alter().
*
Expand Down
47 changes: 33 additions & 14 deletions src/Element/PluginSelect.php
Expand Up @@ -36,6 +36,7 @@ public function getInfo() {
return [
'#input' => TRUE,
'#plugin_type' => NULL,
'#plugin_element_type' => 'select',
'#categories' => [],
'#title' => $this->t('Select plugin'),
'#process' => [
Expand All @@ -56,28 +57,25 @@ public static function processPluginSelect(&$element, FormStateInterface $form_s
if (!$element['#plugin_type']) {
throw new \InvalidArgumentException('You must specify the plugin type ID.');
}
if (!in_array($element['#plugin_element_type'], ['radios', 'select'])) {
throw new \InvalidArgumentException('The commerce_plugin_select element only supports select/radios.');
}

$element['#tree'] = TRUE;

/** @var \Drupal\Core\Executable\ExecutableManagerInterface $plugin_manager */
$plugin_manager = \Drupal::service('plugin.manager.' . $element['#plugin_type']);

$values = $element['#value'];

$target_plugin_id = !empty($values['target_plugin_id']) ? $values['target_plugin_id'] : '_none';

$ajax_wrapper_id = Html::getUniqueId('ajax-wrapper');
$ajax_settings = [
'callback' => [get_called_class(), 'pluginFormAjax'],
'wrapper' => $ajax_wrapper_id,
];

// Prefix and suffix used for Ajax replacement.
$element['#prefix'] = '<div id="' . $ajax_wrapper_id . '">';
$element['#suffix'] = '</div>';

// Store #array_parents in the form state, so we can get the elements from
// the complete form array by using only thes form state.
// the complete form array by using only the form state.
$element['array_parents'] = [
'#type' => 'value',
'#value' => $element['#array_parents'],
Expand All @@ -87,21 +85,27 @@ public static function processPluginSelect(&$element, FormStateInterface $form_s
'#type' => 'value',
'#value' => $element['#plugin_type'],
];

$element['target_plugin_id'] = [
'#type' => 'select',
'#type' => $element['#plugin_element_type'],
'#title' => $element['#title'],
'#multiple' => FALSE,
'#options' => [
'_none' => t('None'),
'#ajax' => [
'callback' => [get_called_class(), 'pluginFormAjax'],
'wrapper' => $ajax_wrapper_id,
],
'#ajax' => $ajax_settings,
'#default_value' => $target_plugin_id,
'#ajax_array_parents' => $element['#array_parents'],
'#required' => $element['#required'],
];

// Add a "_none" option if the element is not required.
if (!$element['#required']) {
$element['target_plugin_id']['#options']['_none'] = t('None');
}

$categories = array_combine($element['#categories'], $element['#categories']);
$has_categories = !empty($categories);
$definitions = [];
foreach ($plugin_manager->getDefinitions() as $definition) {
// If categories have been specified, limit definitions based on them.
if ($has_categories && !isset($categories[$definition['category']])) {
Expand All @@ -115,12 +119,23 @@ public static function processPluginSelect(&$element, FormStateInterface $form_s
else {
$element['target_plugin_id']['#options'][$definition['id']] = $definition['label'];
}
$definitions[] = $definition['id'];
}

// If the element is required, set the default value to the first plugin.
// definition available in the options array.
if ($element['#required']) {
if ($target_plugin_id == '_none' && !empty($element['target_plugin_id']['#options'])) {
$target_plugin_id = reset($definitions);
$values['target_plugin_configuration'] = [];
$element['target_plugin_id']['#default_value'] = $target_plugin_id;
}
}

if ($target_plugin_id != '_none') {
/** @var \Drupal\Core\Executable\ExecutableInterface $plugin */
$plugin = $plugin_manager->createInstance($target_plugin_id, $values['target_plugin_configuration']);
if ($plugin instanceof PluginFormInterface) {
if ($plugin instanceof PluginFormInterface) {
$element['target_plugin_configuration'] = [
'#tree' => TRUE,
];
Expand All @@ -135,9 +150,13 @@ public static function processPluginSelect(&$element, FormStateInterface $form_s
* Ajax callback.
*/
public static function pluginFormAjax(&$form, FormStateInterface &$form_state, Request $request) {
// Retrieve the element to be rendered.
$triggering_element = $form_state->getTriggeringElement();
while (!isset($triggering_element['#ajax_array_parents'])) {
array_pop($triggering_element['#array_parents']);
$triggering_element = NestedArray::getValue($form, $triggering_element['#array_parents']);
}
$form_element = NestedArray::getValue($form, $triggering_element['#ajax_array_parents']);

return $form_element;
}

Expand Down
21 changes: 21 additions & 0 deletions src/Event/CommerceEvents.php
@@ -0,0 +1,21 @@
<?php

namespace Drupal\commerce\Event;

/**
* Defines events for the base Commerce module.
*
* Note that submodules have their own defined events.
*/
final class CommerceEvents {

/**
* Name of the event fired when altering the referenceable plugin types.
*
* @Event
*
* @see \Drupal\commerce\Event\ReferenceablePluginTypesEvent.php
*/
const REFERENCEABLE_PLUGIN_TYPES = 'commerce.referenceable_plugin_types';

}
54 changes: 54 additions & 0 deletions src/Event/ReferenceablePluginTypesEvent.php
@@ -0,0 +1,54 @@
<?php

namespace Drupal\commerce\Event;

use Symfony\Component\EventDispatcher\Event;

/**
* Defines the referenceable plugin types event.
*
* @see \Drupal\commerce\Event\CommerceEvents
*/
class ReferenceablePluginTypesEvent extends Event {

/**
* The plugin types, in the id => label format.
*
* @var array
*/
protected $pluginTypes;

/**
* Constructs a new ReferenceablePluginTypesEvent object.
*
* @param array $plugin_types
* The plugin types, in the id => label format.
*/
public function __construct(array $plugin_types) {
$this->pluginTypes = $plugin_types;
}

/**
* Gets the plugin types.
*
* @return array
* The plugin types, in the id => label format.
*/
public function getPluginTypes() {
return $this->pluginTypes;
}

/**
* Sets the plugin types.
*
* @param array $plugin_types
* The plugin types, in the id => label format.
*
* @return $this
*/
public function setPluginTypes(array $plugin_types) {
$this->pluginTypes = $plugin_types;
return $this;
}

}
53 changes: 47 additions & 6 deletions src/Plugin/Field/FieldType/PluginItemDeriver.php
Expand Up @@ -2,29 +2,70 @@

namespace Drupal\commerce\Plugin\Field\FieldType;

use Drupal\commerce\Event\CommerceEvents;
use Drupal\commerce\Event\ReferenceablePluginTypesEvent;
use Drupal\Component\Plugin\Derivative\DeriverBase;
use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;

/**
* Deriver for the executable plugin item field type.
* Deriver for the commerce_plugin_item field type.
*/
class PluginItemDeriver extends DeriverBase {
class PluginItemDeriver extends DeriverBase implements ContainerDeriverInterface {

use StringTranslationTrait;

/**
* The event dispatcher.
*
* @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
*/
protected $eventDispatcher;

/**
* Constructs a new PluginItemDeriver object.
*
* @param string $base_plugin_id
* The base plugin ID.
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher
* The event dispatcher.
*/
public function __construct($base_plugin_id, EventDispatcherInterface $event_dispatcher) {
$this->basePluginId = $base_plugin_id;
$this->eventDispatcher = $event_dispatcher;
}

/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, $base_plugin_id) {
return new static(
$base_plugin_id,
$container->get('event_dispatcher')
);
}

/**
* {@inheritdoc}
*/
public function getDerivativeDefinitions($base_plugin_definition) {
$supported = [
$plugin_types = [
'condition' => $this->t('Conditions'),
'action' => $this->t('Action'),
'commerce_promotion_offer' => $this->t('Promotion offer'),
'commerce_promotion_condition' => $this->t('Promotion condition'),
];
// Core has no way to list plugin types, so each referenceable plugin
// type needs to register itself via the event.
$event = new ReferenceablePluginTypesEvent($plugin_types);
$this->eventDispatcher->dispatch(CommerceEvents::REFERENCEABLE_PLUGIN_TYPES, $event);
$plugin_types = $event->getPluginTypes();

foreach ($supported as $id => $label) {
$this->derivatives[$id] = [
'plugin_type' => $id,
foreach ($plugin_types as $plugin_type => $label) {
$this->derivatives[$plugin_type] = [
'plugin_type' => $plugin_type,
'label' => $label,
'category' => $this->t('Plugin'),
] + $base_plugin_definition;
Expand Down
40 changes: 40 additions & 0 deletions src/Plugin/Field/FieldWidget/PluginRadiosWidget.php
@@ -0,0 +1,40 @@
<?php

namespace Drupal\commerce\Plugin\Field\FieldWidget;

use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Form\FormStateInterface;

/**
* Plugin implementation of the 'commerce_plugin_radios' widget.
*
* @FieldWidget(
* id = "commerce_plugin_radios",
* label = @Translation("Plugin radios"),
* field_types = {
* "commerce_plugin_item"
* },
* )
*/
class PluginRadiosWidget extends PluginSelectWidget {

/**
* {@inheritdoc}
*/
public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
list($field_type, $plugin_type) = explode(':', $this->fieldDefinition->getType());
return [
'#type' => 'commerce_plugin_select',
'#plugin_element_type' => 'radios',
'#plugin_type' => $plugin_type,
'#categories' => $this->fieldDefinition->getSetting('categories'),
'#default_value' => [
'target_plugin_id' => $items[$delta]->target_plugin_id,
'target_plugin_configuration' => $items[$delta]->target_plugin_configuration,
],
'#required' => $this->fieldDefinition->isRequired(),
'#title' => $this->fieldDefinition->getLabel(),
];
}

}
5 changes: 2 additions & 3 deletions src/Plugin/Field/FieldWidget/PluginSelectWidget.php
Expand Up @@ -34,14 +34,15 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen
'target_plugin_id' => $items[$delta]->target_plugin_id,
'target_plugin_configuration' => $items[$delta]->target_plugin_configuration,
],
'#required' => $this->fieldDefinition->isRequired(),
'#title' => $this->fieldDefinition->getLabel(),
];
}

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

// Iterate through the provided values and run the plugin configuration form
// through the plugin's submit configuration form method, if available.
foreach ($values as $delta => &$item_value) {
Expand All @@ -50,15 +51,13 @@ public function massageFormValues(array $values, array $form, FormStateInterface
}

$element = NestedArray::getValue($form, $item_value['array_parents']);

/** @var \Drupal\Core\Executable\ExecutableManagerInterface $plugin_manager */
$plugin_manager = \Drupal::service('plugin.manager.' . $item_value['target_plugin_type']);
$plugin = $plugin_manager->createInstance($item_value['target_plugin_id'], $item_value['target_plugin_configuration']);

// If the plugin implements the PluginFormInterface, pass the values to
// its submit method for final processing.
if ($plugin instanceof PluginFormInterface) {

/** @var \Drupal\Component\Plugin\ConfigurablePluginInterface $plugin */
$plugin->submitConfigurationForm($element['target_plugin_configuration'], $form_state);
$item_value['target_plugin_configuration'] = $plugin->getConfiguration();
Expand Down

0 comments on commit 1578f7f

Please sign in to comment.