Skip to content

Commit 622bcd5

Browse files
bojanzbojanz
authored andcommitted
Issue #2886812 by bojanz, joachim: PluginSelectWidget should support plugins which don't implement PluginFormInterface
1 parent 5a1f9ee commit 622bcd5

File tree

4 files changed

+141
-40
lines changed

4 files changed

+141
-40
lines changed

src/Plugin/Field/FieldWidget/PluginSelectWidget.php

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use Drupal\Core\Field\WidgetBase;
1111
use Drupal\Core\Form\FormStateInterface;
1212
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
13+
use Drupal\Core\Plugin\PluginFormInterface;
1314
use Symfony\Component\DependencyInjection\ContainerInterface;
1415

1516
/**
@@ -76,9 +77,10 @@ public static function create(ContainerInterface $container, array $configuratio
7677
public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
7778
list(, $plugin_type) = explode(':', $this->fieldDefinition->getType());
7879

80+
$definitions = $this->pluginManager->getDefinitions();
7981
$plugins = array_map(function ($definition) {
8082
return $definition['label'];
81-
}, $this->pluginManager->getDefinitions());
83+
}, $definitions);
8284
asort($plugins);
8385
$target_plugin_id_parents = array_merge($element['#field_parents'], [$items->getName(), $delta, 'target_plugin_id']);
8486
$target_plugin_id = NestedArray::getValue($form_state->getUserInput(), $target_plugin_id_parents);
@@ -105,27 +107,48 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen
105107
'#type' => 'select',
106108
'#title' => $this->fieldDefinition->getLabel(),
107109
'#options' => $plugins,
108-
'#ajax' => [
109-
'callback' => [get_class($this), 'ajaxRefresh'],
110-
'wrapper' => $ajax_wrapper_id,
111-
],
112110
'#default_value' => $target_plugin_id,
113111
'#required' => $this->fieldDefinition->isRequired(),
114112
];
115113
if (!$element['target_plugin_id']['#required']) {
116114
$element['target_plugin_id']['#empty_value'] = '';
117115
}
118-
119-
$element['target_plugin_configuration'] = [
120-
'#type' => 'commerce_plugin_configuration',
121-
'#plugin_type' => $plugin_type,
122-
'#plugin_id' => $target_plugin_id,
123-
'#default_value' => $target_plugin_configuration,
124-
];
116+
if (self::supportsConfiguration($definitions)) {
117+
$element['target_plugin_id']['#ajax'] = [
118+
'callback' => [get_class($this), 'ajaxRefresh'],
119+
'wrapper' => $ajax_wrapper_id,
120+
];
121+
$element['target_plugin_configuration'] = [
122+
'#type' => 'commerce_plugin_configuration',
123+
'#plugin_type' => $plugin_type,
124+
'#plugin_id' => $target_plugin_id,
125+
'#default_value' => $target_plugin_configuration,
126+
];
127+
}
125128

126129
return $element;
127130
}
128131

132+
/**
133+
* Determines whether plugin configuration is supported.
134+
*
135+
* Supported if the plugins implement PluginFormInterface.
136+
*
137+
* @param array $definitions
138+
* The available plugin definitions.
139+
*
140+
* @return bool
141+
* TRUE if plugin configuration is supported, FALSE otherwise.
142+
*/
143+
protected function supportsConfiguration(array $definitions) {
144+
// The plugin manager has $this->pluginInterface, but there's no getter
145+
// for it, so it can't be used to check for PluginFormInterface.
146+
// Instead, we assume that all plugins implement the same interfaces,
147+
// and perform the check against the first plugin.
148+
$definition = reset($definitions);
149+
return is_subclass_of($definition['class'], PluginFormInterface::class);
150+
}
151+
129152
/**
130153
* Ajax callback.
131154
*/

tests/modules/commerce_test/commerce_test.services.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,8 @@ services:
33
class: Drupal\commerce_test\TestAvailabilityChecker
44
tags:
55
- { name: commerce.availability_checker }
6+
7+
commerce_test.referenceable_plugin_types_subscriber:
8+
class: \Drupal\commerce_test\EventSubscriber\ReferenceablePluginTypesSubscriber
9+
tags:
10+
- { name: event_subscriber }
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php
2+
3+
namespace Drupal\commerce_test\EventSubscriber;
4+
5+
use Drupal\commerce\Event\CommerceEvents;
6+
use Drupal\commerce\Event\ReferenceablePluginTypesEvent;
7+
use Drupal\Core\StringTranslation\StringTranslationTrait;
8+
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
9+
10+
class ReferenceablePluginTypesSubscriber implements EventSubscriberInterface {
11+
12+
use StringTranslationTrait;
13+
14+
/**
15+
* {@inheritdoc}
16+
*/
17+
public static function getSubscribedEvents() {
18+
$events[CommerceEvents::REFERENCEABLE_PLUGIN_TYPES][] = ['onPluginTypes'];
19+
return $events;
20+
}
21+
22+
/**
23+
* Registers the 'commerce_payment_method_type' plugin type as referenceable.
24+
*
25+
* Needed by PluginSelectTest.
26+
*
27+
* @param \Drupal\commerce\Event\ReferenceablePluginTypesEvent $event
28+
* The event.
29+
*/
30+
public function onPluginTypes(ReferenceablePluginTypesEvent $event) {
31+
$types = $event->getPluginTypes();
32+
$types['commerce_payment_method_type'] = $this->t('Payment method type');
33+
$event->setPluginTypes($types);
34+
}
35+
36+
}

tests/src/FunctionalJavascript/PluginSelectTest.php

Lines changed: 65 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ class PluginSelectTest extends CommerceBrowserTestBase {
3535
'entity_test',
3636
'commerce_test',
3737
'commerce_order',
38+
'commerce_payment',
3839
];
3940

4041
/**
@@ -53,61 +54,75 @@ protected function setUp() {
5354
parent::setUp();
5455

5556
Role::create(['id' => 'test_role', 'label' => $this->randomString()])->save();
56-
$field_storage = FieldStorageConfig::create([
57-
'field_name' => 'test_conditions',
58-
'entity_type' => 'entity_test',
59-
'type' => 'commerce_plugin_item:commerce_condition',
60-
]);
61-
$field_storage->save();
62-
63-
$field = FieldConfig::create([
64-
'field_name' => 'test_conditions',
65-
'entity_type' => 'entity_test',
66-
'bundle' => 'entity_test',
67-
]);
68-
$field->save();
69-
7057
$this->entityTestStorage = $this->container->get('entity_type.manager')->getStorage('entity_test');
7158
}
7259

7360
/**
7461
* Tests the plugin_select widget.
7562
*/
7663
public function testPluginSelect() {
64+
$this->createField('commerce_condition');
7765
$display = commerce_get_entity_display('entity_test', 'entity_test', 'form');
78-
$display->setComponent('test_conditions', [
66+
$display->setComponent('test_plugin', [
7967
'type' => 'commerce_plugin_select',
8068
])->save();
8169

82-
$this->doTest();
70+
$this->doTestConditions();
8371
}
8472

8573
/**
8674
* Tests the plugin_radios widget.
8775
*/
8876
public function testPluginRadios() {
77+
$this->createField('commerce_condition');
8978
$display = commerce_get_entity_display('entity_test', 'entity_test', 'form');
90-
$display->setComponent('test_conditions', [
79+
$display->setComponent('test_plugin', [
9180
'type' => 'commerce_plugin_radios',
9281
])->save();
9382

94-
$this->doTest();
83+
$this->doTestConditions();
9584
}
9685

9786
/**
98-
* Performs the assertions common to both test methods.
87+
* Tests the plugin_select widget on a plugin type without configuration.
9988
*/
100-
protected function doTest() {
89+
public function testPluginSelectWithoutConfiguration() {
90+
$this->createField('commerce_payment_method_type');
91+
$display = commerce_get_entity_display('entity_test', 'entity_test', 'form');
92+
$display->setComponent('test_plugin', [
93+
'type' => 'commerce_plugin_select',
94+
])->save();
95+
10196
$entity = $this->createEntity('entity_test', [
10297
'name' => 'Test',
10398
]);
10499
$this->drupalGet($entity->toUrl('edit-form'));
105-
$this->getSession()->getPage()->fillField('test_conditions[0][target_plugin_id]', 'order_item_quantity');
100+
101+
$this->submitForm([
102+
'test_plugin[0][target_plugin_id]' => 'paypal',
103+
], 'Save');
104+
$this->assertSession()->pageTextContains('entity_test 1 has been updated.');
105+
106+
$this->entityTestStorage->resetCache([$entity->id()]);
107+
$entity = $this->entityTestStorage->load($entity->id());
108+
$this->assertEquals('paypal', $entity->test_plugin->target_plugin_id);
109+
$this->assertEquals([], $entity->test_plugin->target_plugin_configuration);
110+
}
111+
112+
/**
113+
* Tests the configuration common to testPluginSelect and testPluginRadios.
114+
*/
115+
protected function doTestConditions() {
116+
$entity = $this->createEntity('entity_test', [
117+
'name' => 'Test',
118+
]);
119+
$this->drupalGet($entity->toUrl('edit-form'));
120+
$this->getSession()->getPage()->fillField('test_plugin[0][target_plugin_id]', 'order_item_quantity');
106121
$this->waitForAjaxToFinish();
107122

108123
$this->submitForm([
109-
'test_conditions[0][target_plugin_configuration][order_item_quantity][operator]' => '==',
110-
'test_conditions[0][target_plugin_configuration][order_item_quantity][quantity]' => '99',
124+
'test_plugin[0][target_plugin_configuration][order_item_quantity][operator]' => '==',
125+
'test_plugin[0][target_plugin_configuration][order_item_quantity][quantity]' => '99',
111126
], 'Save');
112127
$this->assertSession()->pageTextContains('entity_test 1 has been updated.');
113128

@@ -116,16 +131,16 @@ protected function doTest() {
116131
$this->assertEquals([
117132
'operator' => '==',
118133
'quantity' => 99,
119-
], $entity->test_conditions->target_plugin_configuration);
134+
], $entity->test_plugin->target_plugin_configuration);
120135

121136
// Select the other condition.
122137
$this->drupalGet($entity->toUrl('edit-form'));
123-
$this->getSession()->getPage()->fillField('test_conditions[0][target_plugin_id]', 'order_total_price');
138+
$this->getSession()->getPage()->fillField('test_plugin[0][target_plugin_id]', 'order_total_price');
124139
$this->waitForAjaxToFinish();
125140

126141
$this->submitForm([
127-
'test_conditions[0][target_plugin_configuration][order_total_price][operator]' => '<',
128-
'test_conditions[0][target_plugin_configuration][order_total_price][amount][number]' => '6.67',
142+
'test_plugin[0][target_plugin_configuration][order_total_price][operator]' => '<',
143+
'test_plugin[0][target_plugin_configuration][order_total_price][amount][number]' => '6.67',
129144
], 'Save');
130145
$this->assertSession()->pageTextContains('entity_test 1 has been updated.');
131146

@@ -137,7 +152,29 @@ protected function doTest() {
137152
'number' => '6.67',
138153
'currency_code' => 'USD',
139154
],
140-
], $entity->test_conditions->target_plugin_configuration);
155+
], $entity->test_plugin->target_plugin_configuration);
156+
}
157+
158+
/**
159+
* Creates a commerce_plugin_item field for the given plugin type.
160+
*
161+
* @param string $plugin_type
162+
* The plugin type.
163+
*/
164+
protected function createField($plugin_type) {
165+
$field_storage = FieldStorageConfig::create([
166+
'field_name' => 'test_plugin',
167+
'entity_type' => 'entity_test',
168+
'type' => 'commerce_plugin_item:' . $plugin_type,
169+
]);
170+
$field_storage->save();
171+
172+
$field = FieldConfig::create([
173+
'field_name' => 'test_plugin',
174+
'entity_type' => 'entity_test',
175+
'bundle' => 'entity_test',
176+
]);
177+
$field->save();
141178
}
142179

143180
}

0 commit comments

Comments
 (0)