src/Eccube/Controller/ProductController.php line 150

Open in your IDE?
  1. <?php
  2. namespace Eccube\Controller;
  3. use Eccube\Controller\AbstractController;
  4. use Eccube\Entity\BaseInfo;
  5. use Eccube\Entity\Master\ProductStatus;
  6. use Eccube\Entity\Product;
  7. use Eccube\Event\EccubeEvents;
  8. use Eccube\Event\EventArgs;
  9. use Eccube\Form\Type\AddCartType;
  10. use Eccube\Form\Type\SearchProductType;
  11. use Eccube\Repository\BaseInfoRepository;
  12. use Eccube\Repository\CustomerFavoriteProductRepository;
  13. use Eccube\Repository\Master\ProductListMaxRepository;
  14. use Eccube\Repository\ProductRepository;
  15. use Eccube\Repository\CategoryRepository;
  16. use Eccube\Service\CartService;
  17. use Eccube\Service\PurchaseFlow\PurchaseContext;
  18. use Eccube\Service\PurchaseFlow\PurchaseFlow;
  19. use Knp\Bundle\PaginatorBundle\Pagination\SlidingPagination;
  20. use Knp\Component\Pager\PaginatorInterface;
  21. use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
  22. use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
  23. use Symfony\Component\HttpFoundation\Request;
  24. use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
  25. use Symfony\Component\Routing\Annotation\Route;
  26. use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
  27. use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
  28. use Plugin\ProductPlus42\Repository\ProductDataDetailRepository;
  29. use Plugin\CustomerRank42\Repository\CustomerPriceRepository;
  30. use Plugin\PiaProductRestock\Repository\ProductRestockRepository;
  31. use Plugin\PiaProductRestock\Repository\ProductRestockCustomerRepository;
  32. class ProductController extends AbstractController
  33. {
  34.   /**
  35.    * @var PurchaseFlow
  36.    */
  37.   protected $purchaseFlow;
  38.   /**
  39.    * @var CustomerFavoriteProductRepository
  40.    */
  41.   protected $customerFavoriteProductRepository;
  42.   /**
  43.    * @var CartService
  44.    */
  45.   protected $cartService;
  46.   /**
  47.    * @var ProductRepository
  48.    */
  49.   protected $productRepository;
  50.   /**
  51.    * @var CategoryRepository
  52.    */
  53.   protected $categoryRepository;
  54.   /**
  55.    * @var BaseInfo
  56.    */
  57.   protected $BaseInfo;
  58.   /**
  59.    * @var AuthenticationUtils
  60.    */
  61.   protected $helper;
  62.   /**
  63.    * @var ProductListMaxRepository
  64.    */
  65.   protected $productListMaxRepository;
  66.   private $title '';
  67.   /**
  68.    * @var ProductDataDetailRepository
  69.    */
  70.   protected $productDataDetailRepository;
  71.   /**
  72.    * @var CustomerPriceRepository
  73.    */
  74.   protected $customerPriceRepository;
  75.   /**
  76.    * @var ProductRestockRepository
  77.    */
  78.   protected $productRestockRepository;
  79.   /**
  80.    * @var ProductRestockCustomerRepository
  81.    */
  82.   protected $productRestockCustomerRepository;
  83.   /**
  84.    * ProductController constructor.
  85.    *
  86.    * @param PurchaseFlow $cartPurchaseFlow
  87.    * @param CustomerFavoriteProductRepository $customerFavoriteProductRepository
  88.    * @param CartService $cartService
  89.    * @param ProductRepository $productRepository
  90.    * @param CategoryRepository $categoryRepository
  91.    * @param BaseInfoRepository $baseInfoRepository
  92.    * @param AuthenticationUtils $helper
  93.    * @param ProductListMaxRepository $productListMaxRepository
  94.    * @param ProductDataDetailRepository $ProductDataDetailRepository
  95.    * @param CustomerPriceRepository $customerPriceRepository
  96.    * @param ProductRestockRepository $productRestockRepository
  97.    * @param ProductRestockCustomerRepository $productRestockCustomerRepository
  98.    */
  99.   public function __construct(
  100.     PurchaseFlow                      $cartPurchaseFlow,
  101.     CustomerFavoriteProductRepository $customerFavoriteProductRepository,
  102.     CartService                       $cartService,
  103.     ProductRepository                 $productRepository,
  104.     CategoryRepository $categoryRepository,
  105.     BaseInfoRepository                $baseInfoRepository,
  106.     AuthenticationUtils               $helper,
  107.     ProductListMaxRepository          $productListMaxRepository,
  108.     ProductDataDetailRepository       $ProductDataDetailRepository,
  109.     CustomerPriceRepository $customerPriceRepository,
  110.     ProductRestockRepository $productRestockRepository,
  111.     ProductRestockCustomerRepository $productRestockCustomerRepository
  112.   )
  113.   {
  114.     $this->purchaseFlow $cartPurchaseFlow;
  115.     $this->customerFavoriteProductRepository $customerFavoriteProductRepository;
  116.     $this->cartService $cartService;
  117.     $this->productRepository $productRepository;
  118.     $this->categoryRepository $categoryRepository;
  119.     $this->BaseInfo $baseInfoRepository->get();
  120.     $this->helper $helper;
  121.     $this->productListMaxRepository $productListMaxRepository;
  122.     $this->productDataDetailRepository $ProductDataDetailRepository;
  123.     $this->customerPriceRepository $customerPriceRepository;
  124.     $this->productRestockRepository $productRestockRepository;
  125.     $this->productRestockCustomerRepository $productRestockCustomerRepository;
  126.   }
  127.   /**
  128.    * 商品一覧画面.
  129.    *
  130.    * @Route("/products/list", name="product_list", methods={"GET"})
  131.    * @Template("Product/list.twig")
  132.    */
  133.   public function index(Request $requestPaginatorInterface $paginator)
  134.   {
  135.     $this->session->set('header_footer_layout'"palace");
  136.     // Doctrine SQLFilter
  137.     if ($this->BaseInfo->isOptionNostockHidden()) {
  138.       $this->entityManager->getFilters()->enable('option_nostock_hidden');
  139.     }
  140.     // handleRequestは空のqueryの場合は無視するため
  141.     if ($request->getMethod() === 'GET') {
  142.       $request->query->set('pageno'$request->query->get('pageno'''));
  143.     }
  144.     // searchForm
  145.     /* @var $builder \Symfony\Component\Form\FormBuilderInterface */
  146.     $builder $this->formFactory->createNamedBuilder(''SearchProductType::class);
  147.     if ($request->getMethod() === 'GET') {
  148.       $builder->setMethod('GET');
  149.     }
  150.     $event = new EventArgs(
  151.       [
  152.         'builder' => $builder,
  153.       ],
  154.       $request
  155.     );
  156.     $this->eventDispatcher->dispatch($eventEccubeEvents::FRONT_PRODUCT_INDEX_INITIALIZE);
  157.     /* @var $searchForm \Symfony\Component\Form\FormInterface */
  158.     $searchForm $builder->getForm();
  159.     $searchForm->handleRequest($request);
  160.     // paginator
  161.     $searchData $searchForm->getData();
  162.     $qb $this->productRepository->getQueryBuilderBySearchData($searchData);
  163.     $event = new EventArgs(
  164.       [
  165.         'searchData' => $searchData,
  166.         'qb' => $qb,
  167.       ],
  168.       $request
  169.     );
  170.     $this->eventDispatcher->dispatch($eventEccubeEvents::FRONT_PRODUCT_INDEX_SEARCH);
  171.     $searchData $event->getArgument('searchData');
  172.     $query $qb->getQuery()
  173.       ->useResultCache(true$this->eccubeConfig['eccube_result_cache_lifetime_short']);
  174.     /** @var SlidingPagination $pagination */
  175.     $pagination $paginator->paginate(
  176.       $query,
  177.       !empty($searchData['pageno']) ? $searchData['pageno'] : 1,
  178.       !empty($searchData['disp_number']) ? $searchData['disp_number']->getId() : $this->productListMaxRepository->findOneBy([], ['sort_no' => 'ASC'])->getId()
  179.     );
  180.     $ids = [];
  181.     foreach ($pagination as $Product) {
  182.       $ids[] = $Product->getId();
  183.     }
  184.     $ProductsAndClassCategories $this->productRepository->findProductsWithSortedClassCategories($ids'p.id');
  185.     // addCart form
  186.     $forms = [];
  187.     foreach ($pagination as $Product) {
  188.       /* @var $builder \Symfony\Component\Form\FormBuilderInterface */
  189.       $builder $this->formFactory->createNamedBuilder(
  190.         '',
  191.         AddCartType::class,
  192.         null,
  193.         [
  194.           'product' => $ProductsAndClassCategories[$Product->getId()],
  195.           'allow_extra_fields' => true,
  196.         ]
  197.       );
  198.       $addCartForm $builder->getForm();
  199.       $forms[$Product->getId()] = $addCartForm->createView();
  200.     }
  201.     $Category $searchForm->get('category_id')->getData();
  202.     $category_id = empty($_GET["category_id"]) ? "" intval($_GET["category_id"]);
  203.     return [
  204.       'subtitle' => $this->getPageTitle($searchData),
  205.       'pagination' => $pagination,
  206.       'search_form' => $searchForm->createView(),
  207.       'forms' => $forms,
  208.       'Category' => $Category,
  209.         'category_id' => $category_id,
  210.     ];
  211.   }
  212.   /**
  213.    * 商品詳細画面.
  214.    *
  215.    * @Route("/products/detail/{id}", name="product_detail", methods={"GET"}, requirements={"id" = "\d+"})
  216.    * @Template("Product/detail.twig")
  217.    * @ParamConverter("Product", options={"repository_method" = "findWithSortedClassCategories"})
  218.    *
  219.    * @param Request $request
  220.    * @param Product $Product
  221.    *
  222.    * @return array
  223.    */
  224.   public function detail(Request $requestProduct $Product)
  225.   {
  226.     $this->session->set('header_footer_layout'"palace");
  227.     if (!$this->checkVisibility($Product)) {
  228.       throw new NotFoundHttpException();
  229.     }
  230.     // ============================================================
  231.     // 限定公開URLチェック(column_id: 66)
  232.     // ============================================================
  233.     $product_id $Product->getId();
  234.     $genteiValue $this->productDataDetailRepository->getObjectFromProductId($product_id"66");
  235.     if (!empty($genteiValue)) {
  236.       $d_id $request->query->get('d_id');
  237.       if (empty($d_id) || $d_id !== $genteiValue) {
  238.         return $this->render('common_error.twig', [
  239.           'error_title'   => '限定公開商品',
  240.           'error_message' => 'この商品は限定公開商品です。現在購入いただけません。',
  241.           'Product'       => $Product,
  242.         ]);
  243.       }
  244.     }
  245.     $builder $this->formFactory->createNamedBuilder(
  246.       '',
  247.       AddCartType::class,
  248.       null,
  249.       [
  250.         'product' => $Product,
  251.         'id_add_product_id' => false,
  252.       ]
  253.     );
  254.     $event = new EventArgs(
  255.       [
  256.         'builder' => $builder,
  257.         'Product' => $Product,
  258.       ],
  259.       $request
  260.     );
  261.     $this->eventDispatcher->dispatch($eventEccubeEvents::FRONT_PRODUCT_DETAIL_INITIALIZE);
  262.     $is_favorite false;
  263.     if ($this->isGranted('ROLE_USER')) {
  264.       $Customer $this->getUser();
  265.       $is_favorite $this->customerFavoriteProductRepository->isFavorite($Customer$Product);
  266.     }
  267.     // 商品種別を取得
  268.     $ProductClasses $Product->getProductClasses();
  269.     $product_type_id "";
  270.     foreach ($ProductClasses as $class) {
  271.       $type $class->getSaleType();
  272.       $product_type_id $type->getId();
  273.     }
  274.     // 商品の拡張項目を取得
  275.     $product_id $Product->getId();
  276.     $htEx $this->productDataDetailRepository->getHashFromProductId($product_id);
  277.     // 地金カテゴリーの確認
  278.     $jigane_flg $this->categoryRepository->hasCategory($product_id"5");
  279.     // 会員価格を表示
  280.     $member_price $this->customerPriceRepository->getPriceFromProductId($product_id);
  281.     // 再入荷通知情報を取得
  282.     $restock $this->productRestockRepository->findByProductId($product_id);
  283.     $restock_disp_flg $restock $restock->getDispFlg() : 0;
  284.     // 在庫がある場合は再入荷ボタンを非表示
  285.     $stock 0;
  286.     foreach ($Product->getProductClasses() as $productClass) {
  287.       if ($productClass->isVisible()) {
  288.         $stock += $productClass->getStock();
  289.       }
  290.     }
  291.     if ($stock 0) {
  292.       $restock_disp_flg 0;
  293.     }
  294.     // ログインユーザーの再入荷登録状況
  295.     $restock_regist_flg 2// 未登録
  296.     if ($this->isGranted('ROLE_USER') && $restock) {
  297.       $customer $this->getUser();
  298.       $isRegistered $this->productRestockCustomerRepository->isRegistered(
  299.         $restock->getId(),
  300.         $customer->getId()
  301.       );
  302.       $restock_regist_flg $isRegistered 2;
  303.     }
  304.     return [
  305.       'title' => $this->title,
  306.       'subtitle' => $Product->getName(),
  307.       'form' => $builder->getForm()->createView(),
  308.       'Product' => $Product,
  309.       'is_favorite' => $is_favorite,
  310.       'product_type_id' => $product_type_id,
  311.       'htEx' => $htEx,
  312.       'jigane_flg' => $jigane_flg,
  313.       'member_price' => $member_price,
  314.       'restock_disp_flg' => $restock_disp_flg,
  315.       'restock_regist_flg' => $restock_regist_flg,
  316.     ];
  317.   }
  318.   /**
  319.    * お気に入り追加.
  320.    *
  321.    * @Route("/products/add_favorite/{id}", name="product_add_favorite", requirements={"id" = "\d+"}, methods={"GET", "POST"})
  322.    */
  323.   public function addFavorite(Request $requestProduct $Product)
  324.   {
  325.     $this->checkVisibility($Product);
  326.     $event = new EventArgs(
  327.       [
  328.         'Product' => $Product,
  329.       ],
  330.       $request
  331.     );
  332.     $this->eventDispatcher->dispatch($eventEccubeEvents::FRONT_PRODUCT_FAVORITE_ADD_INITIALIZE);
  333.     if ($this->isGranted('ROLE_USER')) {
  334.       $Customer $this->getUser();
  335.       $this->customerFavoriteProductRepository->addFavorite($Customer$Product);
  336.       $this->session->getFlashBag()->set('product_detail.just_added_favorite'$Product->getId());
  337.       $event = new EventArgs(
  338.         [
  339.           'Product' => $Product,
  340.         ],
  341.         $request
  342.       );
  343.       $this->eventDispatcher->dispatch($eventEccubeEvents::FRONT_PRODUCT_FAVORITE_ADD_COMPLETE);
  344.       return $this->redirectToRoute('product_detail', ['id' => $Product->getId()]);
  345.     } else {
  346.       // 非会員の場合、ログイン画面を表示
  347.       //  ログイン後の画面遷移先を設定
  348.       $this->setLoginTargetPath($this->generateUrl('product_add_favorite', ['id' => $Product->getId()], UrlGeneratorInterface::ABSOLUTE_URL));
  349.       $this->session->getFlashBag()->set('eccube.add.favorite'true);
  350.       $event = new EventArgs(
  351.         [
  352.           'Product' => $Product,
  353.         ],
  354.         $request
  355.       );
  356.       $this->eventDispatcher->dispatch($eventEccubeEvents::FRONT_PRODUCT_FAVORITE_ADD_COMPLETE);
  357.       return $this->redirectToRoute('mypage_login');
  358.     }
  359.   }
  360.   /**
  361.    * カートに追加.
  362.    *
  363.    * @Route("/products/add_cart/{id}", name="product_add_cart", methods={"POST"}, requirements={"id" = "\d+"})
  364.    */
  365.   public function addCart(Request $requestProduct $Product)
  366.   {
  367.     // エラーメッセージの配列
  368.     $errorMessages = [];
  369.     if (!$this->checkVisibility($Product)) {
  370.       throw new NotFoundHttpException();
  371.     }
  372.     $builder $this->formFactory->createNamedBuilder(
  373.       '',
  374.       AddCartType::class,
  375.       null,
  376.       [
  377.         'product' => $Product,
  378.         'id_add_product_id' => false,
  379.       ]
  380.     );
  381.     $event = new EventArgs(
  382.       [
  383.         'builder' => $builder,
  384.         'Product' => $Product,
  385.       ],
  386.       $request
  387.     );
  388.     $this->eventDispatcher->dispatch($eventEccubeEvents::FRONT_PRODUCT_CART_ADD_INITIALIZE);
  389.     /* @var $form \Symfony\Component\Form\FormInterface */
  390.     $form $builder->getForm();
  391.     $form->handleRequest($request);
  392.     if (!$form->isValid()) {
  393.       throw new NotFoundHttpException();
  394.     }
  395.     $addCartData $form->getData();
  396.     log_info(
  397.       'カート追加処理開始',
  398.       [
  399.         'product_id' => $Product->getId(),
  400.         'product_class_id' => $addCartData['product_class_id'],
  401.         'quantity' => $addCartData['quantity'],
  402.       ]
  403.     );
  404.     // オーナーチェック
  405.     if ($this->isGranted('ROLE_USER')) {
  406.       $product_id $Product->getId();
  407.       $owner_id $this->productDataDetailRepository->getObjectFromProductId($product_id"6");
  408.       $user_id $this->getUser()->getId();
  409.       if($owner_id == $user_id){
  410.         $this->addRequestError("お客様が出品中のコインは購入する事が出来ません。");
  411.         return $this->redirectToRoute('cart');
  412.       }
  413.     }
  414.     // カートへ追加
  415.     $this->cartService->addProduct($addCartData['product_class_id'], $addCartData['quantity']);
  416.     // 明細の正規化
  417.     $Carts $this->cartService->getCarts();
  418.     // 同時購入できない商品がある場合
  419.     if(count($Carts) >= 2){
  420.       $product_id $Product->getId();
  421.       foreach ($Carts as $Cart) {
  422.         foreach ($Cart->getCartItems() as $CartItem) {
  423.           $ProductClass $CartItem->getProductClass();
  424.           $NewProduct $ProductClass->getProduct();
  425.           $new_product_id $NewProduct->getId();
  426.           if($new_product_id != $product_id){
  427.             $this->cartService->removeProduct($ProductClass);
  428.           }
  429.         }
  430.       }
  431.     }
  432.     $Carts $this->cartService->getCarts();
  433.     foreach ($Carts as $Cart) {
  434.       $result $this->purchaseFlow->validate($Cart, new PurchaseContext($Cart$this->getUser()));
  435.       // 復旧不可のエラーが発生した場合は追加した明細を削除.
  436.       if ($result->hasError()) {
  437.         $this->cartService->removeProduct($addCartData['product_class_id']);
  438.         foreach ($result->getErrors() as $error) {
  439.           $errorMessages[] = $error->getMessage();
  440.         }
  441.       }
  442.       foreach ($result->getWarning() as $warning) {
  443.         $errorMessages[] = $warning->getMessage();
  444.       }
  445.     }
  446.     $this->cartService->save();
  447.     log_info(
  448.       'カート追加処理完了',
  449.       [
  450.         'product_id' => $Product->getId(),
  451.         'product_class_id' => $addCartData['product_class_id'],
  452.         'quantity' => $addCartData['quantity'],
  453.       ]
  454.     );
  455.     $event = new EventArgs(
  456.       [
  457.         'form' => $form,
  458.         'Product' => $Product,
  459.       ],
  460.       $request
  461.     );
  462.     $this->eventDispatcher->dispatch($eventEccubeEvents::FRONT_PRODUCT_CART_ADD_COMPLETE);
  463.     if ($event->getResponse() !== null) {
  464.       return $event->getResponse();
  465.     }
  466.     if ($request->isXmlHttpRequest()) {
  467.       // ajaxでのリクエストの場合は結果をjson形式で返す。
  468.       // 初期化
  469.       $messages = [];
  470.       if (empty($errorMessages)) {
  471.         // エラーが発生していない場合
  472.         $done true;
  473.         array_push($messagestrans('front.product.add_cart_complete'));
  474.       } else {
  475.         // エラーが発生している場合
  476.         $done false;
  477.         $messages $errorMessages;
  478.       }
  479.       return $this->json(['done' => $done'messages' => $messages]);
  480.     } else {
  481.       // ajax以外でのリクエストの場合はカート画面へリダイレクト
  482.       foreach ($errorMessages as $errorMessage) {
  483.         $this->addRequestError($errorMessage);
  484.       }
  485.       return $this->redirectToRoute('cart');
  486.     }
  487.   }
  488.   /**
  489.    * ページタイトルの設定
  490.    *
  491.    * @param array|null $searchData
  492.    *
  493.    * @return str
  494.    */
  495.   protected function getPageTitle($searchData)
  496.   {
  497.     if (isset($searchData['name']) && !empty($searchData['name'])) {
  498.       return trans('front.product.search_result');
  499.     } elseif (isset($searchData['category_id']) && $searchData['category_id']) {
  500.       return $searchData['category_id']->getName();
  501.     } else {
  502.       return trans('front.product.all_products');
  503.     }
  504.   }
  505.   /**
  506.    * 閲覧可能な商品かどうかを判定
  507.    *
  508.    * @param Product $Product
  509.    *
  510.    * @return boolean 閲覧可能な場合はtrue
  511.    */
  512.   protected function checkVisibility(Product $Product)
  513.   {
  514.     $is_admin $this->session->has('_security_admin');
  515.     // 管理ユーザの場合はステータスやオプションにかかわらず閲覧可能.
  516.     if (!$is_admin) {
  517.       // 削除済み商品は表示しない
  518.       if ($Product->getDelFlg() === 1) {
  519.         return false;
  520.       }
  521.       // 在庫なし商品の非表示オプションが有効な場合.
  522.       // if ($this->BaseInfo->isOptionNostockHidden()) {
  523.       //     if (!$Product->getStockFind()) {
  524.       //         return false;
  525.       //     }
  526.       // }
  527.       // 表示可能なステータス: 1(公開), 6(取り置き), 8(予約品)
  528.       $statusId $Product->getStatus()->getId();
  529.       if (!in_array($statusId, [
  530.         ProductStatus::DISPLAY_SHOW,  // 1: 公開
  531.         6,  // 取り置き(公開)
  532.         8,  // 予約品(公開)
  533.       ])) {
  534.         return false;
  535.       }
  536.     }
  537.     return true;
  538.   }
  539. }