src/Eccube/Service/CartService.php line 418

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.     public function __construct(
  81.         SessionInterface $session,
  82.         EntityManagerInterface $entityManager,
  83.         ProductClassRepository $productClassRepository,
  84.         CartRepository $cartRepository,
  85.         CartItemComparator $cartItemComparator,
  86.         CartItemAllocator $cartItemAllocator,
  87.         OrderRepository $orderRepository,
  88.         TokenStorageInterface $tokenStorage,
  89.         AuthorizationCheckerInterface $authorizationChecker
  90.     ) {
  91.         $this->session $session;
  92.         $this->entityManager $entityManager;
  93.         $this->productClassRepository $productClassRepository;
  94.         $this->cartRepository $cartRepository;
  95.         $this->cartItemComparator $cartItemComparator;
  96.         $this->cartItemAllocator $cartItemAllocator;
  97.         $this->orderRepository $orderRepository;
  98.         $this->tokenStorage $tokenStorage;
  99.         $this->authorizationChecker $authorizationChecker;
  100.     }
  101.     /**
  102.      * 現在のカートの配列を取得する.
  103.      *
  104.      * 本サービスのインスタンスのメンバーが空の場合は、DBまたはセッションからカートを取得する
  105.      *
  106.      * @param bool $empty_delete true の場合、商品明細が空のカートが存在した場合は削除する
  107.      *
  108.      * @return Cart[]
  109.      */
  110.     public function getCarts($empty_delete false)
  111.     {
  112.         if (null !== $this->carts) {
  113.             if ($empty_delete) {
  114.                 $cartKeys = [];
  115.                 foreach (array_keys($this->carts) as $index) {
  116.                     $Cart $this->carts[$index];
  117.                     if ($Cart->getItems()->count() > 0) {
  118.                         $cartKeys[] = $Cart->getCartKey();
  119.                     } else {
  120.                         $this->entityManager->remove($this->carts[$index]);
  121.                         $this->entityManager->flush();
  122.                         unset($this->carts[$index]);
  123.                     }
  124.                 }
  125.                 $this->session->set('cart_keys'$cartKeys);
  126.             }
  127.             return $this->carts;
  128.         }
  129.         if ($this->getUser()) {
  130.             $this->carts $this->getPersistedCarts();
  131.         } else {
  132.             $this->carts $this->getSessionCarts();
  133.         }
  134.         return $this->carts;
  135.     }
  136.     /**
  137.      * 永続化されたカートを返す
  138.      *
  139.      * @return Cart[]
  140.      */
  141.     public function getPersistedCarts()
  142.     {
  143.         return $this->cartRepository->findBy(['Customer' => $this->getUser()]);
  144.     }
  145.     /**
  146.      * セッションにあるカートを返す
  147.      *
  148.      * @return Cart[]
  149.      */
  150.     public function getSessionCarts()
  151.     {
  152.         $cartKeys $this->session->get('cart_keys', []);
  153.         if (empty($cartKeys)) {
  154.             return [];
  155.         }
  156.         return $this->cartRepository->findBy(['cart_key' => $cartKeys], ['id' => 'ASC']);
  157.     }
  158.     /**
  159.      * 会員が保持する永続化されたカートと、非会員時のカートをマージする.
  160.      *
  161.      * カスタマイズ:
  162.      *  - ログイン時は「非会員(匿名)のカートを優先」し、会員が保持していた古いカートは破棄する。
  163.      *  - つまり、ログイン前に使っていた匿名カートだけをログイン後の会員カートとして引き継ぎ、
  164.      *    それ以前に保存されていた会員カートの商品は引き継がない。
  165.      */
  166.     public function mergeFromPersistedCart()
  167.     {
  168.         $sessionCarts $this->getSessionCarts();
  169.         $CartItems = [];
  170.         // セッションにある非会員カートだけを集約する.
  171.         // 既存の会員カートの商品は引き継がず、後続のrestoreCartsで削除される。
  172.         foreach ($sessionCarts as $Cart) {
  173.             $CartItems $this->mergeCartItems($Cart->getCartItems(), $CartItems);
  174.         }
  175.         // カスタマイズ:
  176.         // 非会員カートに1件も商品が無い場合は、既存の会員カートを保持するため何もしない。
  177.         if (count($CartItems) === 0) {
  178.             return;
  179.         }
  180.         $this->restoreCarts($CartItems);
  181.     }
  182.     /**
  183.      * @return Cart|null
  184.      */
  185.     public function getCart()
  186.     {
  187.         $Carts $this->getCarts();
  188.         if (empty($Carts)) {
  189.             return null;
  190.         }
  191.         $cartKeys $this->session->get('cart_keys', []);
  192.         $Cart null;
  193.         if (count($cartKeys) > 0) {
  194.             foreach ($Carts as $cart) {
  195.                 if ($cart->getCartKey() === current($cartKeys)) {
  196.                     $Cart $cart;
  197.                     break;
  198.                 }
  199.             }
  200.         } else {
  201.             $Cart $Carts[0];
  202.         }
  203.         return $Cart;
  204.     }
  205.     /**
  206.      * @param CartItem[] $cartItems
  207.      *
  208.      * @return CartItem[]
  209.      */
  210.     protected function mergeAllCartItems($cartItems = [])
  211.     {
  212.         /** @var CartItem[] $allCartItems */
  213.         $allCartItems = [];
  214.         foreach ($this->getCarts() as $Cart) {
  215.             $allCartItems $this->mergeCartItems($Cart->getCartItems(), $allCartItems);
  216.         }
  217.         return $this->mergeCartItems($cartItems$allCartItems);
  218.     }
  219.     /**
  220.      * @param $cartItems
  221.      * @param $allCartItems
  222.      *
  223.      * @return array
  224.      */
  225.     protected function mergeCartItems($cartItems$allCartItems)
  226.     {
  227.         foreach ($cartItems as $item) {
  228.             $itemExists false;
  229.             foreach ($allCartItems as $itemInArray) {
  230.                 // 同じ明細があればマージする
  231.                 if ($this->cartItemComparator->compare($item$itemInArray)) {
  232.                     $itemInArray->setQuantity($itemInArray->getQuantity() + $item->getQuantity());
  233.                     $itemExists true;
  234.                     break;
  235.                 }
  236.             }
  237.             if (!$itemExists) {
  238.                 $allCartItems[] = $item;
  239.             }
  240.         }
  241.         return $allCartItems;
  242.     }
  243.     protected function restoreCarts($cartItems)
  244.     {
  245.         foreach ($this->getCarts() as $Cart) {
  246.             foreach ($Cart->getCartItems() as $i) {
  247.                 $this->entityManager->remove($i);
  248.                 $this->entityManager->flush();
  249.             }
  250.             $this->entityManager->remove($Cart);
  251.             $this->entityManager->flush();
  252.         }
  253.         $this->carts = [];
  254.         /** @var Cart[] $Carts */
  255.         $Carts = [];
  256.         foreach ($cartItems as $item) {
  257.             $allocatedId $this->cartItemAllocator->allocate($item);
  258.             $cartKey $this->createCartKey($allocatedId$this->getUser());
  259.             if (isset($Carts[$cartKey])) {
  260.                 $Cart $Carts[$cartKey];
  261.                 $Cart->addCartItem($item);
  262.                 $item->setCart($Cart);
  263.             } else {
  264.                 /** @var Cart $Cart */
  265.                 $Cart $this->cartRepository->findOneBy(['cart_key' => $cartKey]);
  266.                 if ($Cart) {
  267.                     foreach ($Cart->getCartItems() as $i) {
  268.                         $this->entityManager->remove($i);
  269.                         $this->entityManager->flush();
  270.                     }
  271.                     $this->entityManager->remove($Cart);
  272.                     $this->entityManager->flush();
  273.                 }
  274.                 $Cart = new Cart();
  275.                 $Cart->setCartKey($cartKey);
  276.                 $Cart->addCartItem($item);
  277.                 $item->setCart($Cart);
  278.                 $Carts[$cartKey] = $Cart;
  279.             }
  280.         }
  281.         $this->carts array_values($Carts);
  282.     }
  283.     /**
  284.      * カートに商品を追加します.
  285.      *
  286.      * @param $ProductClass ProductClass 商品規格
  287.      * @param $quantity int 数量
  288.      * @param $user_imei string|null ユーザーIMEI
  289.      *
  290.      * @return bool 商品を追加できた場合はtrue
  291.      */
  292.     public function addProduct($ProductClass$quantity 1$user_imei null)
  293.     {
  294.         if (!$ProductClass instanceof ProductClass) {
  295.             $ProductClassId $ProductClass;
  296.             $ProductClass $this->entityManager
  297.                 ->getRepository(ProductClass::class)
  298.                 ->find($ProductClassId);
  299.             if (is_null($ProductClass)) {
  300.                 return false;
  301.             }
  302.         }
  303.         $ClassCategory1 $ProductClass->getClassCategory1();
  304.         if ($ClassCategory1 && !$ClassCategory1->isVisible()) {
  305.             return false;
  306.         }
  307.         $ClassCategory2 $ProductClass->getClassCategory2();
  308.         if ($ClassCategory2 && !$ClassCategory2->isVisible()) {
  309.             return false;
  310.         }
  311.         $newItem = new CartItem();
  312.         $newItem->setQuantity($quantity);
  313.         $newItem->setPrice($ProductClass->getPrice02IncTax());
  314.         $newItem->setProductClass($ProductClass);
  315.         // Thêm user_imei nếu được cung cấp
  316.         if ($user_imei !== null) {
  317.             $newItem->setUserImei($user_imei);
  318.         }
  319.         $allCartItems $this->mergeAllCartItems([$newItem]);
  320.         $this->restoreCarts($allCartItems);
  321.         return true;
  322.     }
  323.     public function removeProduct($ProductClass$user_imei null)
  324.     {
  325.         if (!$ProductClass instanceof ProductClass) {
  326.             $ProductClassId $ProductClass;
  327.             $ProductClass $this->entityManager
  328.                 ->getRepository(ProductClass::class)
  329.                 ->find($ProductClassId);
  330.             if (is_null($ProductClass)) {
  331.                 return false;
  332.             }
  333.         }
  334.         $allCartItems $this->mergeAllCartItems();
  335.         $foundIndex = -1;
  336.         foreach ($allCartItems as $index => $itemInCart) {
  337.             // Tìm cart item theo product_class_id và user_imei nếu có
  338.             if ($itemInCart->getProductClass()->getId() == $ProductClass->getId()) {
  339.                 if ($user_imei === null || $itemInCart->getUserImei() === $user_imei) {
  340.                     $foundIndex $index;
  341.                     break;
  342.                 }
  343.             }
  344.         }
  345.         if ($foundIndex >= 0) {
  346.             array_splice($allCartItems$foundIndex1);
  347.             $this->restoreCarts($allCartItems);
  348.         }
  349.         return true;
  350.     }
  351.     public function save()
  352.     {
  353.         $cartKeys = [];
  354.         foreach ($this->carts as $Cart) {
  355.             $Cart->setCustomer($this->getUser());
  356.             $this->entityManager->persist($Cart);
  357.             foreach ($Cart->getCartItems() as $item) {
  358.                 $this->entityManager->persist($item);
  359.             }
  360.             $this->entityManager->flush();
  361.             $cartKeys[] = $Cart->getCartKey();
  362.         }
  363.         $this->session->set('cart_keys'$cartKeys);
  364.         return;
  365.     }
  366.     /**
  367.      * @param  string $pre_order_id
  368.      *
  369.      * @return \Eccube\Service\CartService
  370.      */
  371.     public function setPreOrderId($pre_order_id)
  372.     {
  373.         $this->getCart()->setPreOrderId($pre_order_id);
  374.         return $this;
  375.     }
  376.     /**
  377.      * @return string|null
  378.      */
  379.     public function getPreOrderId()
  380.     {
  381.         $Cart $this->getCart();
  382.         if (!empty($Cart)) {
  383.             return $Cart->getPreOrderId();
  384.         }
  385.         return null;
  386.     }
  387.     /**
  388.      * @return \Eccube\Service\CartService
  389.      */
  390.     public function clear()
  391.     {
  392.         $Carts $this->getCarts();
  393.         if (!empty($Carts)) {
  394.             $removed $this->getCart();
  395.             if ($removed && UnitOfWork::STATE_MANAGED === $this->entityManager->getUnitOfWork()->getEntityState($removed)) {
  396.                 $this->entityManager->remove($removed);
  397.                 $this->entityManager->flush();
  398.                 $cartKeys = [];
  399.                 foreach ($Carts as $key => $Cart) {
  400.                     // テーブルから削除されたカートを除外する
  401.                     if ($Cart == $removed) {
  402.                         unset($Carts[$key]);
  403.                     }
  404.                     $cartKeys[] = $Cart->getCartKey();
  405.                 }
  406.                 $this->session->set('cart_keys'$cartKeys);
  407.                 // 注文完了のカートキーをセッションから削除する
  408.                 $this->session->remove('cart_key');
  409.                 $this->carts $this->cartRepository->findBy(['cart_key' => $cartKeys], ['id' => 'ASC']);
  410.             }
  411.         }
  412.         return $this;
  413.     }
  414.     /**
  415.      * @param CartItemComparator $cartItemComparator
  416.      */
  417.     public function setCartItemComparator($cartItemComparator)
  418.     {
  419.         $this->cartItemComparator $cartItemComparator;
  420.     }
  421.     /**
  422.      * カートキーで指定したインデックスにあるカートを優先にする
  423.      *
  424.      * @param string $cartKey カートキー
  425.      */
  426.     public function setPrimary($cartKey)
  427.     {
  428.         $Carts $this->getCarts();
  429.         $primary $Carts[0];
  430.         $index 0;
  431.         foreach ($Carts as $key => $Cart) {
  432.             if ($Cart->getCartKey() === $cartKey) {
  433.                 $index $key;
  434.                 $primary $Carts[$index];
  435.                 break;
  436.             }
  437.         }
  438.         $prev $Carts[0];
  439.         array_splice($Carts01, [$primary]);
  440.         array_splice($Carts$index1, [$prev]);
  441.         $this->carts $Carts;
  442.         $this->save();
  443.     }
  444.     protected function getUser()
  445.     {
  446.         if (null === $token $this->tokenStorage->getToken()) {
  447.             return;
  448.         }
  449.         if (!is_object($user $token->getUser())) {
  450.             // e.g. anonymous authentication
  451.             return;
  452.         }
  453.         return $user;
  454.     }
  455.     /**
  456.      * @param string $allocatedId
  457.      */
  458.     protected function createCartKey($allocatedIdCustomer $Customer null)
  459.     {
  460.         if ($Customer instanceof Customer) {
  461.             return $Customer->getId().'_'.$allocatedId;
  462.         }
  463.         if ($this->session->has('cart_key_prefix')) {
  464.             return $this->session->get('cart_key_prefix').'_'.$allocatedId;
  465.         }
  466.         do {
  467.             $random StringUtil::random(32);
  468.             $cartKey $random.'_'.$allocatedId;
  469.             $Cart $this->cartRepository->findOneBy(['cart_key' => $cartKey]);
  470.         } while ($Cart);
  471.         $this->session->set('cart_key_prefix'$random);
  472.         return $cartKey;
  473.     }
  474.     /**
  475.      * Lấy tất cả cart items từ tất cả carts
  476.      *
  477.      * @return CartItem[]
  478.      */
  479.     public function getAllCartItems()
  480.     {
  481.         return $this->mergeAllCartItems();
  482.     }
  483.     /**
  484.      * Lấy tổng số lượng của một ProductClass trong cart
  485.      *
  486.      * @param ProductClass $ProductClass
  487.      * @return int
  488.      */
  489.     public function getProductClassQuantityInCart($ProductClass)
  490.     {
  491.         if (!$ProductClass instanceof ProductClass) {
  492.             $ProductClassId $ProductClass;
  493.             $ProductClass $this->entityManager
  494.                 ->getRepository(ProductClass::class)
  495.                 ->find($ProductClassId);
  496.             if (is_null($ProductClass)) {
  497.                 return 0;
  498.             }
  499.         }
  500.         $allCartItems $this->mergeAllCartItems();
  501.         $totalQuantity 0;
  502.         foreach ($allCartItems as $itemInCart) {
  503.             // Kiểm tra theo product_class_id
  504.             if ($itemInCart->getProductClass()->getId() == $ProductClass->getId()) {
  505.                 $totalQuantity += $itemInCart->getQuantity();
  506.             }
  507.         }
  508.         return $totalQuantity;
  509.     }
  510.     /**
  511.      * Kiểm tra IMEI có trùng lặp trong giỏ hàng không
  512.      *
  513.      * @param string $user_imei IMEI cần kiểm tra
  514.      * @return bool true nếu IMEI đã tồn tại trong giỏ hàng
  515.      */
  516.     public function isImeiDuplicate($user_imei)
  517.     {
  518.         if (empty($user_imei)) {
  519.             return false;
  520.         }
  521.         $allCartItems $this->mergeAllCartItems();
  522.         foreach ($allCartItems as $itemInCart) {
  523.             if ($itemInCart->getUserImei() === $user_imei) {
  524.                 return true;
  525.             }
  526.         }
  527.         return false;
  528.     }
  529.     /**
  530.      * Đếm số lượng sản phẩm unique trong giỏ hàng
  531.      *
  532.      * @return int Số lượng sản phẩm unique
  533.      */
  534.     public function getUniqueProductCount()
  535.     {
  536.         $allCartItems $this->mergeAllCartItems();
  537.         $uniqueProducts = [];
  538.         foreach ($allCartItems as $itemInCart) {
  539.             $productId $itemInCart->getProductClass()->getProduct()->getId();
  540.             if (!in_array($productId$uniqueProducts)) {
  541.                 $uniqueProducts[] = $productId;
  542.             }
  543.         }
  544.         return count($uniqueProducts);
  545.     }
  546. }