src/Eccube/Service/CartService.php line 159

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of EC-CUBE
  4.  *
  5.  * Copyright(c) EC-CUBE CO.,LTD. All Rights Reserved.
  6.  *
  7.  * http://www.ec-cube.co.jp/
  8.  *
  9.  * For the full copyright and license information, please view the LICENSE
  10.  * file that was distributed with this source code.
  11.  */
  12. namespace Eccube\Service;
  13. use Doctrine\ORM\EntityManagerInterface;
  14. use Doctrine\ORM\UnitOfWork;
  15. use Eccube\Entity\Cart;
  16. use Eccube\Entity\CartItem;
  17. use Eccube\Entity\Customer;
  18. use Eccube\Entity\ItemHolderInterface;
  19. use Eccube\Entity\ProductClass;
  20. use Eccube\Repository\CartRepository;
  21. use Eccube\Repository\OrderRepository;
  22. use Eccube\Repository\ProductClassRepository;
  23. use Eccube\Service\Cart\CartItemAllocator;
  24. use Eccube\Service\Cart\CartItemComparator;
  25. use Eccube\Util\StringUtil;
  26. use Symfony\Component\HttpFoundation\Session\SessionInterface;
  27. use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
  28. use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
  29. class CartService
  30. {
  31.     /**
  32.      * @var Cart[]
  33.      */
  34.     protected $carts;
  35.     /**
  36.      * @var SessionInterface
  37.      */
  38.     protected $session;
  39.     /**
  40.      * @var \Doctrine\ORM\EntityManagerInterface
  41.      */
  42.     protected $entityManager;
  43.     /**
  44.      * @var ItemHolderInterface
  45.      *
  46.      * @deprecated
  47.      */
  48.     protected $cart;
  49.     /**
  50.      * @var ProductClassRepository
  51.      */
  52.     protected $productClassRepository;
  53.     /**
  54.      * @var CartRepository
  55.      */
  56.     protected $cartRepository;
  57.     /**
  58.      * @var CartItemComparator
  59.      */
  60.     protected $cartItemComparator;
  61.     /**
  62.      * @var CartItemAllocator
  63.      */
  64.     protected $cartItemAllocator;
  65.     /**
  66.      * @var OrderRepository
  67.      */
  68.     protected $orderRepository;
  69.     /**
  70.      * @var TokenStorageInterface
  71.      */
  72.     protected $tokenStorage;
  73.     /**
  74.      * @var AuthorizationCheckerInterface
  75.      */
  76.     protected $authorizationChecker;
  77.     /**
  78.      * CartService constructor.
  79.      *
  80.      * @param SessionInterface $session
  81.      * @param EntityManagerInterface $entityManager
  82.      * @param ProductClassRepository $productClassRepository
  83.      * @param CartItemComparator $cartItemComparator
  84.      * @param CartItemAllocator $cartItemAllocator
  85.      * @param TokenStorageInterface $tokenStorage
  86.      * @param AuthorizationCheckerInterface $authorizationChecker
  87.      */
  88.     public function __construct(
  89.         SessionInterface $session,
  90.         EntityManagerInterface $entityManager,
  91.         ProductClassRepository $productClassRepository,
  92.         CartRepository $cartRepository,
  93.         CartItemComparator $cartItemComparator,
  94.         CartItemAllocator $cartItemAllocator,
  95.         OrderRepository $orderRepository,
  96.         TokenStorageInterface $tokenStorage,
  97.         AuthorizationCheckerInterface $authorizationChecker
  98.     ) {
  99.         $this->session $session;
  100.         $this->entityManager $entityManager;
  101.         $this->productClassRepository $productClassRepository;
  102.         $this->cartRepository $cartRepository;
  103.         $this->cartItemComparator $cartItemComparator;
  104.         $this->cartItemAllocator $cartItemAllocator;
  105.         $this->orderRepository $orderRepository;
  106.         $this->tokenStorage $tokenStorage;
  107.         $this->authorizationChecker $authorizationChecker;
  108.     }
  109.     /**
  110.      * 現在のカートの配列を取得する.
  111.      *
  112.      * 本サービスのインスタンスのメンバーが空の場合は、DBまたはセッションからカートを取得する
  113.      *
  114.      * @param bool $empty_delete true の場合、商品明細が空のカートが存在した場合は削除する
  115.      *
  116.      * @return Cart[]
  117.      */
  118.     public function getCarts($empty_delete false)
  119.     {
  120.         if (null !== $this->carts) {
  121.             if ($empty_delete) {
  122.                 $cartKeys = [];
  123.                 foreach (array_keys($this->carts) as $index) {
  124.                     $Cart $this->carts[$index];
  125.                     if ($Cart->getItems()->count() > 0) {
  126.                         $cartKeys[] = $Cart->getCartKey();
  127.                     } else {
  128.                         $this->entityManager->remove($this->carts[$index]);
  129.                         $this->entityManager->flush($this->carts[$index]);
  130.                         unset($this->carts[$index]);
  131.                     }
  132.                 }
  133.                 $this->session->set('cart_keys'$cartKeys);
  134.             }
  135.             return $this->carts;
  136.         }
  137.         if ($this->getUser()) {
  138.             $this->carts $this->getPersistedCarts();
  139.         } else {
  140.             $this->carts $this->getSessionCarts();
  141.         }
  142.         return $this->carts;
  143.     }
  144.     /**
  145.      * 永続化されたカートを返す
  146.      *
  147.      * @return Cart[]
  148.      */
  149.     public function getPersistedCarts()
  150.     {
  151.         return $this->cartRepository->findBy(['Customer' => $this->getUser()]);
  152.     }
  153.     /**
  154.      * セッションにあるカートを返す
  155.      *
  156.      * @return Cart[]
  157.      */
  158.     public function getSessionCarts()
  159.     {
  160.         $cartKeys $this->session->get('cart_keys', []);
  161.         if (empty($cartKeys)) {
  162.             return [];
  163.         }
  164.         return $this->cartRepository->findBy(['cart_key' => $cartKeys], ['id' => 'DESC']);
  165.     }
  166.     /**
  167.      * 会員が保持する永続化されたカートと、非会員時のカートをマージする.
  168.      */
  169.     public function mergeFromPersistedCart()
  170.     {
  171.         $CartItems = [];
  172.         foreach ($this->getPersistedCarts() as $Cart) {
  173.             $CartItems $this->mergeCartItems($Cart->getCartItems(), $CartItems);
  174.         }
  175.         // セッションにある非会員カートとDBから取得した会員カートをマージする.
  176.         foreach ($this->getSessionCarts() as $Cart) {
  177.             $CartItems $this->mergeCartItems($Cart->getCartItems(), $CartItems);
  178.         }
  179.         $this->restoreCarts($CartItems);
  180.     }
  181.     /**
  182.      * @return Cart|null
  183.      */
  184.     public function getCart()
  185.     {
  186.         $Carts $this->getCarts();
  187.         if (empty($Carts)) {
  188.             return null;
  189.         }
  190.         $cartKeys $this->session->get('cart_keys', []);
  191.         $Cart null;
  192.         if (count($cartKeys) > 0) {
  193.             foreach ($Carts as $cart) {
  194.                 if ($cart->getCartKey() === current($cartKeys)) {
  195.                     $Cart $cart;
  196.                     break;
  197.                 }
  198.             }
  199.         } else {
  200.             $Cart $Carts[0];
  201.         }
  202.         return $Cart;
  203.     }
  204.     /**
  205.      * @param CartItem[] $cartItems
  206.      *
  207.      * @return CartItem[]
  208.      */
  209.     protected function mergeAllCartItems($cartItems = [])
  210.     {
  211.         /** @var CartItem[] $allCartItems */
  212.         $allCartItems = [];
  213.         foreach ($this->getCarts() as $Cart) {
  214.             $allCartItems $this->mergeCartItems($Cart->getCartItems(), $allCartItems);
  215.         }
  216.         return $this->mergeCartItems($cartItems$allCartItems);
  217.     }
  218.     /**
  219.      * @param $cartItems
  220.      * @param $allCartItems
  221.      *
  222.      * @return array
  223.      */
  224.     protected function mergeCartItems($cartItems$allCartItems)
  225.     {
  226.         foreach ($cartItems as $item) {
  227.             $itemExists false;
  228.             foreach ($allCartItems as $itemInArray) {
  229.                 // 同じ明細があればマージする
  230.                 if ($this->cartItemComparator->compare($item$itemInArray)) {
  231.                     $itemInArray->setQuantity($itemInArray->getQuantity() + $item->getQuantity());
  232.                     $itemExists true;
  233.                     break;
  234.                 }
  235.             }
  236.             if (!$itemExists) {
  237.                 $allCartItems[] = $item;
  238.             }
  239.         }
  240.         return $allCartItems;
  241.     }
  242.     protected function restoreCarts($cartItems)
  243.     {
  244.         foreach ($this->getCarts() as $Cart) {
  245.             foreach ($Cart->getCartItems() as $i) {
  246.                 $this->entityManager->remove($i);
  247.                 $this->entityManager->flush($i);
  248.             }
  249.             $this->entityManager->remove($Cart);
  250.             $this->entityManager->flush($Cart);
  251.         }
  252.         $this->carts = [];
  253.         /** @var Cart[] $Carts */
  254.         $Carts = [];
  255.         foreach ($cartItems as $item) {
  256.             $allocatedId $this->cartItemAllocator->allocate($item);
  257.             $cartKey $this->createCartKey($allocatedId$this->getUser());
  258.             if (isset($Carts[$cartKey])) {
  259.                 $Cart $Carts[$cartKey];
  260.                 $Cart->addCartItem($item);
  261.                 $item->setCart($Cart);
  262.             } else {
  263.                 /** @var Cart $Cart */
  264.                 $Cart $this->cartRepository->findOneBy(['cart_key' => $cartKey]);
  265.                 if ($Cart) {
  266.                     foreach ($Cart->getCartItems() as $i) {
  267.                         $this->entityManager->remove($i);
  268.                         $this->entityManager->flush($i);
  269.                     }
  270.                     $this->entityManager->remove($Cart);
  271.                     $this->entityManager->flush($Cart);
  272.                 }
  273.                 $Cart = new Cart();
  274.                 $Cart->setCartKey($cartKey);
  275.                 $Cart->addCartItem($item);
  276.                 $item->setCart($Cart);
  277.                 $Carts[$cartKey] = $Cart;
  278.             }
  279.         }
  280.         $this->carts array_values($Carts);
  281.     }
  282.     /**
  283.      * カートに商品を追加します.
  284.      *
  285.      * @param $ProductClass ProductClass 商品規格
  286.      * @param $quantity int 数量
  287.      *
  288.      * @return bool 商品を追加できた場合はtrue
  289.      */
  290.     public function addProduct($ProductClass$quantity 1)
  291.     {
  292.         if (!$ProductClass instanceof ProductClass) {
  293.             $ProductClassId $ProductClass;
  294.             $ProductClass $this->entityManager
  295.                 ->getRepository(ProductClass::class)
  296.                 ->find($ProductClassId);
  297.             if (is_null($ProductClass)) {
  298.                 return false;
  299.             }
  300.         }
  301.         $ClassCategory1 $ProductClass->getClassCategory1();
  302.         if ($ClassCategory1 && !$ClassCategory1->isVisible()) {
  303.             return false;
  304.         }
  305.         $ClassCategory2 $ProductClass->getClassCategory2();
  306.         if ($ClassCategory2 && !$ClassCategory2->isVisible()) {
  307.             return false;
  308.         }
  309.         $newItem = new CartItem();
  310.         $newItem->setQuantity($quantity);
  311.         $newItem->setPrice($ProductClass->getPrice02IncTax());
  312.         $newItem->setProductClass($ProductClass);
  313.         $allCartItems $this->mergeAllCartItems([$newItem]);
  314.         $this->restoreCarts($allCartItems);
  315.         return true;
  316.     }
  317.     public function removeProduct($ProductClass)
  318.     {
  319.         if (!$ProductClass instanceof ProductClass) {
  320.             $ProductClassId $ProductClass;
  321.             $ProductClass $this->entityManager
  322.                 ->getRepository(ProductClass::class)
  323.                 ->find($ProductClassId);
  324.             if (is_null($ProductClass)) {
  325.                 return false;
  326.             }
  327.         }
  328.         $removeItem = new CartItem();
  329.         $removeItem->setPrice($ProductClass->getPrice02IncTax());
  330.         $removeItem->setProductClass($ProductClass);
  331.         $allCartItems $this->mergeAllCartItems();
  332.         $foundIndex = -1;
  333.         foreach ($allCartItems as $index => $itemInCart) {
  334.             if ($this->cartItemComparator->compare($itemInCart$removeItem)) {
  335.                 $foundIndex $index;
  336.                 break;
  337.             }
  338.         }
  339.         array_splice($allCartItems$foundIndex1);
  340.         $this->restoreCarts($allCartItems);
  341.         return true;
  342.     }
  343.     public function save()
  344.     {
  345.         $cartKeys = [];
  346.         foreach ($this->carts as $Cart) {
  347.             $Cart->setCustomer($this->getUser());
  348.             $this->entityManager->persist($Cart);
  349.             foreach ($Cart->getCartItems() as $item) {
  350.                 $this->entityManager->persist($item);
  351.                 $this->entityManager->flush($item);
  352.             }
  353.             $this->entityManager->flush($Cart);
  354.             $cartKeys[] = $Cart->getCartKey();
  355.         }
  356.         $this->session->set('cart_keys'$cartKeys);
  357.         return;
  358.     }
  359.     /**
  360.      * @param  string $pre_order_id
  361.      *
  362.      * @return \Eccube\Service\CartService
  363.      */
  364.     public function setPreOrderId($pre_order_id)
  365.     {
  366.         $this->getCart()->setPreOrderId($pre_order_id);
  367.         return $this;
  368.     }
  369.     /**
  370.      * @return null|string
  371.      */
  372.     public function getPreOrderId()
  373.     {
  374.         $Cart $this->getCart();
  375.         if (!empty($Cart)) {
  376.             return $Cart->getPreOrderId();
  377.         }
  378.         return null;
  379.     }
  380.     /**
  381.      * @return \Eccube\Service\CartService
  382.      */
  383.     public function clear()
  384.     {
  385.         $Carts $this->getCarts();
  386.         if (!empty($Carts)) {
  387.             $removed $this->getCart();
  388.             if ($removed && UnitOfWork::STATE_MANAGED === $this->entityManager->getUnitOfWork()->getEntityState($removed)) {
  389.                 $this->entityManager->remove($removed);
  390.                 $this->entityManager->flush($removed);
  391.                 $cartKeys = [];
  392.                 foreach ($Carts as $key => $Cart) {
  393.                     // テーブルから削除されたカートを除外する
  394.                     if ($Cart == $removed) {
  395.                         unset($Carts[$key]);
  396.                     }
  397.                     $cartKeys[] = $Cart->getCartKey();
  398.                 }
  399.                 $this->session->set('cart_keys'$cartKeys);
  400.                 // 注文完了のカートキーをセッションから削除する
  401.                 $this->session->remove('cart_key');
  402.                 $this->carts $this->cartRepository->findBy(['cart_key' => $cartKeys], ['id' => 'DESC']);
  403.             }
  404.         }
  405.         return $this;
  406.     }
  407.     /**
  408.      * @param CartItemComparator $cartItemComparator
  409.      */
  410.     public function setCartItemComparator($cartItemComparator)
  411.     {
  412.         $this->cartItemComparator $cartItemComparator;
  413.     }
  414.     /**
  415.      * カートキーで指定したインデックスにあるカートを優先にする
  416.      *
  417.      * @param string $cartKey カートキー
  418.      */
  419.     public function setPrimary($cartKey)
  420.     {
  421.         $Carts $this->getCarts();
  422.         $primary $Carts[0];
  423.         $index 0;
  424.         foreach ($Carts as $key => $Cart) {
  425.             if ($Cart->getCartKey() === $cartKey) {
  426.                 $index $key;
  427.                 $primary $Carts[$index];
  428.                 break;
  429.             }
  430.         }
  431.         $prev $Carts[0];
  432.         array_splice($Carts01, [$primary]);
  433.         array_splice($Carts$index1, [$prev]);
  434.         $this->carts $Carts;
  435.         $this->save();
  436.     }
  437.     protected function getUser()
  438.     {
  439.         if (null === $token $this->tokenStorage->getToken()) {
  440.             return;
  441.         }
  442.         if (!is_object($user $token->getUser())) {
  443.             // e.g. anonymous authentication
  444.             return;
  445.         }
  446.         return $user;
  447.     }
  448.     /**
  449.      * @param string $allocatedId
  450.      */
  451.     protected function createCartKey($allocatedIdCustomer $Customer null)
  452.     {
  453.         if ($Customer instanceof Customer) {
  454.             return $Customer->getId().'_'.$allocatedId;
  455.         }
  456.         if ($this->session->has('cart_key_prefix')) {
  457.             return $this->session->get('cart_key_prefix').'_'.$allocatedId;
  458.         }
  459.         do {
  460.             $random StringUtil::random(32);
  461.             $cartKey $random.'_'.$allocatedId;
  462.             $Cart $this->cartRepository->findOneBy(['cart_key' => $cartKey]);
  463.         } while ($Cart);
  464.         $this->session->set('cart_key_prefix'$random);
  465.         return $cartKey;
  466.     }
  467. }