Skip to content

Commit ddb4287

Browse files
committed
Issue #2845097: Introduce a CommerceElementBase
1 parent 70a62f2 commit ddb4287

File tree

6 files changed

+164
-125
lines changed

6 files changed

+164
-125
lines changed

modules/payment/src/Element/PaymentGatewayForm.php

+6-53
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,10 @@
22

33
namespace Drupal\commerce_payment\Element;
44

5+
use Drupal\commerce\Element\CommerceElementBase;
56
use Drupal\commerce_payment\Entity\EntityWithPaymentGatewayInterface;
67
use Drupal\commerce_payment\Exception\PaymentGatewayException;
78
use Drupal\Core\Form\FormStateInterface;
8-
use Drupal\Core\Render\Element;
9-
use Drupal\Core\Render\Element\RenderElement;
109

1110
/**
1211
* Provides a form element for embedding the payment gateway forms.
@@ -25,7 +24,7 @@
2524
*
2625
* @RenderElement("commerce_payment_gateway_form")
2726
*/
28-
class PaymentGatewayForm extends RenderElement {
27+
class PaymentGatewayForm extends CommerceElementBase {
2928

3029
/**
3130
* {@inheritdoc}
@@ -37,12 +36,14 @@ public function getInfo() {
3736
// The entity operated on. Instance of EntityWithPaymentGatewayInterface.
3837
'#default_value' => NULL,
3938
'#process' => [
39+
[$class, 'attachElementSubmit'],
4040
[$class, 'processForm'],
4141
],
4242
'#element_validate' => [
43+
[$class, 'validateElementSubmit'],
4344
[$class, 'validateForm'],
4445
],
45-
'#element_submit' => [
46+
'#commerce_element_submit' => [
4647
[$class, 'submitForm'],
4748
],
4849
'#theme_wrappers' => ['container'],
@@ -82,10 +83,6 @@ public static function processForm(array $element, FormStateInterface $form_stat
8283
if (isset($element['#page_title'])) {
8384
$complete_form['#title'] = $element['#page_title'];
8485
}
85-
// The #validate callbacks of the complete form run last.
86-
// That allows executeElementSubmitHandlers() to be completely certain that
87-
// the form has passed validation before proceeding.
88-
$complete_form['#validate'][] = [get_class(), 'executeElementSubmitHandlers'];
8986

9087
return $element;
9188
}
@@ -97,18 +94,8 @@ public static function processForm(array $element, FormStateInterface $form_stat
9794
* The form element.
9895
* @param \Drupal\Core\Form\FormStateInterface $form_state
9996
* The current state of the form.
100-
*
101-
* @throws \Exception
102-
* Thrown if button-level #validate handlers are detected on the parent
103-
* form, as a protection against buggy behavior.
10497
*/
10598
public static function validateForm(array &$element, FormStateInterface $form_state) {
106-
// Button-level #validate handlers replace the form-level ones, which means
107-
// that executeElementSubmitHandlers() won't be triggered.
108-
if ($handlers = $form_state->getValidateHandlers()) {
109-
throw new \Exception('The commerce_payment_gateway_form element is not compatible with submit buttons that set #validate handlers');
110-
}
111-
11299
$plugin_form = self::createPluginForm($element);
113100
$plugin_form->validateConfigurationForm($element, $form_state);
114101
}
@@ -144,7 +131,7 @@ public static function submitForm(array &$element, FormStateInterface $form_stat
144131
* The plugin form.
145132
*/
146133
public static function createPluginForm(array $element) {
147-
/** @var \Drupal\commerce\PluginForm\PluginFormFactoryInterface $plugin_form_factory */
134+
/** @var \Drupal\Core\Plugin\PluginFormFactoryInterface $plugin_form_factory */
148135
$plugin_form_factory = \Drupal::service('plugin_form.factory');
149136
/** @var \Drupal\commerce_payment\Entity\EntityWithPaymentGatewayInterface $entity */
150137
$entity = $element['#default_value'];
@@ -156,38 +143,4 @@ public static function createPluginForm(array $element) {
156143
return $plugin_form;
157144
}
158145

159-
/**
160-
* Submits elements by calling their #element_submit callbacks.
161-
*
162-
* Form API has no #element_submit, requiring us to simulate it by running
163-
* our #element_submit handlers either in the last step of validation, or the
164-
* first step of submission. In this case it's the last step of validation,
165-
* allowing exceptions thrown by the plugin to be converted into form errors.
166-
*
167-
* @param array $element
168-
* The form element.
169-
* @param \Drupal\Core\Form\FormStateInterface $form_state
170-
* The form state.
171-
*/
172-
public static function executeElementSubmitHandlers(array &$element, FormStateInterface $form_state) {
173-
if (!$form_state->isSubmitted() || $form_state->hasAnyErrors()) {
174-
// The form wasn't submitted (#ajax in progress) or failed validation.
175-
return;
176-
}
177-
178-
// Recurse through all children.
179-
foreach (Element::children($element) as $key) {
180-
if (!empty($element[$key])) {
181-
static::executeElementSubmitHandlers($element[$key], $form_state);
182-
}
183-
}
184-
185-
// If there are callbacks on this level, run them.
186-
if (!empty($element['#element_submit'])) {
187-
foreach ($element['#element_submit'] as $callback) {
188-
call_user_func_array($callback, [&$element, &$form_state]);
189-
}
190-
}
191-
}
192-
193146
}

modules/promotion/src/Plugin/Commerce/PromotionOffer/PercentageOffBase.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta
5454
*/
5555
public function validateConfigurationForm(array &$form, FormStateInterface $form_state) {
5656
$values = $form_state->getValue($form['#parents']);
57-
if (empty($values['target_plugin_configuration']['amount'])) {
57+
if (empty($values['amount'])) {
5858
$form_state->setError($form, $this->t('Percentage amount cannot be empty.'));
5959
}
6060
}

src/Element/CommerceElementBase.php

+101
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
<?php
2+
3+
namespace Drupal\commerce\Element;
4+
5+
use Drupal\Core\Form\FormStateInterface;
6+
use Drupal\Core\Render\Element;
7+
use Drupal\Core\Render\Element\FormElement;
8+
9+
/**
10+
* Provides a base class for Commerce form elements.
11+
*
12+
* Allows form elements to use #commerce_element_submit, a substitute
13+
* for the #element_submit that's missing from Drupal core.
14+
*
15+
* Each inheriting form element should add the attachElementSubmit and
16+
* validateElementSubmit callbacks to their getInfo() methods.
17+
*/
18+
abstract class CommerceElementBase extends FormElement {
19+
20+
/**
21+
* Attaches the #commerce_element_submit functionality.
22+
*
23+
* @param array $element
24+
* The form element being processed.
25+
* @param \Drupal\Core\Form\FormStateInterface $form_state
26+
* The current state of the form.
27+
* @param array $complete_form
28+
* The complete form structure.
29+
*
30+
* @return array
31+
* The processed form element.
32+
*/
33+
public static function attachElementSubmit(array $element, FormStateInterface $form_state, array &$complete_form) {
34+
if (isset($complete_form['#commerce_element_submit_attached'])) {
35+
return $element;
36+
}
37+
// The #validate callbacks of the complete form run last.
38+
// That allows executeElementSubmitHandlers() to be completely certain that
39+
// the form has passed validation before proceeding.
40+
$complete_form['#validate'][] = [get_class(), 'executeElementSubmitHandlers'];
41+
$complete_form['#commerce_element_submit_attached'] = TRUE;
42+
43+
return $element;
44+
}
45+
46+
/**
47+
* Confirms that #commerce_element_submit handlers can be run.
48+
*
49+
* @param array $element
50+
* The form element.
51+
* @param \Drupal\Core\Form\FormStateInterface $form_state
52+
* The current state of the form.
53+
*
54+
* @throws \Exception
55+
* Thrown if button-level #validate handlers are detected on the parent
56+
* form, as a protection against buggy behavior.
57+
*/
58+
public static function validateElementSubmit(array &$element, FormStateInterface $form_state) {
59+
// Button-level #validate handlers replace the form-level ones, which means
60+
// that executeElementSubmitHandlers() won't be triggered.
61+
if ($handlers = $form_state->getValidateHandlers()) {
62+
throw new \Exception('The current form must not have button-level #validate handlers');
63+
}
64+
}
65+
66+
/**
67+
* Submits elements by calling their #commerce_element_submit callbacks.
68+
*
69+
* Form API has no #element_submit, requiring us to simulate it by running
70+
* the #commerce_element_submit handlers either in the last step of
71+
* validation, or the first step of submission. In this case it's the last
72+
* step of validation, allowing thrown exceptions to be converted into form
73+
* errors.
74+
*
75+
* @param array $element
76+
* The form element.
77+
* @param \Drupal\Core\Form\FormStateInterface $form_state
78+
* The form state.
79+
*/
80+
public static function executeElementSubmitHandlers(array &$element, FormStateInterface $form_state) {
81+
if (!$form_state->isSubmitted() || $form_state->hasAnyErrors()) {
82+
// The form wasn't submitted (#ajax in progress) or failed validation.
83+
return;
84+
}
85+
86+
// Recurse through all children.
87+
foreach (Element::children($element) as $key) {
88+
if (!empty($element[$key])) {
89+
static::executeElementSubmitHandlers($element[$key], $form_state);
90+
}
91+
}
92+
93+
// If there are callbacks on this level, run them.
94+
if (!empty($element['#commerce_element_submit'])) {
95+
foreach ($element['#commerce_element_submit'] as $callback) {
96+
call_user_func_array($callback, [&$element, &$form_state]);
97+
}
98+
}
99+
}
100+
101+
}

0 commit comments

Comments
 (0)