*/ class Cart extends BaseAction implements EventSubscriberInterface { /** @var RequestStack */ protected $requestStack; /** @var TokenProvider */ protected $tokenProvider; public function __construct(RequestStack $requestStack, TokenProvider $tokenProvider) { $this->requestStack = $requestStack; $this->tokenProvider = $tokenProvider; } public function persistCart(CartPersistEvent $event) { $cart = $event->getCart(); if ($cart->isNew()) { $cart ->setToken($this->generateCartCookieIdentifier()) ->save(); $this->getSession()->setSessionCart($cart); } } /** * add an article in the current cart * * @param \Thelia\Core\Event\Cart\CartEvent $event * @param $eventName * @param EventDispatcherInterface $dispatcher */ public function addItem(CartEvent $event, $eventName, EventDispatcherInterface $dispatcher) { $cart = $event->getCart(); $newness = $event->getNewness(); $append = $event->getAppend(); $quantity = $event->getQuantity(); $currency = $cart->getCurrency(); $customer = $cart->getCustomer(); $discount = 0; if ($cart->isNew()) { $persistEvent = new CartPersistEvent($cart); $dispatcher->dispatch(TheliaEvents::CART_PERSIST, $persistEvent); } if (null !== $customer && $customer->getDiscount() > 0) { $discount = $customer->getDiscount(); } $productSaleElementsId = $event->getProductSaleElementsId(); $productId = $event->getProduct(); // Search for an identical item in the cart $findItemEvent = clone $event; $dispatcher->dispatch(TheliaEvents::CART_FINDITEM, $findItemEvent); $cartItem = $findItemEvent->getCartItem(); if ($cartItem === null || $newness) { $productSaleElements = ProductSaleElementsQuery::create()->findPk($productSaleElementsId); if (null !== $productSaleElements) { $productPrices = $productSaleElements->getPricesByCurrency($currency, $discount); $cartItem = $this->doAddItem($dispatcher, $cart, $productId, $productSaleElements, $quantity, $productPrices); } else { // We did no find any PSE... Something is wrong with the DB, just throw an exception. throw new TheliaProcessException("This item cannot be added to the cart: no matching product sale element was found."); } } elseif ($append && $cartItem !== null) { $cartItem->addQuantity($quantity)->save(); } $event->setCartItem($cartItem); } /** * * Delete specify article present into cart * * @param \Thelia\Core\Event\Cart\CartEvent $event */ public function deleteItem(CartEvent $event) { if (null !== $cartItemId = $event->getCartItemId()) { $cart = $event->getCart(); CartItemQuery::create() ->filterByCartId($cart->getId()) ->filterById($cartItemId) ->delete(); // Force an update of the Cart object to provide // to other listeners an updated CartItem collection. $cart->clearCartItems(); } } /** * Clear the cart * @param CartEvent $event */ public function clear(CartEvent $event) { if (null !== $cart = $event->getCart()) { $cart->delete(); } } /** * * Modify article's quantity * * don't use Form here just test the Request. * * @param \Thelia\Core\Event\Cart\CartEvent $event * @param $eventName * @param EventDispatcherInterface $dispatcher */ public function changeItem(CartEvent $event, $eventName, EventDispatcherInterface $dispatcher) { if ((null !== $cartItemId = $event->getCartItemId()) && (null !== $quantity = $event->getQuantity())) { $cart = $event->getCart(); $cartItem = CartItemQuery::create() ->filterByCartId($cart->getId()) ->filterById($cartItemId) ->findOne(); if ($cartItem) { $event->setCartItem( $this->updateQuantity($dispatcher, $cartItem, $quantity) ); } } } public function updateCart(CurrencyChangeEvent $event, $eventName, EventDispatcherInterface $dispatcher) { $cart = $event->getRequest()->getSession()->getSessionCart($dispatcher); if (null !== $cart) { $this->updateCartPrices($cart, $event->getCurrency()); } } /** * * Refresh article's price * * @param \Thelia\Model\Cart $cart * @param \Thelia\Model\Currency $currency */ public function updateCartPrices(CartModel $cart, CurrencyModel $currency) { $customer = $cart->getCustomer(); $discount = 0; if (null !== $customer && $customer->getDiscount() > 0) { $discount = $customer->getDiscount(); } // cart item foreach ($cart->getCartItems() as $cartItem) { $productSaleElements = $cartItem->getProductSaleElements(); $productPrice = $productSaleElements->getPricesByCurrency($currency, $discount); $cartItem ->setPrice($productPrice->getPrice()) ->setPromoPrice($productPrice->getPromoPrice()); $cartItem->save(); } // update the currency cart $cart->setCurrencyId($currency->getId()); $cart->save(); } /** * increase the quantity for an existing cartItem * * @param EventDispatcherInterface $dispatcher * @param CartItem $cartItem * @param float $quantity * * @throws \Exception * @throws \Propel\Runtime\Exception\PropelException * @return CartItem */ protected function updateQuantity(EventDispatcherInterface $dispatcher, CartItem $cartItem, $quantity) { $cartItem->setDisptacher($dispatcher); $cartItem->updateQuantity($quantity) ->save(); return $cartItem; } /** * try to attach a new item to an existing cart * * @param EventDispatcherInterface $dispatcher * @param \Thelia\Model\Cart $cart * @param int $productId * @param ProductSaleElements $productSaleElements * @param float $quantity * @param ProductPriceTools $productPrices * * @return CartItem */ protected function doAddItem( EventDispatcherInterface $dispatcher, CartModel $cart, $productId, ProductSaleElements $productSaleElements, $quantity, ProductPriceTools $productPrices ) { $cartItem = new CartItem(); $cartItem->setDisptacher($dispatcher); $cartItem ->setCart($cart) ->setProductId($productId) ->setProductSaleElementsId($productSaleElements->getId()) ->setQuantity($quantity) ->setPrice($productPrices->getPrice()) ->setPromoPrice($productPrices->getPromoPrice()) ->setPromo($productSaleElements->getPromo()) ->setPriceEndOfLife(time() + ConfigQuery::read("cart.priceEOF", 60*60*24*30)) ->save(); return $cartItem; } /** * find a specific record in CartItem table using the Cart id, the product id * and the product_sale_elements id * * @param int $cartId * @param int $productId * @param int $productSaleElementsId * @return CartItem * * @deprecated this method is deprecated. Dispatch a TheliaEvents::CART_FINDITEM instead */ protected function findItem($cartId, $productId, $productSaleElementsId) { return CartItemQuery::create() ->filterByCartId($cartId) ->filterByProductId($productId) ->filterByProductSaleElementsId($productSaleElementsId) ->findOne(); } /** * Find a specific record in CartItem table using the current CartEvent * * @param CartEvent $event the cart event */ public function findCartItem(CartEvent $event) { // Do not try to find a cartItem if one exists in the event, as previous event handlers // mays have put it in th event. if (null === $event->getCartItem() && null !== $foundItem = CartItemQuery::create() ->filterByCartId($event->getCart()->getId()) ->filterByProductId($event->getProduct()) ->filterByProductSaleElementsId($event->getProductSaleElementsId()) ->findOne()) { $event->setCartItem($foundItem); } } /** * Search if cart already exists in session. If not try to restore it from the cart cookie, * or duplicate an old one. * * @param CartRestoreEvent $cartRestoreEvent * @param $eventName * @param EventDispatcherInterface $dispatcher */ public function restoreCurrentCart(CartRestoreEvent $cartRestoreEvent, $eventName, EventDispatcherInterface $dispatcher) { $cookieName = ConfigQuery::read("cart.cookie_name", 'thelia_cart'); $persistentCookie = ConfigQuery::read("cart.use_persistent_cookie", 1); $cart = null; if ($this->requestStack->getCurrentRequest()->cookies->has($cookieName) && $persistentCookie) { $cart = $this->managePersistentCart($cartRestoreEvent, $cookieName, $dispatcher); } elseif (!$persistentCookie) { $cart = $this->manageNonPersistentCookie($cartRestoreEvent, $dispatcher); } // Still no cart ? Create a new one. if (null === $cart) { $cart = $this->dispatchNewCart($dispatcher); } $cartRestoreEvent->setCart($cart); } /** * The cart token is not saved in a cookie, if the cart is present in session, we just change the customer id * if needed or create duplicate the current cart if the customer is not the same as customer already present in * the cart. * * @param CartRestoreEvent $cartRestoreEvent * @param EventDispatcherInterface $dispatcher * @return CartModel * @throws \Exception * @throws \Propel\Runtime\Exception\PropelException */ protected function manageNonPersistentCookie(CartRestoreEvent $cartRestoreEvent, EventDispatcherInterface $dispatcher) { $cart = $cartRestoreEvent->getCart(); if (null === $cart) { $cart = $this->dispatchNewCart($dispatcher); } else { $cart = $this->manageCartDuplicationAtCustomerLogin($cart, $dispatcher); } return $cart; } /** * * The cart token is saved in a cookie so we try to retrieve it. Then the customer is checked. * * @param CartRestoreEvent $cartRestoreEvent * @param $cookieName * @param EventDispatcherInterface $dispatcher * @return CartModel * @throws \Exception * @throws \Propel\Runtime\Exception\PropelException */ protected function managePersistentCart(CartRestoreEvent $cartRestoreEvent, $cookieName, EventDispatcherInterface $dispatcher) { // The cart cookie exists -> get the cart token $token = $this->requestStack->getCurrentRequest()->cookies->get($cookieName); // Check if a cart exists for this token if (null !== $cart = CartQuery::create()->findOneByToken($token)) { $cart = $this->manageCartDuplicationAtCustomerLogin($cart, $dispatcher); } return $cart; } protected function manageCartDuplicationAtCustomerLogin(CartModel $cart, EventDispatcherInterface $dispatcher) { /** @var CustomerModel $customer */ if (null !== $customer = $this->getSession()->getCustomerUser()) { // Check if we have to duplicate the existing cart. $duplicateCart = true; // A customer is logged in. if (null === $cart->getCustomerId()) { // If the customer has a discount, whe have to duplicate the cart, // so that the discount will be applied to the products in cart. if (0 === $customer->getDiscount() || 0 === $cart->countCartItems()) { // If no discount, or an empty cart, there's no need to duplicate. $duplicateCart = false; } } if ($duplicateCart) { // Duplicate the cart $cart = $this->duplicateCart($dispatcher, $cart, $customer); } else { // No duplication required, just assign the cart to the customer $cart->setCustomerId($customer->getId())->save(); } } elseif ($cart->getCustomerId() != null) { // The cart belongs to another user if (0 === $cart->countCartItems()) { // No items in cart, assign it to nobody. $cart->setCustomerId(null)->save(); } else { // Some itemls in cart, duplicate it without assigning a customer ID. $cart = $this->duplicateCart($dispatcher, $cart); } } return $cart; } /** * @param EventDispatcherInterface $dispatcher * @return CartModel */ protected function dispatchNewCart(EventDispatcherInterface $dispatcher) { $cartCreateEvent = new CartCreateEvent(); $dispatcher->dispatch(TheliaEvents::CART_CREATE_NEW, $cartCreateEvent); return $cartCreateEvent->getCart(); } /** * Create a new, empty cart object, and assign it to the current customer, if any. * * @param CartCreateEvent $cartCreateEvent */ public function createEmptyCart(CartCreateEvent $cartCreateEvent) { $cart = new CartModel(); $cart->setCurrency($this->getSession()->getCurrency(true)); /** @var CustomerModel $customer */ if (null !== $customer = $this->getSession()->getCustomerUser()) { $cart->setCustomer(CustomerQuery::create()->findPk($customer->getId())); } $this->getSession()->setSessionCart($cart); if (ConfigQuery::read("cart.use_persistent_cookie", 1) == 1) { // set cart_use_cookie to "" to remove the cart cookie // see Thelia\Core\EventListener\ResponseListener $this->getSession()->set("cart_use_cookie", ""); } $cartCreateEvent->setCart($cart); } /** * Duplicate an existing Cart. If a customer ID is provided the created cart will be attached to this customer. * * @param EventDispatcherInterface $dispatcher * @param CartModel $cart * @param CustomerModel $customer * @return CartModel */ protected function duplicateCart(EventDispatcherInterface $dispatcher, CartModel $cart, CustomerModel $customer = null) { $newCart = $cart->duplicate( $this->generateCartCookieIdentifier(), $customer, $this->getSession()->getCurrency(), $dispatcher ); $cartEvent = new CartDuplicationEvent($newCart, $cart); $dispatcher->dispatch(TheliaEvents::CART_DUPLICATE, $cartEvent); return $cartEvent->getDuplicatedCart(); } /** * Generate the cart cookie identifier, or return null if the cart is only managed in the session object, * not in a client cookie. * * @return string */ protected function generateCartCookieIdentifier() { $id = null; if (ConfigQuery::read("cart.use_persistent_cookie", 1) == 1) { $id = $this->tokenProvider->getToken(); $this->getSession()->set('cart_use_cookie', $id); } return $id; } /** * {@inheritdoc} */ public static function getSubscribedEvents() { return array( TheliaEvents::CART_PERSIST => array("persistCart", 128), TheliaEvents::CART_RESTORE_CURRENT => array("restoreCurrentCart", 128), TheliaEvents::CART_CREATE_NEW => array("createEmptyCart", 128), TheliaEvents::CART_ADDITEM => array("addItem", 128), TheliaEvents::CART_FINDITEM => array("findCartItem", 128), TheliaEvents::CART_DELETEITEM => array("deleteItem", 128), TheliaEvents::CART_UPDATEITEM => array("changeItem", 128), TheliaEvents::CART_CLEAR => array("clear", 128), TheliaEvents::CHANGE_DEFAULT_CURRENCY => array("updateCart", 128), ); } /** * Returns the session from the current request * * @return \Thelia\Core\HttpFoundation\Session\Session */ protected function getSession() { return $this->requestStack->getCurrentRequest()->getSession(); } }