src/Eccube/Controller/CartController.php line 125

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\Controller;
  13. use Eccube\Entity\BaseInfo;
  14. use Eccube\Entity\ProductClass;
  15. use Eccube\Event\EccubeEvents;
  16. use Eccube\Event\EventArgs;
  17. use Eccube\Repository\BaseInfoRepository;
  18. use Eccube\Repository\ProductClassRepository;
  19. use Eccube\Service\CartService;
  20. use Eccube\Service\OrderHelper;
  21. use Eccube\Service\PurchaseFlow\PurchaseContext;
  22. use Eccube\Service\PurchaseFlow\PurchaseFlow;
  23. use Eccube\Service\PurchaseFlow\PurchaseFlowResult;
  24. use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
  25. use Symfony\Component\HttpFoundation\Request;
  26. use Symfony\Component\Routing\Annotation\Route;
  27. use Eccube\Helper\PageNameHelper;
  28. class CartController extends AbstractController
  29. {
  30.     /**
  31.      * @var ProductClassRepository
  32.      */
  33.     protected $productClassRepository;
  34.     /**
  35.      * @var CartService
  36.      */
  37.     protected $cartService;
  38.     /**
  39.      * @var PurchaseFlow
  40.      */
  41.     protected $purchaseFlow;
  42.     /**
  43.      * @var BaseInfo
  44.      */
  45.     protected $baseInfo;
  46.     /**
  47.      * @var PageNameHelper
  48.      */
  49.     protected $pageNameHelper;
  50.     /**
  51.      * CartController constructor.
  52.      *
  53.      * @param ProductClassRepository $productClassRepository
  54.      * @param CartService $cartService
  55.      * @param PurchaseFlow $cartPurchaseFlow
  56.      * @param BaseInfoRepository $baseInfoRepository
  57.      */
  58.     public function __construct(
  59.         ProductClassRepository $productClassRepository,
  60.         CartService $cartService,
  61.         PurchaseFlow $cartPurchaseFlow,
  62.         BaseInfoRepository $baseInfoRepository,
  63.         PageNameHelper $pageNameHelper
  64.     ) {
  65.         $this->productClassRepository = $productClassRepository;
  66.         $this->cartService = $cartService;
  67.         $this->purchaseFlow = $cartPurchaseFlow;
  68.         $this->baseInfo = $baseInfoRepository->get();
  69.         $this->pageNameHelper = $pageNameHelper;
  70.     }
  71.     /**
  72.      * カート画面.
  73.      *
  74.      * @Route("/cart", name="cart", methods={"GET"})
  75.      * @Template("Cart/index.twig")
  76.      */
  77.     public function index(Request $request)
  78.     {
  79.         // カートを取得して明細の正規化を実行
  80.         $Carts = $this->cartService->getCarts();
  81.         $this->execPurchaseFlow($Carts);
  82.         // TODO itemHolderから取得できるように
  83.         $least = [];
  84.         $quantity = [];
  85.         $isDeliveryFree = [];
  86.         $totalPrice = 0;
  87.         $totalQuantity = 0;
  88.         foreach ($Carts as $Cart) {
  89.             $quantity[$Cart->getCartKey()] = 0;
  90.             $isDeliveryFree[$Cart->getCartKey()] = false;
  91.             if ($this->baseInfo->getDeliveryFreeQuantity()) {
  92.                 if ($this->baseInfo->getDeliveryFreeQuantity() > $Cart->getQuantity()) {
  93.                     $quantity[$Cart->getCartKey()] = $this->baseInfo->getDeliveryFreeQuantity() - $Cart->getQuantity();
  94.                 } else {
  95.                     $isDeliveryFree[$Cart->getCartKey()] = true;
  96.                 }
  97.             }
  98.             if ($this->baseInfo->getDeliveryFreeAmount()) {
  99.                 if (!$isDeliveryFree[$Cart->getCartKey()] && $this->baseInfo->getDeliveryFreeAmount() <= $Cart->getTotalPrice()) {
  100.                     $isDeliveryFree[$Cart->getCartKey()] = true;
  101.                 } else {
  102.                     $least[$Cart->getCartKey()] = $this->baseInfo->getDeliveryFreeAmount() - $Cart->getTotalPrice();
  103.                 }
  104.             }
  105.             $totalPrice += $Cart->getTotalPrice();
  106.             $totalQuantity += $Cart->getQuantity();
  107.         }
  108.         // カートが分割された時のセッション情報を削除
  109.         $request->getSession()->remove(OrderHelper::SESSION_CART_DIVIDE_FLAG);
  110.         // 空のカートを削除し取得し直す
  111.         $Carts = $this->cartService->getCarts(true);
  112.         return [
  113.             'totalPrice' => $totalPrice,
  114.             'totalQuantity' => $totalQuantity,
  115.             'Carts' => $Carts,
  116.             'least' => $least,
  117.             'quantity' => $quantity,
  118.             'is_delivery_free' => $isDeliveryFree,
  119.             'hasMissingUserImei' => $this->cartHasMissingUserImei($Carts),
  120.             'pageName' => $this->pageNameHelper->createPageName(trans('front.cart.page_name')),
  121.         ];
  122.     }
  123.     /**
  124.      * @param $Carts
  125.      *
  126.      * @return \Symfony\Component\HttpFoundation\RedirectResponse|null
  127.      */
  128.     protected function execPurchaseFlow($Carts)
  129.     {
  130.         /** @var PurchaseFlowResult[] $flowResults */
  131.         $flowResults = array_map(function ($Cart) {
  132.             $purchaseContext = new PurchaseContext($Cart, $this->getUser());
  133.             return $this->purchaseFlow->validate($Cart, $purchaseContext);
  134.         }, $Carts);
  135.         // 復旧不可のエラーが発生した場合はカートをクリアして再描画
  136.         $hasError = false;
  137.         foreach ($flowResults as $result) {
  138.             if ($result->hasError()) {
  139.                 $hasError = true;
  140.                 foreach ($result->getErrors() as $error) {
  141.                     $this->addRequestError($error->getMessage());
  142.                 }
  143.             }
  144.         }
  145.         if ($hasError) {
  146.             $this->cartService->clear();
  147.             return $this->redirectToRoute('cart');
  148.         }
  149.         $this->cartService->save();
  150.         foreach ($flowResults as $index => $result) {
  151.             foreach ($result->getWarning() as $warning) {
  152.                 if ($Carts[$index]->getItems()->count() > 0) {
  153.                     $cart_key = $Carts[$index]->getCartKey();
  154.                     $this->addRequestError($warning->getMessage(), "front.cart.${cart_key}");
  155.                 } else {
  156.                     // キーが存在しない場合はグローバルにエラーを表示する
  157.                     $this->addRequestError($warning->getMessage());
  158.                 }
  159.             }
  160.         }
  161.         return null;
  162.     }
  163.     /**
  164.      * カート明細の加算/減算/削除を行う.
  165.      *
  166.      * - 加算
  167.      *      - 明細の個数を1増やす
  168.      * - 減算
  169.      *      - 明細の個数を1減らす
  170.      *      - 個数が0になる場合は、明細を削除する
  171.      * - 削除
  172.      *      - 明細を削除する
  173.      *
  174.      * @Route(
  175.      *     path="/cart/{operation}/{productClassId}",
  176.      *     name="cart_handle_item",
  177.      *     methods={"PUT"},
  178.      *     requirements={
  179.      *          "operation": "up|down|remove",
  180.      *          "productClassId": "\d+"
  181.      *     }
  182.      * )
  183.      */
  184.     public function handleCartItem(Request $request, $operation, $productClassId)
  185.     {
  186.         log_info('カート明細操作開始', ['operation' => $operation, 'product_class_id' => $productClassId]);
  187.         $this->isTokenValid();
  188.         /** @var ProductClass $ProductClass */
  189.         $ProductClass = $this->productClassRepository->find($productClassId);
  190.         if (is_null($ProductClass)) {
  191.             log_info('商品が存在しないため、カート画面へredirect', ['operation' => $operation, 'product_class_id' => $productClassId]);
  192.             return $this->redirectToRoute('cart');
  193.         }
  194.         // Lấy user_imei từ request
  195.         $user_imei = $request->request->get('user_imei');
  196.         // 明細の増減・削除
  197.         switch ($operation) {
  198.             case 'up':
  199.                 $this->cartService->addProduct($ProductClass, 1, $user_imei);
  200.                 break;
  201.             case 'down':
  202.                 $this->cartService->addProduct($ProductClass, -1, $user_imei);
  203.                 break;
  204.             case 'remove':
  205.                 $this->cartService->removeProduct($ProductClass, $user_imei);
  206.                 break;
  207.         }
  208.         // カートを取得して明細の正規化を実行
  209.         $Carts = $this->cartService->getCarts();
  210.         $this->execPurchaseFlow($Carts);
  211.         log_info('カート演算処理終了', ['operation' => $operation, 'product_class_id' => $productClassId]);
  212.         return $this->redirectToRoute('cart');
  213.     }
  214.     /**
  215.      * カートをロック状態に設定し、購入確認画面へ遷移する.
  216.      *
  217.      * @Route("/cart/buystep/{cart_key}", name="cart_buystep", requirements={"cart_key" = "[a-zA-Z0-9]+[_][\x20-\x7E]+"}, methods={"GET"})
  218.      */
  219.     public function buystep(Request $request, $cart_key)
  220.     {
  221.         $Carts = $this->cartService->getCart();
  222.         if (!is_object($Carts)) {
  223.             return $this->redirectToRoute('cart');
  224.         }
  225.         if ($this->cartHasMissingUserImei($Carts)) {
  226.             return $this->redirectToRoute('cart');
  227.         }
  228.         // FRONT_CART_BUYSTEP_INITIALIZE
  229.         $event = new EventArgs(
  230.             [],
  231.             $request
  232.         );
  233.         $this->eventDispatcher->dispatch($event, EccubeEvents::FRONT_CART_BUYSTEP_INITIALIZE);
  234.         $this->cartService->setPrimary($cart_key);
  235.         $this->cartService->save();
  236.         // FRONT_CART_BUYSTEP_COMPLETE
  237.         $event = new EventArgs(
  238.             [],
  239.             $request
  240.         );
  241.         $this->eventDispatcher->dispatch($event, EccubeEvents::FRONT_CART_BUYSTEP_COMPLETE);
  242.         if ($event->hasResponse()) {
  243.             return $event->getResponse();
  244.         }
  245.         return $this->redirectToRoute('shopping');
  246.     }
  247.     /**
  248.      * IMEIが必要な商品でIMEI未入力があるか判定する.
  249.      *
  250.      * @param \Eccube\Entity\Cart|\Eccube\Entity\Cart[] $Carts
  251.      *
  252.      * @return bool
  253.      */
  254.     protected function cartHasMissingUserImei($Carts): bool
  255.     {
  256.         if (!is_array($Carts)) {
  257.             $Carts = $Carts ? [$Carts] : [];
  258.         }
  259.         foreach ($Carts as $Cart) {
  260.             foreach ($Cart->getCartItems() as $CartItem) {
  261.                 $ProductClass = $CartItem->getProductClass();
  262.                 $Product = $ProductClass ? $ProductClass->getProduct() : null;
  263.                 if ($Product && $Product->hasUserImeiCategory() && trim((string) $CartItem->getUserImei()) === '') {
  264.                     return true;
  265.                 }
  266.             }
  267.         }
  268.         return false;
  269.     }
  270. }