You are here

class CheckoutController in Ubercart 8.4

Controller routines for the checkout.

Hierarchy

Expanded class hierarchy of CheckoutController

File

uc_cart/src/Controller/CheckoutController.php, line 21

Namespace

Drupal\uc_cart\Controller
View source
class CheckoutController extends ControllerBase implements ContainerInjectionInterface {

  /**
   * The checkout pane manager.
   *
   * @var \Drupal\uc_cart\Plugin\CheckoutPaneManager
   */
  protected $checkoutPaneManager;

  /**
   * The cart manager.
   *
   * @var \Drupal\uc_cart\CartManagerInterface
   */
  protected $cartManager;

  /**
   * The session.
   *
   * @var \Symfony\Component\HttpFoundation\Session\SessionInterface
   */
  protected $session;

  /**
   * The datetime.time service.
   *
   * @var \Drupal\Component\Datetime\TimeInterface
   */
  protected $dateTime;

  /**
   * Constructs a CheckoutController.
   *
   * @param \Drupal\uc_cart\Plugin\CheckoutPaneManager $checkout_pane_manager
   *   The checkout pane plugin manager.
   * @param \Drupal\uc_cart\CartManagerInterface $cart_manager
   *   The cart manager.
   * @param \Symfony\Component\HttpFoundation\Session\SessionInterface $session
   *   The session.
   * @param \Drupal\Component\Datetime\TimeInterface $date_time
   *   The datetime.time service.
   */
  public function __construct(CheckoutPaneManager $checkout_pane_manager, CartManagerInterface $cart_manager, SessionInterface $session, TimeInterface $date_time) {
    $this->checkoutPaneManager = $checkout_pane_manager;
    $this->cartManager = $cart_manager;
    $this->session = $session;
    $this->dateTime = $date_time;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    return new static($container
      ->get('plugin.manager.uc_cart.checkout_pane'), $container
      ->get('uc_cart.manager'), $container
      ->get('session'), $container
      ->get('datetime.time'));
  }

  /**
   * Builds the cart checkout page from available checkout pane plugins.
   */
  public function checkout() {
    $cart_config = $this
      ->config('uc_cart.settings');
    $items = $this->cartManager
      ->get()
      ->getContents();
    if (count($items) == 0 || !$cart_config
      ->get('checkout_enabled')) {
      return $this
        ->redirect('uc_cart.cart');
    }

    // Send anonymous users to login page when anonymous checkout is disabled.
    if ($this
      ->currentUser()
      ->isAnonymous() && !$cart_config
      ->get('checkout_anonymous')) {
      $this
        ->messenger()
        ->addMessage($this
        ->t('You must login before you can proceed to checkout.'));
      if ($this
        ->config('user.settings')
        ->get('register') != USER_REGISTER_ADMINISTRATORS_ONLY) {
        $this
          ->messenger()
          ->addMessage($this
          ->t('If you do not have an account yet, you should <a href=":url">register now</a>.', [
          ':url' => Url::fromRoute('user.register', [], [
            'query' => $this
              ->getDestinationArray(),
          ])
            ->toString(),
        ]));
      }
      return $this
        ->redirect('user.login', [], [
        'query' => $this
          ->getDestinationArray(),
      ]);
    }

    // Load an order from the session, if available.
    if ($this->session
      ->has('cart_order')) {
      $order = $this
        ->loadOrder();
      if ($order) {

        // To prevent identity theft, don't use an existing order if it has
        // changed status or owner, or if there has been no activity for 10
        // minutes.
        $request_time = $this->dateTime
          ->getRequestTime();
        if ($order
          ->getStateId() != 'in_checkout' || $this
          ->currentUser()
          ->isAuthenticated() && $this
          ->currentUser()
          ->id() != $order
          ->getOwnerId() || $order
          ->getChangedTime() < $request_time - CartInterface::CHECKOUT_TIMEOUT) {
          if ($order
            ->getStateId() == 'in_checkout' && $order
            ->getChangedTime() < $request_time - CartInterface::CHECKOUT_TIMEOUT) {

            // Mark expired orders as abandoned.
            $order
              ->setStatusId('abandoned')
              ->save();
          }
          unset($order);
        }
      }
      else {

        // Ghost session.
        $this->session
          ->remove('cart_order');
        $this
          ->messenger()
          ->addMessage($this
          ->t('Your session has expired or is no longer valid.  Please review your order and try again.'));
        return $this
          ->redirect('uc_cart.cart');
      }
    }

    // Determine if the form is being submitted or built for the first time.
    if (isset($_POST['form_id']) && $_POST['form_id'] == 'uc_cart_checkout_form') {

      // If this is a form submission, make sure the cart order is still valid.
      if (!isset($order)) {
        $this
          ->messenger()
          ->addMessage($this
          ->t('Your session has expired or is no longer valid.  Please review your order and try again.'));
        return $this
          ->redirect('uc_cart.cart');
      }
      elseif ($this->session
        ->has('uc_cart_order_rebuild')) {
        $this
          ->messenger()
          ->addMessage($this
          ->t('Your shopping cart contents have changed. Please review your order and try again.'));
        return $this
          ->redirect('uc_cart.cart');
      }
    }
    else {

      // Prepare the cart order.
      $rebuild = FALSE;
      if (!isset($order)) {

        // Create a new order if necessary.
        $order = Order::create([
          'uid' => $this
            ->currentUser()
            ->id(),
        ]);
        $order
          ->save();
        $this->session
          ->set('cart_order', $order
          ->id());
        $rebuild = TRUE;
      }
      elseif ($this->session
        ->has('uc_cart_order_rebuild')) {

        // Or, if the cart has changed, then remove old products and line items.
        $result = \Drupal::entityQuery('uc_order_product')
          ->condition('order_id', $order
          ->id())
          ->execute();
        if (!empty($result)) {
          $storage = $this
            ->entityTypeManager()
            ->getStorage('uc_order_product');
          $entities = $storage
            ->loadMultiple(array_keys($result));
          $storage
            ->delete($entities);
        }
        uc_order_delete_line_item($order
          ->id(), TRUE);
        $rebuild = TRUE;
      }
      if ($rebuild) {

        // Copy the cart contents to the cart order.
        $order->products = [];
        foreach ($items as $item) {
          $order->products[] = $item
            ->toOrderProduct();
        }
        $this->session
          ->remove('uc_cart_order_rebuild');
      }
      elseif (!uc_order_product_revive($order->products)) {
        $this
          ->messenger()
          ->addError($this
          ->t('Some of the products in this order are no longer available.'));
        return $this
          ->redirect('uc_cart.cart');
      }
    }
    $min = $cart_config
      ->get('minimum_subtotal');
    if ($min > 0 && $order
      ->getSubtotal() < $min) {
      $this
        ->messenger()
        ->addError($this
        ->t('The minimum order subtotal for checkout is @min.', [
        '@min' => uc_currency_format($min),
      ]));
      return $this
        ->redirect('uc_cart.cart');
    }

    // Invoke the customer starts checkout hook.
    $this
      ->moduleHandler()
      ->invokeAll('uc_cart_checkout_start', [
      $order,
    ]);

    // Trigger the checkout start event.

    /* rules_invoke_event('uc_cart_checkout_start', $order); */
    $event = new CheckoutStartEvent($order);
    \Drupal::service('event_dispatcher')
      ->dispatch($event::EVENT_NAME, $event);
    return $this
      ->formBuilder()
      ->getForm('Drupal\\uc_cart\\Form\\CheckoutForm', $order);
  }

  /**
   * Allows a customer to review their order before finally submitting it.
   */
  public function review() {
    if (!$this->session
      ->has('cart_order') || !$this->session
      ->has('uc_checkout_review_' . $this->session
      ->get('cart_order'))) {
      return $this
        ->redirect('uc_cart.checkout');
    }
    $order = $this
      ->loadOrder();
    if (!$order || $order
      ->getStateId() != 'in_checkout') {
      $this->session
        ->remove('uc_checkout_complete_' . $this->session
        ->get('cart_order'));
      return $this
        ->redirect('uc_cart.checkout');
    }
    elseif (!uc_order_product_revive($order->products)) {
      $this
        ->messenger()
        ->addError($this
        ->t('Some of the products in this order are no longer available.'));
      return $this
        ->redirect('uc_cart.cart');
    }
    $filter = [
      'enabled' => FALSE,
    ];

    // If the cart isn't shippable, bypass panes with shippable == TRUE.
    if (!$order
      ->isShippable() && $this
      ->config('uc_cart.settings')
      ->get('panes.delivery.settings.delivery_not_shippable')) {
      $filter['shippable'] = TRUE;
    }
    $panes = $this->checkoutPaneManager
      ->getPanes($filter);
    foreach ($panes as $pane) {
      $return = $pane
        ->review($order);
      if (!is_null($return)) {
        $data[$pane
          ->getTitle()] = $return;
      }
    }
    $build = [
      '#theme' => 'uc_cart_checkout_review',
      '#panes' => $data,
      '#form' => $this
        ->formBuilder()
        ->getForm('Drupal\\uc_cart\\Form\\CheckoutReviewForm', $order),
    ];
    $build['#attached']['library'][] = 'uc_cart/uc_cart.styles';
    $build['#attached']['library'][] = 'uc_cart/uc_cart.review.scripts';

    // Invoke the customer reviews order checkout hook.
    $this
      ->moduleHandler()
      ->invokeAll('uc_cart_checkout_review_order', [
      $order,
    ]);

    // Trigger the checkout review order event.

    /* rules_invoke_event('uc_cart_checkout_review_order', $order); */
    $event = new CheckoutReviewOrderEvent($order);
    \Drupal::service('event_dispatcher')
      ->dispatch($event::EVENT_NAME, $event);
    return $build;
  }

  /**
   * Completes the sale and finishes checkout.
   */
  public function complete() {
    if (!$this->session
      ->has('cart_order') || !$this->session
      ->has('uc_checkout_complete_' . $this->session
      ->get('cart_order'))) {
      return $this
        ->redirect('uc_cart.cart');
    }
    $order = $this
      ->loadOrder();
    if (empty($order)) {

      // If order was lost, display customer message and log the occurrence.
      $this
        ->messenger()
        ->addError($this
        ->t("We're sorry.  An error occurred while processing your order that prevents us from completing it at this time. Please contact us and we will resolve the issue as soon as possible."));
      $this
        ->getLogger('uc_cart')
        ->error('An empty order made it to checkout! Cart order ID: @cart_order', [
        '@cart_order' => $this->session
          ->get('cart_order'),
      ]);
      return $this
        ->redirect('uc_cart.cart');
    }
    $this->session
      ->remove('uc_checkout_complete_' . $this->session
      ->get('cart_order'));
    $this->session
      ->remove('cart_order');

    // Add a comment to let sales team know this came in through the site.
    uc_order_comment_save($order
      ->id(), 0, $this
      ->t('Order created through website.'), 'admin');
    return $this->cartManager
      ->completeSale($order);
  }

  /**
   * Loads the order that is being processed for checkout from the session.
   *
   * @return \Drupal\uc_order\OrderInterface
   *   The order object.
   */
  protected function loadOrder() {
    $id = $this->session
      ->get('cart_order');

    // Reset uc_order entity cache then load order.
    $storage = $this
      ->entityTypeManager()
      ->getStorage('uc_order');
    $storage
      ->resetCache([
      $id,
    ]);
    return $storage
      ->load($id);
  }

}

Members

Namesort descending Modifiers Type Description Overrides
CheckoutController::$cartManager protected property The cart manager.
CheckoutController::$checkoutPaneManager protected property The checkout pane manager.
CheckoutController::$dateTime protected property The datetime.time service.
CheckoutController::$session protected property The session.
CheckoutController::checkout public function Builds the cart checkout page from available checkout pane plugins.
CheckoutController::complete public function Completes the sale and finishes checkout.
CheckoutController::create public static function Instantiates a new instance of this class. Overrides ControllerBase::create
CheckoutController::loadOrder protected function Loads the order that is being processed for checkout from the session.
CheckoutController::review public function Allows a customer to review their order before finally submitting it.
CheckoutController::__construct public function Constructs a CheckoutController.
ControllerBase::$configFactory protected property The configuration factory.
ControllerBase::$currentUser protected property The current user service. 1
ControllerBase::$entityFormBuilder protected property The entity form builder.
ControllerBase::$entityManager protected property The entity manager.
ControllerBase::$entityTypeManager protected property The entity type manager.
ControllerBase::$formBuilder protected property The form builder. 2
ControllerBase::$keyValue protected property The key-value storage. 1
ControllerBase::$languageManager protected property The language manager. 1
ControllerBase::$moduleHandler protected property The module handler. 2
ControllerBase::$stateService protected property The state service.
ControllerBase::cache protected function Returns the requested cache bin.
ControllerBase::config protected function Retrieves a configuration object.
ControllerBase::container private function Returns the service container.
ControllerBase::currentUser protected function Returns the current user. 1
ControllerBase::entityFormBuilder protected function Retrieves the entity form builder.
ControllerBase::entityManager Deprecated protected function Retrieves the entity manager service.
ControllerBase::entityTypeManager protected function Retrieves the entity type manager.
ControllerBase::formBuilder protected function Returns the form builder service. 2
ControllerBase::keyValue protected function Returns a key/value storage collection. 1
ControllerBase::languageManager protected function Returns the language manager service. 1
ControllerBase::moduleHandler protected function Returns the module handler. 2
ControllerBase::redirect protected function Returns a redirect response object for the specified route. Overrides UrlGeneratorTrait::redirect
ControllerBase::state protected function Returns the state storage service.
LinkGeneratorTrait::$linkGenerator protected property The link generator. 1
LinkGeneratorTrait::getLinkGenerator Deprecated protected function Returns the link generator.
LinkGeneratorTrait::l Deprecated protected function Renders a link to a route given a route name and its parameters.
LinkGeneratorTrait::setLinkGenerator Deprecated public function Sets the link generator service.
LoggerChannelTrait::$loggerFactory protected property The logger channel factory service.
LoggerChannelTrait::getLogger protected function Gets the logger for a specific channel.
LoggerChannelTrait::setLoggerFactory public function Injects the logger channel factory.
MessengerTrait::$messenger protected property The messenger. 29
MessengerTrait::messenger public function Gets the messenger. 29
MessengerTrait::setMessenger public function Sets the messenger.
RedirectDestinationTrait::$redirectDestination protected property The redirect destination service. 1
RedirectDestinationTrait::getDestinationArray protected function Prepares a 'destination' URL query parameter for use with \Drupal\Core\Url.
RedirectDestinationTrait::getRedirectDestination protected function Returns the redirect destination service.
RedirectDestinationTrait::setRedirectDestination public function Sets the redirect destination service.
StringTranslationTrait::$stringTranslation protected property The string translation service. 1
StringTranslationTrait::formatPlural protected function Formats a string containing a count of items.
StringTranslationTrait::getNumberOfPlurals protected function Returns the number of plurals supported by a given language.
StringTranslationTrait::getStringTranslation protected function Gets the string translation service.
StringTranslationTrait::setStringTranslation public function Sets the string translation service to use. 2
StringTranslationTrait::t protected function Translates a string to the current language or to a given language.
UrlGeneratorTrait::$urlGenerator protected property The url generator.
UrlGeneratorTrait::getUrlGenerator Deprecated protected function Returns the URL generator service.
UrlGeneratorTrait::setUrlGenerator Deprecated public function Sets the URL generator service.
UrlGeneratorTrait::url Deprecated protected function Generates a URL or path for a specific route based on the given parameters.