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);
}
}
