abstract class CheckoutFlowBase in Commerce Core 8.2
Provides the base checkout flow class.
Checkout flows should extend this class only if they don't want to use checkout panes. Otherwise they should extend CheckoutFlowWithPanesBase.
Hierarchy
- class \Drupal\Component\Plugin\PluginBase implements DerivativeInspectionInterface, PluginInspectionInterface
- class \Drupal\Core\Plugin\PluginBase uses DependencySerializationTrait, MessengerTrait, StringTranslationTrait
- class \Drupal\commerce_checkout\Plugin\Commerce\CheckoutFlow\CheckoutFlowBase implements CheckoutFlowInterface, ContainerFactoryPluginInterface uses AjaxFormTrait
- class \Drupal\Core\Plugin\PluginBase uses DependencySerializationTrait, MessengerTrait, StringTranslationTrait
Expanded class hierarchy of CheckoutFlowBase
File
- modules/
checkout/ src/ Plugin/ Commerce/ CheckoutFlow/ CheckoutFlowBase.php, line 28
Namespace
Drupal\commerce_checkout\Plugin\Commerce\CheckoutFlowView source
abstract class CheckoutFlowBase extends PluginBase implements CheckoutFlowInterface, ContainerFactoryPluginInterface {
use AjaxFormTrait;
/**
* The entity manager.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* The event dispatcher.
*
* @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
*/
protected $eventDispatcher;
/**
* The current order.
*
* @var \Drupal\commerce_order\Entity\OrderInterface
*/
protected $order;
/**
* The parent config entity.
*
* Not available while the plugin is being configured.
*
* @var \Drupal\commerce_checkout\Entity\CheckoutFlowInterface
*/
protected $parentEntity;
/**
* Static cache of visible steps.
*
* @var array
*/
protected $visibleSteps = [];
/**
* Constructs a new CheckoutFlowBase object.
*
* @param array $configuration
* A configuration array containing information about the plugin instance.
* @param string $plugin_id
* The plugin_id for the plugin instance.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager.
* @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher
* The event dispatcher.
* @param \Drupal\Core\Routing\RouteMatchInterface $route_match
* The route match.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager, EventDispatcherInterface $event_dispatcher, RouteMatchInterface $route_match) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->entityTypeManager = $entity_type_manager;
$this->eventDispatcher = $event_dispatcher;
$this->order = $route_match
->getParameter('commerce_order');
if (array_key_exists('_entity', $configuration)) {
$this->parentEntity = $configuration['_entity'];
unset($configuration['_entity']);
}
$this
->setConfiguration($configuration);
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static($configuration, $plugin_id, $plugin_definition, $container
->get('entity_type.manager'), $container
->get('event_dispatcher'), $container
->get('current_route_match'));
}
/**
* {@inheritdoc}
*/
public function __sleep() {
if (!empty($this->parentEntity)) {
$this->_parentEntityId = $this->parentEntity
->id();
unset($this->parentEntity);
}
if (!empty($this->order)) {
$this->_orderId = $this->order
->id();
unset($this->order);
}
return parent::__sleep();
}
/**
* {@inheritdoc}
*/
public function __wakeup() {
parent::__wakeup();
if (!empty($this->_parentEntityId)) {
$checkout_flow_storage = $this->entityTypeManager
->getStorage('commerce_checkout_flow');
$this->parentEntity = $checkout_flow_storage
->load($this->_parentEntityId);
unset($this->_parentEntityId);
}
if (!empty($this->_orderId)) {
$order_storage = $this->entityTypeManager
->getStorage('commerce_order');
$this->order = $order_storage
->load($this->_orderId);
unset($this->_orderId);
}
}
/**
* {@inheritdoc}
*/
public function getOrder() {
return $this->order;
}
/**
* {@inheritdoc}
*/
public function getPreviousStepId($step_id) {
$step_ids = array_keys($this
->getSteps());
$previous_step_index = array_search($step_id, $step_ids) - 1;
while (isset($step_ids[$previous_step_index])) {
if (!$this
->isStepVisible($step_ids[$previous_step_index])) {
$previous_step_index--;
continue;
}
return $step_ids[$previous_step_index];
}
return NULL;
}
/**
* {@inheritdoc}
*/
public function getNextStepId($step_id) {
$step_ids = array_keys($this
->getSteps());
$next_step_index = array_search($step_id, $step_ids) + 1;
while (isset($step_ids[$next_step_index])) {
if (!$this
->isStepVisible($step_ids[$next_step_index])) {
$next_step_index++;
continue;
}
return $step_ids[$next_step_index];
}
return NULL;
}
/**
* {@inheritdoc}
*/
public function redirectToStep($step_id) {
if (!$this
->isStepVisible($step_id)) {
throw new \InvalidArgumentException(sprintf('Invalid step ID "%s" passed to redirectToStep().', $step_id));
}
$this->order
->set('checkout_step', $step_id);
$this
->onStepChange($step_id);
$this->order
->save();
throw new NeedsRedirectException(Url::fromRoute('commerce_checkout.form', [
'commerce_order' => $this->order
->id(),
'step' => $step_id,
])
->toString());
}
/**
* {@inheritdoc}
*/
public function getSteps() {
// Each checkout flow plugin defines its own steps.
// These two steps are always expected to be present.
return [
'payment' => [
'label' => $this
->t('Payment'),
'next_label' => $this
->t('Pay and complete purchase'),
'has_sidebar' => FALSE,
'hidden' => TRUE,
],
'complete' => [
'label' => $this
->t('Complete'),
'next_label' => $this
->t('Complete checkout'),
'has_sidebar' => FALSE,
],
];
}
/**
* {@inheritdoc}
*/
public function getVisibleSteps() {
if (empty($this->visibleSteps)) {
$steps = $this
->getSteps();
foreach ($steps as $step_id => $step) {
if (!$this
->isStepVisible($step_id)) {
unset($steps[$step_id]);
}
}
$this->visibleSteps = $steps;
}
return $this->visibleSteps;
}
/**
* Gets whether the given step is visible.
*
* @param string $step_id
* The step ID.
*
* @return bool
* TRUE if the step is visible, FALSE otherwise.
*/
protected function isStepVisible($step_id) {
// All available steps are visible by default.
$step_ids = array_keys($this
->getSteps());
return in_array($step_id, $step_ids);
}
/**
* {@inheritdoc}
*/
public function calculateDependencies() {
return [];
}
/**
* {@inheritdoc}
*/
public function getConfiguration() {
return $this->configuration;
}
/**
* {@inheritdoc}
*/
public function setConfiguration(array $configuration) {
$this->configuration = NestedArray::mergeDeep($this
->defaultConfiguration(), $configuration);
}
/**
* {@inheritdoc}
*/
public function defaultConfiguration() {
return [
'display_checkout_progress' => TRUE,
'display_checkout_progress_breadcrumb_links' => FALSE,
];
}
/**
* {@inheritdoc}
*/
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
$form['display_checkout_progress'] = [
'#type' => 'checkbox',
'#title' => $this
->t('Display checkout progress'),
'#description' => $this
->t('Used by the checkout progress block to determine visibility.'),
'#default_value' => $this->configuration['display_checkout_progress'],
];
$form['display_checkout_progress_breadcrumb_links'] = [
'#type' => 'checkbox',
'#title' => $this
->t('Display checkout progress breadcrumb as links'),
'#description' => $this
->t('Let the checkout progress block render the breadcrumb as links.'),
'#default_value' => $this->configuration['display_checkout_progress_breadcrumb_links'],
];
return $form;
}
/**
* {@inheritdoc}
*/
public function validateConfigurationForm(array &$form, FormStateInterface $form_state) {
}
/**
* {@inheritdoc}
*/
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
if (!$form_state
->getErrors()) {
$values = $form_state
->getValue($form['#parents']);
$this->configuration = [];
$this->configuration['display_checkout_progress'] = $values['display_checkout_progress'];
$this->configuration['display_checkout_progress_breadcrumb_links'] = $values['display_checkout_progress_breadcrumb_links'];
}
}
/**
* {@inheritdoc}
*/
public function getBaseFormId() {
return 'commerce_checkout_flow';
}
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'commerce_checkout_flow_' . $this->pluginId;
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state, $step_id = NULL) {
// The $step_id argument is optional only because PHP disallows adding
// required arguments to an existing interface's method.
if (empty($step_id)) {
throw new \InvalidArgumentException('The $step_id cannot be empty.');
}
if ($form_state
->isRebuilding()) {
// Ensure a fresh order, in case an ajax submit has modified it.
$order_storage = $this->entityTypeManager
->getStorage('commerce_order');
$this->order = $order_storage
->load($this->order
->id());
}
$steps = $this
->getSteps();
$form['#tree'] = TRUE;
$form['#step_id'] = $step_id;
$form['#title'] = $steps[$step_id]['label'];
$form['#theme'] = [
'commerce_checkout_form',
];
$form['#attached']['library'][] = 'commerce_checkout/form';
// Workaround for core bug #2897377.
$form['#id'] = Html::getId($form_state
->getBuildInfo()['form_id']);
if ($this
->hasSidebar($step_id)) {
$form['sidebar']['order_summary'] = [
'#theme' => 'commerce_checkout_order_summary',
'#order_entity' => $this->order,
'#checkout_step' => $step_id,
];
}
$form['actions'] = $this
->actions($form, $form_state);
// Make sure the cache is removed if the parent entity or the order change.
CacheableMetadata::createFromRenderArray($form)
->addCacheableDependency($this->parentEntity)
->addCacheableDependency($this->order)
->applyTo($form);
return $form;
}
/**
* {@inheritdoc}
*/
public function validateForm(array &$form, FormStateInterface $form_state) {
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
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,
]);
}
$this->order
->save();
}
/**
* Reacts to the current step changing.
*
* Called before saving the order and redirecting.
*
* Handles the following logic
* 1) Locks the order before the payment page,
* 2) Unlocks the order when leaving the payment page
* 3) Places the order before the complete page.
*
* @param string $step_id
* The new step ID.
*/
protected function onStepChange($step_id) {
// Lock the order while on the 'payment' checkout step. Unlock elsewhere.
if ($step_id == 'payment') {
$this->order
->lock();
}
elseif ($step_id != 'payment') {
$this->order
->unlock();
}
// Place the order.
if ($step_id == 'complete' && $this->order
->getState()
->getId() == 'draft') {
// Notify other modules.
$event = new OrderEvent($this->order);
$this->eventDispatcher
->dispatch(CheckoutEvents::COMPLETION, $event);
$this->order
->getState()
->applyTransitionById('place');
}
}
/**
* Gets whether the given step has a sidebar.
*
* @param string $step_id
* The step ID.
*
* @return bool
* TRUE if the given step has a sidebar, FALSE otherwise.
*/
protected function hasSidebar($step_id) {
$steps = $this
->getSteps();
return !empty($steps[$step_id]['has_sidebar']);
}
/**
* Builds the actions element for the current form.
*
* @param array $form
* The current form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current form state.
*
* @return array
* The actions element.
*/
protected function actions(array $form, FormStateInterface $form_state) {
$steps = $this
->getSteps();
$next_step_id = $this
->getNextStepId($form['#step_id']);
$previous_step_id = $this
->getPreviousStepId($form['#step_id']);
$has_next_step = $next_step_id && isset($steps[$next_step_id]['next_label']);
$has_previous_step = $previous_step_id && isset($steps[$previous_step_id]['previous_label']);
$actions = [
'#type' => 'actions',
'#access' => $has_next_step,
];
if ($has_next_step) {
$actions['next'] = [
'#type' => 'submit',
'#value' => $steps[$next_step_id]['next_label'],
'#button_type' => 'primary',
'#submit' => [
'::submitForm',
],
];
if ($has_previous_step) {
$label = $steps[$previous_step_id]['previous_label'];
$options = [
'attributes' => [
'class' => [
'link--previous',
],
],
];
$actions['next']['#suffix'] = Link::createFromRoute($label, 'commerce_checkout.form', [
'commerce_order' => $this->order
->id(),
'step' => $previous_step_id,
], $options)
->toString();
}
}
return $actions;
}
}
Members
Name | Modifiers | Type | Description | Overrides |
---|---|---|---|---|
AjaxFormTrait:: |
public static | function | Ajax handler for refreshing an entire form. | |
CheckoutFlowBase:: |
protected | property | The entity manager. | |
CheckoutFlowBase:: |
protected | property | The event dispatcher. | |
CheckoutFlowBase:: |
protected | property | The current order. | |
CheckoutFlowBase:: |
protected | property | The parent config entity. | |
CheckoutFlowBase:: |
protected | property | Static cache of visible steps. | |
CheckoutFlowBase:: |
protected | function | Builds the actions element for the current form. | |
CheckoutFlowBase:: |
public | function |
Form constructor. Overrides PluginFormInterface:: |
1 |
CheckoutFlowBase:: |
public | function |
Form constructor. Overrides FormInterface:: |
1 |
CheckoutFlowBase:: |
public | function |
Calculates dependencies for the configured plugin. Overrides DependentPluginInterface:: |
1 |
CheckoutFlowBase:: |
public static | function |
Creates an instance of the plugin. Overrides ContainerFactoryPluginInterface:: |
1 |
CheckoutFlowBase:: |
public | function |
Gets default configuration for this plugin. Overrides ConfigurableInterface:: |
1 |
CheckoutFlowBase:: |
public | function |
Returns a string identifying the base form. Overrides BaseFormIdInterface:: |
|
CheckoutFlowBase:: |
public | function |
Gets this plugin's configuration. Overrides ConfigurableInterface:: |
|
CheckoutFlowBase:: |
public | function |
Returns a unique string identifying the form. Overrides FormInterface:: |
|
CheckoutFlowBase:: |
public | function |
Gets the next step ID for the given step ID. Overrides CheckoutFlowInterface:: |
|
CheckoutFlowBase:: |
public | function |
Gets the current order. Overrides CheckoutFlowInterface:: |
|
CheckoutFlowBase:: |
public | function |
Gets the previous step ID for the given step ID. Overrides CheckoutFlowInterface:: |
|
CheckoutFlowBase:: |
public | function |
Gets the defined steps. Overrides CheckoutFlowInterface:: |
1 |
CheckoutFlowBase:: |
public | function |
Gets the visible steps. Overrides CheckoutFlowInterface:: |
|
CheckoutFlowBase:: |
protected | function | Gets whether the given step has a sidebar. | |
CheckoutFlowBase:: |
protected | function | Gets whether the given step is visible. | 1 |
CheckoutFlowBase:: |
protected | function | Reacts to the current step changing. | |
CheckoutFlowBase:: |
public | function |
Redirects an order to a specific step in the checkout. Overrides CheckoutFlowInterface:: |
|
CheckoutFlowBase:: |
public | function |
Sets the configuration for this plugin instance. Overrides ConfigurableInterface:: |
|
CheckoutFlowBase:: |
public | function |
Form submission handler. Overrides PluginFormInterface:: |
1 |
CheckoutFlowBase:: |
public | function |
Form submission handler. Overrides FormInterface:: |
1 |
CheckoutFlowBase:: |
public | function |
Form validation handler. Overrides PluginFormInterface:: |
|
CheckoutFlowBase:: |
public | function |
Form validation handler. Overrides FormInterface:: |
1 |
CheckoutFlowBase:: |
public | function |
Constructs a new CheckoutFlowBase object. Overrides PluginBase:: |
1 |
CheckoutFlowBase:: |
public | function |
Overrides DependencySerializationTrait:: |
1 |
CheckoutFlowBase:: |
public | function |
Overrides DependencySerializationTrait:: |
|
DependencySerializationTrait:: |
protected | property | An array of entity type IDs keyed by the property name of their storages. | |
DependencySerializationTrait:: |
protected | property | An array of service IDs keyed by property name used for serialization. | |
MessengerTrait:: |
protected | property | The messenger. | 29 |
MessengerTrait:: |
public | function | Gets the messenger. | 29 |
MessengerTrait:: |
public | function | Sets the messenger. | |
PluginBase:: |
protected | property | Configuration information passed into the plugin. | 1 |
PluginBase:: |
protected | property | The plugin implementation definition. | 1 |
PluginBase:: |
protected | property | The plugin_id. | |
PluginBase:: |
constant | A string which is used to separate base plugin IDs from the derivative ID. | ||
PluginBase:: |
public | function |
Gets the base_plugin_id of the plugin instance. Overrides DerivativeInspectionInterface:: |
|
PluginBase:: |
public | function |
Gets the derivative_id of the plugin instance. Overrides DerivativeInspectionInterface:: |
|
PluginBase:: |
public | function |
Gets the definition of the plugin implementation. Overrides PluginInspectionInterface:: |
3 |
PluginBase:: |
public | function |
Gets the plugin_id of the plugin instance. Overrides PluginInspectionInterface:: |
|
PluginBase:: |
public | function | Determines if the plugin is configurable. | |
StringTranslationTrait:: |
protected | property | The string translation service. | 1 |
StringTranslationTrait:: |
protected | function | Formats a string containing a count of items. | |
StringTranslationTrait:: |
protected | function | Returns the number of plurals supported by a given language. | |
StringTranslationTrait:: |
protected | function | Gets the string translation service. | |
StringTranslationTrait:: |
public | function | Sets the string translation service to use. | 2 |
StringTranslationTrait:: |
protected | function | Translates a string to the current language or to a given language. |