arch-8.x-1.x-dev/modules/checkout/src/Controller/CheckoutController.php
modules/checkout/src/Controller/CheckoutController.php
<?php
namespace Drupal\arch_checkout\Controller;
use Drupal\arch_cart\Cart\CartHandlerInterface;
use Drupal\arch_checkout\CheckoutType\CheckoutTypeManagerInterface;
use Drupal\arch_checkout\CheckoutType\CheckoutTypePluginInterface;
use Drupal\arch_checkout\CheckoutType\Exception\CheckoutTypeException;
use Drupal\arch_checkout\Services\CheckoutCompletePageAccess;
use Drupal\arch_order\Entity\OrderInterface;
use Drupal\arch_order\Services\OrderStatusService;
use Drupal\arch_payment\PaymentMethodManagerInterface;
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\PageCache\ResponsePolicy\KillSwitch;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Theme\ThemeManagerInterface;
use Drupal\Core\Url;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
/**
* Checkout page controller.
*
* @package Drupal\arch_checkout\Controller
*/
class CheckoutController extends ControllerBase implements ContainerInjectionInterface {
/**
* Checkout Type manager.
*
* @var \Drupal\arch_checkout\CheckoutType\CheckoutTypeManagerInterface
*/
protected $checkoutTypeManager;
/**
* Payment method manager.
*
* @var \Drupal\arch_payment\PaymentMethodManagerInterface
*/
protected $paymentMethodManager;
/**
* The page cache kill switch service.
*
* @var \Drupal\Core\PageCache\ResponsePolicy\KillSwitch
*/
protected $pageCacheKillSwitch;
/**
* Module handler.
*
* @var \Drupal\Core\Extension\ModuleHandlerInterface
*/
protected $moduleHandler;
/**
* Theme manager.
*
* @var \Drupal\Core\Theme\ThemeManagerInterface
*/
protected $themeManager;
/**
* Cart.
*
* @var \Drupal\arch_cart\Cart\CartInterface
*/
protected $cart;
/**
* Order status service.
*
* @var \Drupal\arch_order\Services\OrderStatusService
*/
protected $orderStatus;
/**
* Checkout complete page access checker service.
*
* @var \Drupal\arch_checkout\Services\CheckoutCompletePageAccess
*/
protected $checkoutCompletePageAccess;
/**
* CheckoutController constructor.
*
* @param \Drupal\arch_cart\Cart\CartHandlerInterface $cart_handler
* Cart handler.
* @param \Drupal\arch_checkout\CheckoutType\CheckoutTypeManagerInterface $checkout_type_manager
* The checkout type manager.
* @param \Drupal\arch_payment\PaymentMethodManagerInterface $payment_method_manager
* Payment manager.
* @param \Drupal\Core\PageCache\ResponsePolicy\KillSwitch $kill_switch
* Page cache kill switch service.
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* Module handler.
* @param \Drupal\Core\Theme\ThemeManagerInterface $theme_manager
* Theme manager.
* @param \Drupal\arch_order\Services\OrderStatusService $order_status
* Order status service.
* @param \Drupal\arch_checkout\Services\CheckoutCompletePageAccess $checkoutCompletePageAccess
* Checkout complete page access checker service.
*/
public function __construct(
CartHandlerInterface $cart_handler,
CheckoutTypeManagerInterface $checkout_type_manager,
PaymentMethodManagerInterface $payment_method_manager,
KillSwitch $kill_switch,
ModuleHandlerInterface $module_handler,
ThemeManagerInterface $theme_manager,
OrderStatusService $order_status,
CheckoutCompletePageAccess $checkoutCompletePageAccess,
) {
$this->cart = $cart_handler->getCart();
$this->checkoutTypeManager = $checkout_type_manager;
$this->paymentMethodManager = $payment_method_manager;
$this->pageCacheKillSwitch = $kill_switch;
$this->moduleHandler = $module_handler;
$this->themeManager = $theme_manager;
$this->orderStatus = $order_status;
$this->checkoutCompletePageAccess = $checkoutCompletePageAccess;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('arch_cart_handler'),
$container->get('plugin.manager.checkout_type'),
$container->get('plugin.manager.payment_method'),
$container->get('page_cache_kill_switch'),
$container->get('module_handler'),
$container->get('theme.manager'),
$container->get('order.statuses'),
$container->get('arch_checkout.checkout_complete_page.access')
);
}
/**
* Access callback.
*
* @param \Drupal\Core\Session\AccountInterface $account
* Run access checks for this account.
*
* @return \Drupal\Core\Access\AccessResult
* Result.
*/
public function checkoutAccess(AccountInterface $account) {
if (
$account->isAnonymous()
&& !$this->checkoutTypeManager->isAnonymousCheckoutAllowed()
) {
$reason = $this->t(
'Please <a href="@register_url">create an account</a> or <a href="@login_url">sign in</a> to checkout!',
[
'@register_url' => Url::fromRoute('user.register')->toString(),
'@login_url' => Url::fromRoute('user.login')->toString(),
],
['context' => 'arch_checkout']
);
$this->messenger()->addError($reason);
return AccessResult::forbidden((string) $reason);
}
return AccessResult::allowedIfHasPermission($account, 'access content');
}
/**
* Checkout page handler.
*
* @return array|\Symfony\Component\HttpFoundation\RedirectResponse
* Redirect response or render array.
*
* @throws \Drupal\Component\Plugin\Exception\PluginException
*/
public function checkout() {
if (
!$this->cart->hasProduct()
&& $this->checkoutTypeManager->shouldRedirectIfCartEmpty()
) {
return $this->redirect('arch_cart.content');
}
try {
$default_plugin = $this->checkoutTypeManager->getDefaultCheckoutType();
// Let other modules alterate the selected plugin for checkout page.
$this->moduleHandler->alter('checkout_plugin', $default_plugin, $this->checkoutTypeManager);
}
catch (CheckoutTypeException $ce) {
$this->getLogger('Checkout')->error($ce->getMessage());
$this->messenger()->addError('Sorry, something went wrong during processing your request. Please contact site admin if it is persist.');
return $this->redirect('<front>');
}
if (empty($default_plugin)) {
$this->getLogger('Checkout')->error('No default checkout type is set.');
$this->messenger()->addError('Sorry, something went wrong during processing your request. Please contact site admin if it is persist.');
return $this->redirect('<front>');
}
/** @var \Drupal\arch_checkout\CheckoutType\CheckoutTypePluginInterface $plugin */
$plugin = $this->checkoutTypeManager->createInstance($default_plugin['id'], []);
if (!($plugin instanceof CheckoutTypePluginInterface)) {
$this->getLogger('Checkout')->error('The default checkout type which is set is not implements the required interface.');
$this->messenger()->addError('Sorry, something went wrong during processing your request. Please contact site admin if it is persist.');
return $this->redirect('<front>');
}
$form = $plugin->build();
$form['#cache']['contexts'][] = 'user';
$form['#cache']['contexts'][] = 'session';
$form['#cache']['max-age'] = 0;
// Let modules alterate the output.
$this->moduleHandler->alter('checkout_page', $form, $plugin);
$this->themeManager->alter('checkout_page', $form, $plugin);
return $form;
}
/**
* Page title callback for complete page.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* Request object.
*
* @return \Drupal\Core\StringTranslation\TranslatableMarkup
* Page title.
*/
public function completeTitle(Request $request) {
/** @var \Drupal\arch_order\Entity\Order $order */
$order = $request->get('order_id');
$title = $this->t('Thank you for your purchase!', [], ['context' => 'arch_checkout']);
// Let modules alterate the title.
$this->moduleHandler->alter('checkout_complete_page_title', $title, $order);
return $title;
}
/**
* Checkout complete action.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* Request object.
*
* @return array|\Symfony\Component\HttpFoundation\RedirectResponse
* Complete page content.
*
* @throws \Drupal\Component\Plugin\Exception\PluginException
* @throws \Drupal\Core\Entity\EntityStorageException
*/
public function complete(Request $request) {
/** @var \Drupal\arch_order\Entity\Order $order */
$order = $request->get('order_id');
if ($order->getOwner()->getEmail() !== $this->currentUser()->getEmail()) {
if ($this->currentUser()->isAuthenticated()) {
throw new AccessDeniedHttpException();
}
elseif (!$this->checkoutTypeManager->isAnonymousCheckoutAllowed()) {
throw new AccessDeniedHttpException();
}
}
if (!$this->checkoutCompletePageAccess->canAccess($order)) {
return $this->redirect('<front>');
}
// Make sure we will not see a cached checkout complete page.
$this->pageCacheKillSwitch->trigger();
$payment_method = NULL;
$payment_method_id = $order->get('payment_method')->getString();
if (!empty($payment_method_id)) {
/** @var \Drupal\arch_payment\PaymentMethodInterface $payment_method */
$payment_method = $this->paymentMethodManager->createInstance($payment_method_id);
}
// Make it possible to Payment Methods alterate the output.
$checkout_complete_info = NULL;
if ($payment_method instanceof CheckoutCompleteInterface) {
$checkout_complete_info = $payment_method->checkoutCompleteInfo($order);
}
$output = [
'#theme' => 'arch_checkout_complete',
'#order' => $order,
'#message' => $this->t('We have received your order, and we are getting it ready.', [], ['context' => 'arch_checkout']),
'#message_extra' => $this->t('You will receive a confirmation e-mail shortly.', [], ['context' => 'arch_checkout']),
'#checkout_complete_info' => $checkout_complete_info,
];
// Let modules alterate the output.
$this->moduleHandler->alter('checkout_complete_page', $output, $order);
$this->themeManager->alter('checkout_complete_page', $output, $order);
/** @var \Drupal\arch_order\Entity\OrderStatusInterface[] $orderStatuses */
$orderStatuses = $this->orderStatus->getOrderStatuses();
$new_status = 'completed';
$this->moduleHandler->alter('checkout_complete_order_status', $new_status, $order);
if (!in_array($new_status, array_keys($orderStatuses))) {
$new_status = 'completed';
}
if (
$order->get('status')->getString() != 'completed'
&& $this->shouldChangeOrderStatusToCompleted($order)
) {
$order->setNewRevision();
$order->setRevisionUserId($this->currentUser()->id());
$order->setRevisionLogMessage('Order status has changed from "' . $order->get('status')->getString() . '" to "' . $new_status . '".');
$order->setRevisionCreationTime(time());
// Complete order.
$order->set('status', $new_status);
// Update order.
$order->save();
$this->moduleHandler->invokeAll('checkout_completed', [$order]);
$this->moduleHandler->alter('checkout_completed_page', $output, $order);
$this->themeManager->alter('checkout_completed_page', $output, $order);
}
// Reset cart.
$this->cart->resetStore();
return $output;
}
/**
* Check we should update order status to "complete".
*
* @param \Drupal\arch_order\Entity\OrderInterface $order
* Order to change.
*
* @return bool
* Return TRUE of order status change is not disallowed.
*/
protected function shouldChangeOrderStatusToCompleted(OrderInterface $order) {
$result = $this->moduleHandler->invokeAll('checkout_complete_page_should_update_order_status', [
$order,
]);
return !in_array(FALSE, $result, TRUE);
}
}
