
Problem/Motivation
I faced very «edge case» situation with \Drupal\commerce_checkout\Plugin\Commerce\CheckoutFlow\CheckoutFlowBase
.
I have those steps in the checkout flow:
- login (Pane for login or register new customer)
- required_profile_data (Pane which ask user to fill out required fields which missing values)
- shipping (Pane with shipping methods)
- complete
The most important step is required_profile_data. This is a custom pane with entity inline form for the user entity. This is a multistep form. It has two steps, and steps stored in form state storage. Since it's the multistep form I have specific submitPaneForm
method which of course set next step if needed and set the form to rebuild by $form_state->setRebuild();
. And there is problem lies.
All submits from pane form will trigger CheckoutFlowBase::submitForm
which sets next step id and redirect response for the form.
if ($next_step_id = $this->getNextStepId($form['#step_id'])) {
$this->order->set('checkout_step', $next_step_id);
$this->onStepChange($next_step_id);
$form_state->setRedirect('commerce_checkout.form', [
'commerce_order' => $this->order->id(),
'step' => $next_step_id,
]);
}
In my case, the first step is not final for current pane form and form rebuilds correctly and user will see the second step of the pane. But if the user leaves checkout at this moment and then return later, it will be on the «next step», which is shipping in my case. In such a way they can skip fill out required fields.
Currently, I fix it this way.
1. Add additional submit handler to the next action (for steps which are not final).
$complete_form['actions']['next']['#submit'][] = [$this, 'resetStepId'];
2. Add a new method which «fix» the step to current again.
public function resetStepId(array &$form, FormStateInterface $form_state) {
$this->order->set('checkout_step', $form['#step_id']);
$this->order->save();
}
This works as expected, but that's is not very obvious behavior. Also, this method will trigger the entity save twice per request.
Proposed resolution
1. Check form rebuild status and if it rebuilds don't change step and don't set redirect response.
if (!$form_step->isRebuilding() && $next_step_id = $this->getNextStepId($form['#step_id'])) {
I think there is no need to change step and set redirect if the form is rebuilding.
2. Add new storage value to handle this manually.
if (!$form_step->get('skip_step_change') && $next_step_id = $this->getNextStepId($form['#step_id'])) {
Comments
Comment #2
niklanComment #3
andypostFixed title, looks like it's not a feature