acquia_commercemanager-8.x-1.122/modules/acm_cart/src/CartStorage.php
modules/acm_cart/src/CartStorage.php
<?php namespace Drupal\acm_cart; use Drupal\acm_cart\Event\CartAddressEvent; use Drupal\acm_cart\Event\CartCouponEvent; use Drupal\acm_cart\Event\CartEvent; use Drupal\acm_cart\Event\CartExtensionEvent; use Drupal\acm_cart\Event\CartItemEvent; use Drupal\acm_cart\Event\CartPushEvent; use Drupal\acm_cart\Event\CartRawItemsEvent; use Drupal\acm_cart\Event\Events; use Drupal\acm\Connector\APIWrapperInterface; use Drupal\acm\SessionStoreInterface; use Drupal\acm_sku\Entity\SKU; use Drupal\Core\Logger\LoggerChannelFactoryInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * Class CartStorage. * * @package Drupal\acm_cart */ class CartStorage implements CartInterface, CartStorageInterface { /** * The session storage. * * @var \Drupal\acm\SessionStoreInterface */ protected $storage; /** * API Wrapper object. * * @var \Drupal\acm\Connector\APIWrapperInterface */ protected $apiWrapper; /** * The event dispatcher. * * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface */ protected $eventDispatcher; /** * The logger. * * @var \Psr\Log\LoggerInterface */ protected $logger; /** * The cart. * * @var \Drupal\acm_cart\CartInterface */ protected $cart; /** * Constructor. * * @param \Drupal\acm\SessionStoreInterface $storage * The session storage. * @param \Drupal\acm\Connector\APIWrapperInterface $api_wrapper * ApiWrapper object. * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher * The event dispatcher. * @param \Drupal\Core\Logger\LoggerChannelFactoryInterface $logger_factory * LoggerFactory object. */ public function __construct(SessionStoreInterface $storage, APIWrapperInterface $api_wrapper, EventDispatcherInterface $event_dispatcher, LoggerChannelFactoryInterface $logger_factory) { $this->storage = $storage; $this->apiWrapper = $api_wrapper; $this->eventDispatcher = $event_dispatcher; $this->logger = $logger_factory->get('acm_cart'); // Load the intial cart. $cart = $this->storage->get(self::STORAGE_KEY); if (!empty($cart) && $cart instanceof CartInterface) { $this->cart = $cart; } } /** * {@inheritdoc} */ public function getCartId($create_new = TRUE) { $cookies = $this->getCookies(); $cart_id = NULL; if (isset($cookies['Drupal_visitor_acm_cart_id'])) { return $cookies['Drupal_visitor_acm_cart_id']; } if ($this->cart) { return $this->cart->id(); } elseif ($create_new) { $cart = $this->createCart(); return $cart->id(); } else { return NULL; } } /** * {@inheritdoc} */ public function storeCart(CartInterface $cart) { $cart_event = $this->eventDispatcher->dispatch(Events::STORE_CART, new CartEvent($cart)); $cart = $cart_event->getCart(); // Store cart in memory. $this->cart = $cart; // Store cart. $this->storage->set(self::STORAGE_KEY, $cart); // Update cookies cache in Drupal to use new one. $this->setCookie('acm_cart_id', $cart->id()); $this->setCookie('acm_cart_count', $this->cart->getCartItemsCount()); } /** * {@inheritdoc} */ public function restoreCart($cart_id) { try { // @TODO: Need to rethink about this and get it done in single API call. $cart = (object) $this->apiWrapper->getCart($cart_id, $this->getCustomerId()); if ($cart) { $cart->cart_id = $cart_id; $cart = new Cart($cart); $this->storeCart($cart); } } catch (\Exception $e) { $this->logger->warning('Error occurred while restoring cart id %cart_id: %message', [ '%cart_id' => $cart_id, '%message' => $e->getMessage(), ]); $this->clearCart(); } } /** * {@inheritdoc} */ public function convertGuestCart($customer_id) { if (empty($customer_id)) { return; } $restored = FALSE; // Try to restore an existing cart for this user, but only if the current // guest cart is empty. if ($this->isEmpty()) { try { $cart = (object) $this->apiWrapper->getCart('current', $customer_id); if ($cart) { $cart = new Cart($cart); $this->storeCart($cart); $restored = TRUE; } } catch (\Exception $e) { $message = 'Could not restore a cart for %customer_id, associating cart instead.'; $this->logger->warning($message, [ '%customer_id' => $customer_id, ]); } } // If no cart was restored, associate the current guest cart to a customer // cart. if (!$restored) { $this->associateCart($customer_id); } } /** * {@inheritdoc} */ public function clearCart() { // Only call clear cart event if there was a previous cart. if ($this->cart) { $this->eventDispatcher->dispatch(Events::CLEAR_CART, new CartEvent($this->cart)); } // Clear cart in memory. $this->cart = NULL; // Clear cart cookie. $this->setCookie('acm_cart_id', NULL); // Clear cart count cookie. $this->setCookie('acm_cart_count', NULL); // Clear the values in storage. $this->storage->remove(self::STORAGE_KEY); } /** * {@inheritdoc} */ public function loadCart($create_new = TRUE) { // No cart in storage, try to load an updated cart. if (!$this->cart) { try { $this->updateCart($create_new); } catch (\Exception $e) { // Intentionally suppressing the error here. This will happen when there // is no cart and still updateCart is called. } } // Allow loaded cart to be altered. if ($this->cart) { $cart_event = $this->eventDispatcher->dispatch(Events::LOAD_CART, new CartEvent($this->cart)); $this->cart = $cart_event->getCart(); $this->setCookie('acm_cart_count', $this->cart->getCartItemsCount()); } else { $this->setCookie('acm_cart_count', NULL); } return $this->cart; } /** * Get skus of current cart items. * * @return array * Items in the current cart. */ public function getCartSkus() { $items = $this->items(); if (empty($items)) { return []; } $skus = []; foreach ($items as $item) { $skus[] = $item['sku']; } return $skus; } /** * {@inheritdoc} */ public function updateCart($create_new = TRUE) { $cart_id = $this->getCartId($create_new); $update = NULL; $cart = $this->cart; if ($cart_id && empty($cart)) { $this->restoreCart($cart_id); } // If cart exists, derive update array and update cookie. if ($cart) { $this->setCookie('acm_cart_id', $cart->id()); $update = $cart->getCart(); } /** @var \Drupal\acm_cart\Event\CartPushEvent $cart_event */ $cart_event = $this->eventDispatcher->dispatch(Events::UPDATE_CART, new CartPushEvent($update)); $update = $cart_event->getRawCart(); // Don't tell connector our stored totals for no reason. if (isset($update->totals)) { unset($update->totals); } if ($cart_id) { try { $cartObject = (object) $this->apiWrapper->updateCart($cart_id, $update); } catch (\Exception $e) { // Restore the cart only if exception is not related to API being down. if (!acm_is_exception_api_down_exception($e)) { $this->restoreCart($cart_id); } throw $e; } if (empty($cartObject)) { return; } $cartObject->cart_id = $cart_id; if ($cart) { $cart->updateCartObject($cartObject); } else { $cart = new Cart($cartObject); } $this->storeCart($cart); return $this->cart; } else { return NULL; } } /** * Pushes the cart to the ecommerce app via the Connector API. * * @deprecated Use updateCart() instead. * * @return null|\Drupal\acm_cart\CartInterface * Returns the cart data as sent by the ecommerce backend */ public function pushCart() { $cart = $this->cart; if (!$cart) { return; } // Cart exists, derive update array and update cookie. $this->setCookie('acm_cart_id', $cart->id()); $update = $cart->getCart(); $cart_event = $this->eventDispatcher->dispatch(Events::PUSH_CART, new CartPushEvent($update)); $cart_response = (object) $this->apiWrapper->updateCart($cart->id(), $cart_event->getRawCart()); if (empty($cart_response)) { return; } return $cart; } /** * {@inheritdoc} */ public function createCart() { // @TODO: It seems this customer_id is never used by Magento. // We may need to edit Magento code to associate the cart if customer_id is // given or use the associate endpoint. $customer_id = $this->getCustomerId(); $cart = (object) $this->apiWrapper->createCart($customer_id); $cart = new Cart($cart); $this->storeCart($cart); return $cart; } /** * {@inheritdoc} */ public function associateCart($customer_id, $customer_email = "") { // We first update the cart in storage. $cart = $this->cart; if (!$cart) { return; } $cart_id = $cart->id(); try { $response = $this->apiWrapper->associateCart($cart_id, $customer_id); } catch (\Exception $e) { $this->restoreCart($cart->id()); throw $e; } // If the association worked and the API returned a new cart ID, set our // cart ID to the new one. if (isset($response['status'], $response['success']) && $response['success'] == 1) { $cart_id = $response['status']; } $data = [ 'cart_id' => $cart_id, 'customer_id' => $customer_id, ]; if ($customer_email) { $data['customer_email'] = $customer_email; } $cart->convertToCustomerCart($data); // Update the cart in storage/memory. $this->storeCart($cart); } /** * {@inheritdoc} */ public function cartExists() { if (isset($this->cart) && $this->cart instanceof CartInterface) { return TRUE; } return FALSE; } /** * {@inheritdoc} */ public function isEmpty() { $items = $this->items(); if (empty($items)) { return TRUE; } return FALSE; } /** * Helper function to clear stock cache of all items in cart. */ public function clearCartItemsStockCache() { $items = $this->items(); if (empty($items)) { return; } foreach ($items as $item) { $sku_entity = SKU::loadFromSku($item['sku']); $sku_entity->refreshStock(); } } /** * Checks if user is anonymous. * * @return bool * TRUE if anonymous, FALSE otherwise. */ protected function isUserAnonymous() { return $this->getCurrentUser()->isAnonymous(); } /** * Gets the current user. * * @return \Drupal\Core\Session\AccountProxy * The current user. */ protected function getCurrentUser() { $use_ecomm_sessions = \Drupal::config('acm.commerce_users') ->get('use_ecomm_sessions'); if ($use_ecomm_sessions) { return \Drupal::service('acm.commerce_user_manager'); } else { return \Drupal::currentUser(); } } /** * Gets the current customer user id. * * @return null|int|string * The current customer id. NULL for none, int for Magento, string for * Hybris. */ protected function getCustomerId() { $customer_id = NULL; $current_user = $this->getCurrentUser(); if ($current_user->isAnonymous()) { return $customer_id; } $use_ecomm_sessions = \Drupal::config('acm.commerce_users') ->get('use_ecomm_sessions'); if ($use_ecomm_sessions) { $customer_id = $current_user->getAccount()->id(); } else { $customer_id = $current_user->getAccount()->acm_customer_id; } return $customer_id; } /** * Gets cookies. * * @return array * The cookies. */ protected function getCookies() { if (isset(\Drupal::request()->cookies)) { return \Drupal::request()->cookies->all(); } return $_COOKIE; } /** * Sets a cookie. * * @param string $name * The cookie name. * @param string $value * The cookie value. */ protected function setCookie($name, $value = '') { // Don't set cookies if this service is used via drush. if (PHP_SAPI === 'cli') { return; } if (isset(\Drupal::request()->cookies)) { \Drupal::request()->cookies->set("Drupal_visitor_{$name}", $value); } user_cookie_save([ $name => $value, ]); } /** * {@inheritdoc} */ public function id() { return $this->cart->id(); } /** * {@inheritdoc} */ public function storeId() { return $this->cart->storeId(); } /** * {@inheritdoc} */ public function customerId() { return $this->cart->customerId(); } /** * {@inheritdoc} */ public function customerEmail() { return $this->cart->customerEmail(); } /** * {@inheritdoc} */ public function totals() { return $this->cart->totals(); } /** * {@inheritdoc} */ public function items() { if (!$this->cartExists()) { return []; } return $this->cart->items(); } /** * {@inheritdoc} */ public function addItemToCart($sku, $quantity, array $extension = []) { $cart_event = $this->eventDispatcher->dispatch(Events::ADD_ITEM_TO_CART, new CartItemEvent($sku, $quantity, $extension)); $this->cart->addItemToCart($cart_event->getSku(), $cart_event->getQuantity(), $cart_event->getExtension()); $this->storeCart($this->cart); } /** * {@inheritdoc} */ public function removeItemFromCart($sku) { $cart_event = $this->eventDispatcher->dispatch(Events::REMOVE_ITEM_FROM_CART, new CartItemEvent($sku)); $this->cart->removeItemFromCart($cart_event->getSku()); $this->storeCart($this->cart); } /** * {@inheritdoc} */ public function addRawItemToCart(array $item) { $cart_event = $this->eventDispatcher->dispatch(Events::ADD_RAW_ITEM_TO_CART, new CartRawItemsEvent($item)); $this->cart->addRawItemToCart($cart_event->getItems()); $this->storeCart($this->cart); } /** * {@inheritdoc} */ public function addItemsToCart(array $items) { $cart_event = $this->eventDispatcher->dispatch(Events::ADD_RAW_ITEM_TO_CART, new CartRawItemsEvent($items)); $this->cart->addItemsToCart($cart_event->getItems()); $this->storeCart($this->cart); } /** * {@inheritdoc} */ public function setItemsInCart(array $items) { $cart_event = $this->eventDispatcher->dispatch(Events::SET_ITEMS_IN_CART, new CartRawItemsEvent($items)); $this->cart->setItemsInCart($cart_event->getItems()); $this->storeCart($this->cart); } /** * {@inheritdoc} */ public function updateItemQuantity($sku, $quantity) { $cart_event = $this->eventDispatcher->dispatch(Events::UPDATE_ITEM_QUANTITY, new CartItemEvent($sku, $quantity)); $this->cart->updateItemQuantity($cart_event->getSku(), $cart_event->getQuantity()); $this->storeCart($this->cart); } /** * {@inheritdoc} */ public function getBilling() { return $this->cart->getBilling(); } /** * {@inheritdoc} */ public function setBilling($address) { $cart_event = $this->eventDispatcher->dispatch(Events::SET_BILLING_ADDRESS, new CartAddressEvent($address)); $this->cart->setBilling($cart_event->getAddress()); $this->storeCart($this->cart); } /** * {@inheritdoc} */ public function getShipping() { return $this->cart->getShipping(); } /** * {@inheritdoc} */ public function getShippingMethod() { return $this->cart->getShippingMethod(); } /** * {@inheritdoc} */ public function getShippingMethodAsString() { return $this->cart->getShippingMethodAsString(); } /** * {@inheritdoc} */ public function clearShippingMethod() { $this->cart->clearShippingMethod(); $this->storeCart($this->cart); } /** * {@inheritdoc} */ public function setShippingMethod($carrier, $method, array $extension = []) { $this->cart->setShippingMethod($carrier, $method, $extension); $this->storeCart($this->cart); } /** * {@inheritdoc} */ public function setShipping($address) { $cart_event = $this->eventDispatcher->dispatch(Events::SET_SHIPPING_ADDRESS, new CartAddressEvent($address)); $this->cart->setShipping($cart_event->getAddress()); $this->storeCart($this->cart); } /** * {@inheritdoc} */ public function getPaymentMethod($full_details = TRUE) { return $this->cart->getPaymentMethod($full_details); } /** * {@inheritdoc} */ public function setPaymentMethod($payment_method, array $data = []) { $this->cart->setPaymentMethod($payment_method, $data); $this->storeCart($this->cart); } /** * {@inheritdoc} */ public function getPaymentMethodData() { return $this->cart->getPaymentMethodData(); } /** * {@inheritdoc} */ public function setPaymentMethodData(array $data = []) { $this->cart->setPaymentMethodData($data); $this->storeCart($this->cart); } /** * {@inheritdoc} */ public function getCheckoutStep() { return $this->cart->getCheckoutStep(); } /** * {@inheritdoc} */ public function setCheckoutStep($step_id) { $this->cart->setCheckoutStep($step_id); $this->storeCart($this->cart); } /** * {@inheritdoc} */ public function getShippable() { return $this->cart->getShippable(); } /** * {@inheritdoc} */ public function setShippable($shippable) { $this->cart->setShippable($shippable); $this->storeCart($this->cart); } /** * {@inheritdoc} */ public function getCoupon() { return $this->cart->getCoupon(); } /** * {@inheritdoc} */ public function setCoupon($coupon) { $cart_event = $this->eventDispatcher->dispatch(Events::SET_COUPON, new CartCouponEvent($coupon)); $this->cart->setCoupon($cart_event->getCoupon()); $this->storeCart($this->cart); } /** * {@inheritdoc} */ public function getExtension($key) { return $this->cart->getExtension($key); } /** * {@inheritdoc} */ public function setExtension($key, $value) { $cart_event = $this->eventDispatcher->dispatch(Events::SET_EXTENSION, new CartExtensionEvent($key, $value)); $this->cart->setExtension($cart_event->getKey(), $cart_event->getValue()); $this->storeCart($this->cart); } /** * {@inheritdoc} */ public function getCart($create_new = TRUE) { if (!$this->cart) { // loadcart() sets and returns $this->cart // or NULL if there is no existing cart and $create_new is false. return $this->loadCart($create_new); } return $this->cart; } /** * {@inheritdoc} */ public function getCartContents() { return $this->cart->getCart(); } /** * {@inheritdoc} */ public function convertToCustomerCart(array $cart) { $this->cart->convertToCustomerCart($cart); $this->storeCart($this->cart); } /** * {@inheritdoc} */ public function getGuestCartEmail() { return $this->cart->getGuestCartEmail(); } /** * {@inheritdoc} */ public function setGuestCartEmail($email) { $current_email = $this->getGuestCartEmail(); // We store the email on the cart object that gets saved to storage, but // we don't want to set it on the actual cart. This is so any subsequent // updateCart or pushCart calls don't inadvertently try to set the guest // cart email again. $this->cart->setGuestCartEmail($email); // The guest cart email should only be set once, so if it was already set // we create a new cart and set the current cart to the new cart's ID. if (!empty($current_email)) { $customer_cart = $this->apiWrapper->createCart(); $this->convertToCustomerCart($customer_cart); } // Set the email on the cart we send to the API, but don't set it on the // cart object that we store locally. $cart_contents = $this->getCartContents(); $update_cart = clone $cart_contents; $update_cart->customer_email = $email; try { $this->apiWrapper->updateCart($this->id(), $update_cart); } catch (\Exception $e) { $this->logger->warning('Error occurred trying to update cart id %cart_id: %message', [ '%cart_id' => $this->id(), '%message' => $e->getMessage(), ]); } $this->storeCart($this->cart); } /** * {@inheritdoc} */ public function get($property_name) { return $this->cart->get($property_name); } /** * Clears the payment method info in cart. */ public function clearPayment() { unset($this->cart->payment); } }