Advertisement
Guest User

Untitled

a guest
Jan 24th, 2020
194
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 12.45 KB | None | 0 0
  1. <?php
  2.  
  3. namespace Drupal\commerce_checkout\Plugin\Commerce\CheckoutFlow;
  4.  
  5. use Drupal\commerce\AjaxFormTrait;
  6. use Drupal\commerce\Response\NeedsRedirectException;
  7. use Drupal\Component\Utility\Html;
  8. use Drupal\Component\Utility\NestedArray;
  9. use Drupal\Core\Cache\CacheableMetadata;
  10. use Drupal\Core\Entity\EntityTypeManagerInterface;
  11. use Drupal\Core\Form\FormStateInterface;
  12. use Drupal\Core\Link;
  13. use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
  14. use Drupal\Core\Plugin\PluginBase;
  15. use Drupal\Core\Routing\RouteMatchInterface;
  16. use Drupal\Core\Url;
  17. use Symfony\Component\DependencyInjection\ContainerInterface;
  18. use Symfony\Component\EventDispatcher\EventDispatcherInterface;
  19.  
  20. /**
  21. * Provides the base checkout flow class.
  22. *
  23. * Checkout flows should extend this class only if they don't want to use
  24. * checkout panes. Otherwise they should extend CheckoutFlowWithPanesBase.
  25. */
  26. abstract class CheckoutFlowBase extends PluginBase implements CheckoutFlowInterface, ContainerFactoryPluginInterface {
  27.  
  28. use AjaxFormTrait;
  29.  
  30. /**
  31. * The entity manager.
  32. *
  33. * @var \Drupal\Core\Entity\EntityTypeManagerInterface
  34. */
  35. protected $entityTypeManager;
  36.  
  37. /**
  38. * The event dispatcher.
  39. *
  40. * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
  41. */
  42. protected $eventDispatcher;
  43.  
  44. /**
  45. * The current order.
  46. *
  47. * @var \Drupal\commerce_order\Entity\OrderInterface
  48. */
  49. protected $order;
  50.  
  51. /**
  52. * The parent config entity.
  53. *
  54. * Not available while the plugin is being configured.
  55. *
  56. * @var \Drupal\commerce_checkout\Entity\CheckoutFlowInterface
  57. */
  58. protected $parentEntity;
  59.  
  60. /**
  61. * Constructs a new CheckoutFlowBase object.
  62. *
  63. * @param array $configuration
  64. * A configuration array containing information about the plugin instance.
  65. * @param string $plugin_id
  66. * The plugin_id for the plugin instance.
  67. * @param mixed $plugin_definition
  68. * The plugin implementation definition.
  69. * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
  70. * The entity type manager.
  71. * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher
  72. * The event dispatcher.
  73. * @param \Drupal\Core\Routing\RouteMatchInterface $route_match
  74. * The route match.
  75. */
  76. public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager, EventDispatcherInterface $event_dispatcher, RouteMatchInterface $route_match) {
  77. parent::__construct($configuration, $plugin_id, $plugin_definition);
  78.  
  79. $this->entityTypeManager = $entity_type_manager;
  80. $this->eventDispatcher = $event_dispatcher;
  81. $this->order = $route_match->getParameter('commerce_order');
  82. if (array_key_exists('_entity', $configuration)) {
  83. $this->parentEntity = $configuration['_entity'];
  84. unset($configuration['_entity']);
  85. }
  86. $this->setConfiguration($configuration);
  87. }
  88.  
  89. /**
  90. * {@inheritdoc}
  91. */
  92. public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
  93. return new static(
  94. $configuration,
  95. $plugin_id,
  96. $plugin_definition,
  97. $container->get('entity_type.manager'),
  98. $container->get('event_dispatcher'),
  99. $container->get('current_route_match')
  100. );
  101. }
  102.  
  103. /**
  104. * {@inheritdoc}
  105. */
  106. public function __sleep() {
  107. if (!empty($this->parentEntity)) {
  108. $this->_parentEntityId = $this->parentEntity->id();
  109. unset($this->parentEntity);
  110. }
  111.  
  112. return parent::__sleep();
  113. }
  114.  
  115. /**
  116. * {@inheritdoc}
  117. */
  118. public function __wakeup() {
  119. parent::__wakeup();
  120.  
  121. if (!empty($this->_parentEntityId)) {
  122. $checkout_flow_storage = $this->entityTypeManager->getStorage('commerce_checkout_flow');
  123. $this->parentEntity = $checkout_flow_storage->load($this->_parentEntityId);
  124. unset($this->_parentEntityId);
  125. }
  126. }
  127.  
  128. /**
  129. * {@inheritdoc}
  130. */
  131. public function getOrder() {
  132. return $this->order;
  133. }
  134.  
  135. /**
  136. * {@inheritdoc}
  137. */
  138. public function getPreviousStepId($step_id) {
  139. $step_ids = array_keys($this->getVisibleSteps());
  140. $current_index = array_search($step_id, $step_ids);
  141. return isset($step_ids[$current_index - 1]) ? $step_ids[$current_index - 1] : NULL;
  142. }
  143.  
  144. /**
  145. * {@inheritdoc}
  146. */
  147. public function getNextStepId($step_id) {
  148. $step_ids = array_keys($this->getVisibleSteps());
  149. $current_index = array_search($step_id, $step_ids);
  150. return isset($step_ids[$current_index + 1]) ? $step_ids[$current_index + 1] : NULL;
  151. }
  152.  
  153. /**
  154. * {@inheritdoc}
  155. */
  156. public function redirectToStep($step_id) {
  157. $available_step_ids = array_keys($this->getVisibleSteps());
  158. if (!in_array($step_id, $available_step_ids)) {
  159. throw new \InvalidArgumentException(sprintf('Invalid step ID "%s" passed to redirectToStep().', $step_id));
  160. }
  161.  
  162. $this->order->set('checkout_step', $step_id);
  163. $this->onStepChange($step_id);
  164. $this->order->save();
  165.  
  166. throw new NeedsRedirectException(Url::fromRoute('commerce_checkout.form', [
  167. 'commerce_order' => $this->order->id(),
  168. 'step' => $step_id,
  169. ])->toString());
  170. }
  171.  
  172. /**
  173. * {@inheritdoc}
  174. */
  175. public function getSteps() {
  176. // Each checkout flow plugin defines its own steps.
  177. // These two steps are always expected to be present.
  178. return [
  179. 'payment' => [
  180. 'label' => $this->t('Payment'),
  181. 'next_label' => $this->t('Pay and complete purchase'),
  182. 'has_sidebar' => FALSE,
  183. 'hidden' => TRUE,
  184. ],
  185. 'complete' => [
  186. 'label' => $this->t('Complete'),
  187. 'next_label' => $this->t('Complete checkout'),
  188. 'has_sidebar' => FALSE,
  189. ],
  190. ];
  191. }
  192.  
  193. /**
  194. * {@inheritdoc}
  195. */
  196. public function getVisibleSteps() {
  197. // All steps are visible by default.
  198. return $this->getSteps();
  199. }
  200.  
  201. /**
  202. * {@inheritdoc}
  203. */
  204. public function calculateDependencies() {
  205. return [];
  206. }
  207.  
  208. /**
  209. * {@inheritdoc}
  210. */
  211. public function getConfiguration() {
  212. return $this->configuration;
  213. }
  214.  
  215. /**
  216. * {@inheritdoc}
  217. */
  218. public function setConfiguration(array $configuration) {
  219. $this->configuration = NestedArray::mergeDeep($this->defaultConfiguration(), $configuration);
  220. }
  221.  
  222. /**
  223. * {@inheritdoc}
  224. */
  225. public function defaultConfiguration() {
  226. return [
  227. 'display_checkout_progress' => TRUE,
  228. ];
  229. }
  230.  
  231. /**
  232. * {@inheritdoc}
  233. */
  234. public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
  235. $form['display_checkout_progress'] = [
  236. '#type' => 'checkbox',
  237. '#title' => $this->t('Display checkout progress'),
  238. '#description' => $this->t('Used by the checkout progress block to determine visibility.'),
  239. '#default_value' => $this->configuration['display_checkout_progress'],
  240. ];
  241.  
  242. return $form;
  243. }
  244.  
  245. /**
  246. * {@inheritdoc}
  247. */
  248. public function validateConfigurationForm(array &$form, FormStateInterface $form_state) {}
  249.  
  250. /**
  251. * {@inheritdoc}
  252. */
  253. public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
  254. if (!$form_state->getErrors()) {
  255. $values = $form_state->getValue($form['#parents']);
  256. $this->configuration = [];
  257. $this->configuration['display_checkout_progress'] = $values['display_checkout_progress'];
  258. }
  259. }
  260.  
  261. /**
  262. * {@inheritdoc}
  263. */
  264. public function getBaseFormId() {
  265. return 'commerce_checkout_flow';
  266. }
  267.  
  268. /**
  269. * {@inheritdoc}
  270. */
  271. public function getFormId() {
  272. return 'commerce_checkout_flow_' . $this->pluginId;
  273. }
  274.  
  275. /**
  276. * {@inheritdoc}
  277. */
  278. public function buildForm(array $form, FormStateInterface $form_state, $step_id = NULL) {
  279. // The $step_id argument is optional only because PHP disallows adding
  280. // required arguments to an existing interface's method.
  281. if (empty($step_id)) {
  282. throw new \InvalidArgumentException('The $step_id cannot be empty.');
  283. }
  284. if ($form_state->isRebuilding()) {
  285. // Ensure a fresh order, in case an ajax submit has modified it.
  286. $order_storage = $this->entityTypeManager->getStorage('commerce_order');
  287. $this->order = $order_storage->load($this->order->id());
  288. }
  289.  
  290. $steps = $this->getVisibleSteps();
  291. $form['#tree'] = TRUE;
  292. $form['#step_id'] = $step_id;
  293. $form['#title'] = $steps[$step_id]['label'];
  294. $form['#theme'] = ['commerce_checkout_form'];
  295. $form['#attached']['library'][] = 'commerce_checkout/form';
  296. // Workaround for core bug #2897377.
  297. $form['#id'] = Html::getId($form_state->getBuildInfo()['form_id']);
  298. if ($this->hasSidebar($step_id)) {
  299. $form['sidebar']['order_summary'] = [
  300. '#theme' => 'commerce_checkout_order_summary',
  301. '#order_entity' => $this->order,
  302. '#checkout_step' => $step_id,
  303. ];
  304. }
  305. $form['actions'] = $this->actions($form, $form_state);
  306.  
  307. // Make sure the cache is removed if the parent entity or the order change.
  308. CacheableMetadata::createFromRenderArray($form)
  309. ->addCacheableDependency($this->parentEntity)
  310. ->addCacheableDependency($this->order)
  311. ->applyTo($form);
  312.  
  313. return $form;
  314. }
  315.  
  316. /**
  317. * {@inheritdoc}
  318. */
  319. public function validateForm(array &$form, FormStateInterface $form_state) {}
  320.  
  321. /**
  322. * {@inheritdoc}
  323. */
  324. public function submitForm(array &$form, FormStateInterface $form_state) {
  325. if ($next_step_id = $this->getNextStepId($form['#step_id'])) {
  326. $this->order->set('checkout_step', $next_step_id);
  327. $this->onStepChange($next_step_id);
  328.  
  329. $form_state->setRedirect('commerce_checkout.form', [
  330. 'commerce_order' => $this->order->id(),
  331. 'step' => $next_step_id,
  332. ]);
  333. }
  334.  
  335. $this->order->save();
  336. }
  337.  
  338. /**
  339. * Reacts to the current step changing.
  340. *
  341. * Called before saving the order and redirecting.
  342. *
  343. * Handles the following logic
  344. * 1) Locks the order before the payment page,
  345. * 2) Unlocks the order when leaving the payment page
  346. * 3) Places the order before the complete page.
  347. *
  348. * @param string $step_id
  349. * The new step ID.
  350. */
  351. protected function onStepChange($step_id) {
  352. // Lock the order while on the 'payment' checkout step. Unlock elsewhere.
  353. if ($step_id == 'payment') {
  354. $this->order->lock();
  355. }
  356. elseif ($step_id != 'payment') {
  357. $this->order->unlock();
  358. }
  359. // Place the order.
  360. if ($step_id == 'complete' && $this->order->getState()->getId() == 'draft') {
  361. $this->order->getState()->applyTransitionById('place');
  362. }
  363. }
  364.  
  365. /**
  366. * Gets whether the given step has a sidebar.
  367. *
  368. * @param string $step_id
  369. * The step ID.
  370. *
  371. * @return bool
  372. * TRUE if the given step has a sidebar, FALSE otherwise.
  373. */
  374. protected function hasSidebar($step_id) {
  375. $steps = $this->getVisibleSteps();
  376. return !empty($steps[$step_id]['has_sidebar']);
  377. }
  378.  
  379. /**
  380. * Builds the actions element for the current form.
  381. *
  382. * @param array $form
  383. * The current form.
  384. * @param \Drupal\Core\Form\FormStateInterface $form_state
  385. * The current form state.
  386. *
  387. * @return array
  388. * The actions element.
  389. */
  390. protected function actions(array $form, FormStateInterface $form_state) {
  391. $steps = $this->getVisibleSteps();
  392. $next_step_id = $this->getNextStepId($form['#step_id']);
  393. $previous_step_id = $this->getPreviousStepId($form['#step_id']);
  394. $has_next_step = $next_step_id && isset($steps[$next_step_id]['next_label']);
  395. $has_previous_step = $previous_step_id && isset($steps[$previous_step_id]['previous_label']);
  396.  
  397. $actions = [
  398. '#type' => 'actions',
  399. '#access' => $has_next_step,
  400. ];
  401. if ($has_next_step) {
  402. $actions['next'] = [
  403. '#type' => 'submit',
  404. '#value' => $steps[$next_step_id]['next_label'],
  405. '#button_type' => 'primary',
  406. '#submit' => ['::submitForm'],
  407. ];
  408. if ($has_previous_step) {
  409. $label = $steps[$previous_step_id]['previous_label'];
  410. $options = [
  411. 'attributes' => [
  412. 'class' => ['link--previous'],
  413. ],
  414. ];
  415. $actions['next']['#suffix'] = Link::createFromRoute($label, 'commerce_checkout.form', [
  416. 'commerce_order' => $this->order->id(),
  417. 'step' => $previous_step_id,
  418. ], $options)->toString();
  419. }
  420. }
  421.  
  422. return $actions;
  423. }
  424.  
  425. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement