src/Eccube/Controller/ProductController.php line 261

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.       // パレスとプラザの戻り先を変更
  345.       $product_id $Product->getId();
  346.       $productClasses $Product->getProductClasses();
  347.       $plaza_flg false;
  348.       foreach($productClasses as $ProductClass) {
  349.         $sale_type_id $ProductClass->getSaleType()->getId();
  350.         if($sale_type_id == "2"){
  351.           $plaza_flg true;
  352.         }
  353.       }
  354.       if($plaza_flg === true){
  355.         return $this->redirectToRoute('plaza_product_detail', ['id' => $product_id]);
  356.       }else{
  357.         return $this->redirectToRoute('product_detail', ['id' => $product_id]);
  358.       }
  359.     } else {
  360.       // 非会員の場合、ログイン画面を表示
  361.       //  ログイン後の画面遷移先を設定
  362.       $this->setLoginTargetPath($this->generateUrl('product_add_favorite', ['id' => $Product->getId()], UrlGeneratorInterface::ABSOLUTE_URL));
  363.       $this->session->getFlashBag()->set('eccube.add.favorite'true);
  364.       $event = new EventArgs(
  365.         [
  366.           'Product' => $Product,
  367.         ],
  368.         $request
  369.       );
  370.       $this->eventDispatcher->dispatch($eventEccubeEvents::FRONT_PRODUCT_FAVORITE_ADD_COMPLETE);
  371.       return $this->redirectToRoute('mypage_login');
  372.     }
  373.   }
  374.   /**
  375.    * カートに追加.
  376.    *
  377.    * @Route("/products/add_cart/{id}", name="product_add_cart", methods={"POST"}, requirements={"id" = "\d+"})
  378.    */
  379.   public function addCart(Request $requestProduct $Product)
  380.   {
  381.     // エラーメッセージの配列
  382.     $errorMessages = [];
  383.     if (!$this->checkVisibility($Product)) {
  384.       throw new NotFoundHttpException();
  385.     }
  386.     $builder $this->formFactory->createNamedBuilder(
  387.       '',
  388.       AddCartType::class,
  389.       null,
  390.       [
  391.         'product' => $Product,
  392.         'id_add_product_id' => false,
  393.       ]
  394.     );
  395.     $event = new EventArgs(
  396.       [
  397.         'builder' => $builder,
  398.         'Product' => $Product,
  399.       ],
  400.       $request
  401.     );
  402.     $this->eventDispatcher->dispatch($eventEccubeEvents::FRONT_PRODUCT_CART_ADD_INITIALIZE);
  403.     /* @var $form \Symfony\Component\Form\FormInterface */
  404.     $form $builder->getForm();
  405.     $form->handleRequest($request);
  406.     if (!$form->isValid()) {
  407.       throw new NotFoundHttpException();
  408.     }
  409.     $addCartData $form->getData();
  410.     log_info(
  411.       'カート追加処理開始',
  412.       [
  413.         'product_id' => $Product->getId(),
  414.         'product_class_id' => $addCartData['product_class_id'],
  415.         'quantity' => $addCartData['quantity'],
  416.       ]
  417.     );
  418.     // オーナーチェック
  419.     if ($this->isGranted('ROLE_USER')) {
  420.       $product_id $Product->getId();
  421.       $owner_id $this->productDataDetailRepository->getObjectFromProductId($product_id"6");
  422.       $user_id $this->getUser()->getId();
  423.       if($owner_id == $user_id){
  424.         $this->addRequestError("お客様が出品中のコインは購入する事が出来ません。");
  425.         return $this->redirectToRoute('cart');
  426.       }
  427.     }
  428.     // カートへ追加
  429.     $this->cartService->addProduct($addCartData['product_class_id'], $addCartData['quantity']);
  430.     // 明細の正規化
  431.     $Carts $this->cartService->getCarts();
  432.     // 同時購入できない商品がある場合
  433.     if(count($Carts) >= 2){
  434.       $product_id $Product->getId();
  435.       foreach ($Carts as $Cart) {
  436.         foreach ($Cart->getCartItems() as $CartItem) {
  437.           $ProductClass $CartItem->getProductClass();
  438.           $NewProduct $ProductClass->getProduct();
  439.           $new_product_id $NewProduct->getId();
  440.           if($new_product_id != $product_id){
  441.             $this->cartService->removeProduct($ProductClass);
  442.           }
  443.         }
  444.       }
  445.     }
  446.     $Carts $this->cartService->getCarts();
  447.     foreach ($Carts as $Cart) {
  448.       $result $this->purchaseFlow->validate($Cart, new PurchaseContext($Cart$this->getUser()));
  449.       // 復旧不可のエラーが発生した場合は追加した明細を削除.
  450.       if ($result->hasError()) {
  451.         $this->cartService->removeProduct($addCartData['product_class_id']);
  452.         foreach ($result->getErrors() as $error) {
  453.           $errorMessages[] = $error->getMessage();
  454.         }
  455.       }
  456.       foreach ($result->getWarning() as $warning) {
  457.         $errorMessages[] = $warning->getMessage();
  458.       }
  459.     }
  460.     $this->cartService->save();
  461.     log_info(
  462.       'カート追加処理完了',
  463.       [
  464.         'product_id' => $Product->getId(),
  465.         'product_class_id' => $addCartData['product_class_id'],
  466.         'quantity' => $addCartData['quantity'],
  467.       ]
  468.     );
  469.     $event = new EventArgs(
  470.       [
  471.         'form' => $form,
  472.         'Product' => $Product,
  473.       ],
  474.       $request
  475.     );
  476.     $this->eventDispatcher->dispatch($eventEccubeEvents::FRONT_PRODUCT_CART_ADD_COMPLETE);
  477.     if ($event->getResponse() !== null) {
  478.       return $event->getResponse();
  479.     }
  480.     if ($request->isXmlHttpRequest()) {
  481.       // ajaxでのリクエストの場合は結果をjson形式で返す。
  482.       // 初期化
  483.       $messages = [];
  484.       if (empty($errorMessages)) {
  485.         // エラーが発生していない場合
  486.         $done true;
  487.         array_push($messagestrans('front.product.add_cart_complete'));
  488.       } else {
  489.         // エラーが発生している場合
  490.         $done false;
  491.         $messages $errorMessages;
  492.       }
  493.       return $this->json(['done' => $done'messages' => $messages]);
  494.     } else {
  495.       // ajax以外でのリクエストの場合はカート画面へリダイレクト
  496.       foreach ($errorMessages as $errorMessage) {
  497.         $this->addRequestError($errorMessage);
  498.       }
  499.       return $this->redirectToRoute('cart');
  500.     }
  501.   }
  502.   /**
  503.    * ページタイトルの設定
  504.    *
  505.    * @param array|null $searchData
  506.    *
  507.    * @return str
  508.    */
  509.   protected function getPageTitle($searchData)
  510.   {
  511.     if (isset($searchData['name']) && !empty($searchData['name'])) {
  512.       return trans('front.product.search_result');
  513.     } elseif (isset($searchData['category_id']) && $searchData['category_id']) {
  514.       return $searchData['category_id']->getName();
  515.     } else {
  516.       return trans('front.product.all_products');
  517.     }
  518.   }
  519.   /**
  520.    * 閲覧可能な商品かどうかを判定
  521.    *
  522.    * @param Product $Product
  523.    *
  524.    * @return boolean 閲覧可能な場合はtrue
  525.    */
  526.   protected function checkVisibility(Product $Product)
  527.   {
  528.     $is_admin $this->session->has('_security_admin');
  529.     // 管理ユーザの場合はステータスやオプションにかかわらず閲覧可能.
  530.     if (!$is_admin) {
  531.       // 削除済み商品は表示しない
  532.       if ($Product->getDelFlg() === 1) {
  533.         return false;
  534.       }
  535.       // 在庫なし商品の非表示オプションが有効な場合.
  536.       // if ($this->BaseInfo->isOptionNostockHidden()) {
  537.       //     if (!$Product->getStockFind()) {
  538.       //         return false;
  539.       //     }
  540.       // }
  541.       // 表示可能なステータス: 1(公開), 6(取り置き), 8(予約品)
  542.       $statusId $Product->getStatus()->getId();
  543.       if (!in_array($statusId, [
  544.         ProductStatus::DISPLAY_SHOW,  // 1: 公開
  545.         6,  // 取り置き(公開)
  546.         8,  // 予約品(公開)
  547.       ])) {
  548.         return false;
  549.       }
  550.     }
  551.     return true;
  552.   }
  553. }