app/Customize/Controller/ProductController.php line 1077

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 Customize\Controller;
  13. use Customize\Form\Type\FilterProductType;
  14. use Customize\Repository\OrderRepository;
  15. use Doctrine\ORM\NonUniqueResultException;
  16. use Doctrine\ORM\NoResultException;
  17. use Eccube\Entity\BaseInfo;
  18. use Eccube\Entity\CustomerFavoriteProduct;
  19. use Eccube\Entity\Master\ProductStatus;
  20. use Eccube\Entity\Product;
  21. use Eccube\Entity\ProductClass;
  22. use Eccube\Event\EccubeEvents;
  23. use Eccube\Event\EventArgs;
  24. use Customize\Form\Type\AddCartType;
  25. use Customize\Form\Type\AddCartSubscriptionType;
  26. use Customize\Form\Type\AddCartModalType;
  27. use Customize\Form\Type\Front\HistoryAddCartType;
  28. use Eccube\Form\Type\SearchProductType;
  29. use Eccube\Repository\BaseInfoRepository;
  30. use Customize\Repository\CategoryRepository;
  31. use Eccube\Repository\CustomerFavoriteProductRepository;
  32. use Customize\Repository\CustomerRepository;
  33. use Eccube\Repository\Master\ProductListMaxRepository;
  34. use Customize\Repository\SpecialArticleRepository;
  35. use Customize\Repository\ProductRepository;
  36. use Eccube\Repository\TaxRuleRepository;
  37. use Eccube\Service\CartService;
  38. use Eccube\Service\PurchaseFlow\PurchaseContext;
  39. use Eccube\Service\PurchaseFlow\PurchaseFlow;
  40. use Eccube\Service\TaxRuleService;
  41. use Eccube\Util\FormUtil;
  42. use Knp\Bundle\PaginatorBundle\Pagination\SlidingPagination;
  43. use Knp\Component\Pager\PaginatorInterface;
  44. use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
  45. use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
  46. use Symfony\Component\HttpFoundation\JsonResponse;
  47. use Symfony\Component\HttpFoundation\RedirectResponse;
  48. use Symfony\Component\HttpFoundation\Request;
  49. use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
  50. use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
  51. use Symfony\Component\Routing\Annotation\Route;
  52. use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
  53. use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
  54. use Eccube\Controller\AbstractController;
  55. class ProductController extends AbstractController
  56. {
  57.     /**
  58.      * @var PurchaseFlow
  59.      */
  60.     protected $purchaseFlow;
  61.     /**
  62.      * @var CustomerFavoriteProductRepository
  63.      */
  64.     protected $customerFavoriteProductRepository;
  65.     /**
  66.      * @var CartService
  67.      */
  68.     protected $cartService;
  69.     /**
  70.      * @var ProductRepository
  71.      */
  72.     protected $productRepository;
  73.     /**
  74.      * @var CustomerRepository
  75.      */
  76.     protected $customerRepository;
  77.     /**
  78.      * @var OrderRepository
  79.      */
  80.     protected $orderRepository;
  81.     /**
  82.      * @var BaseInfo
  83.      */
  84.     protected $BaseInfo;
  85.     /**
  86.      * @var AuthenticationUtils
  87.      */
  88.     protected $helper;
  89.     /**
  90.      * @var ProductListMaxRepository
  91.      */
  92.     protected $productListMaxRepository;
  93.     /**
  94.      * @var CategoryRepository
  95.      */
  96.     protected $categoryRepository;
  97.     /**
  98.      * @var TaxRuleRepository
  99.      */
  100.     protected $taxRuleRepository;
  101.     /**
  102.      * @var TaxRuleService
  103.      */
  104.     protected $taxRuleService;
  105.     private $title '';
  106.     /**
  107.      * @var SpecialArticleRepository
  108.      */
  109.     protected $specialArticleRepository;
  110.     /**
  111.      * ProductController constructor.
  112.      *
  113.      * @param PurchaseFlow $cartPurchaseFlow
  114.      * @param CustomerFavoriteProductRepository $customerFavoriteProductRepository
  115.      * @param CartService $cartService
  116.      * @param ProductRepository $productRepository
  117.      * @param BaseInfoRepository $baseInfoRepository
  118.      * @param AuthenticationUtils $helper
  119.      * @param ProductListMaxRepository $productListMaxRepository
  120.      * @param CategoryRepository $categoryRepository
  121.      * @param SpecialArticleRepository $specialArticleRepository
  122.      * @param CustomerRepository $customerRepository
  123.      * @param OrderRepository $orderRepository
  124.      * @param TaxRuleRepository $taxRuleRepository
  125.      * @param TaxRuleService $taxRuleService
  126.      */
  127.     public function __construct(
  128.         PurchaseFlow $cartPurchaseFlow,
  129.         CustomerFavoriteProductRepository $customerFavoriteProductRepository,
  130.         CartService $cartService,
  131.         ProductRepository $productRepository,
  132.         BaseInfoRepository $baseInfoRepository,
  133.         AuthenticationUtils $helper,
  134.         ProductListMaxRepository $productListMaxRepository,
  135.         CategoryRepository $categoryRepository,
  136.         SpecialArticleRepository $specialArticleRepository,
  137.         CustomerRepository $customerRepository,
  138.         OrderRepository $orderRepository,
  139.         TaxRuleRepository $taxRuleRepository,
  140.         TaxRuleService $taxRuleService
  141.     ) {
  142.         $this->purchaseFlow $cartPurchaseFlow;
  143.         $this->customerFavoriteProductRepository $customerFavoriteProductRepository;
  144.         $this->cartService $cartService;
  145.         $this->productRepository $productRepository;
  146.         $this->BaseInfo $baseInfoRepository->get();
  147.         $this->helper $helper;
  148.         $this->productListMaxRepository $productListMaxRepository;
  149.         $this->categoryRepository $categoryRepository;
  150.         $this->specialArticleRepository $specialArticleRepository;
  151.         $this->customerRepository $customerRepository;
  152.         $this->orderRepository $orderRepository;
  153.         $this->taxRuleRepository $taxRuleRepository;
  154.         $this->taxRuleService $taxRuleService;
  155.     }
  156.     /**
  157.      * 商品一覧画面.
  158.      *
  159.      * @Route("/products/list/{category_id}", name="product_list", methods={"GET"}, requirements={"category_id" = "\d+"})
  160.      * @Route("/products/list/filter", name="product_list_filter", methods={"GET", "POST"})
  161.      * @Template("Product/list.twig")
  162.      */
  163.     public function index(Request $requestPaginatorInterface $paginator nullint $category_id nullstring $keyword null): array
  164.     {
  165.         $specialArticles $this->specialArticleRepository->getSortSpecialArticleProducts();
  166.         // is login?
  167.         $Customer $this->getUser();
  168.         // Doctrine SQLFilter
  169.         if ($this->BaseInfo->isOptionNostockHidden()) {
  170.             $this->entityManager->getFilters()->enable('option_nostock_hidden');
  171.         }
  172.         // handleRequestは空のqueryの場合は無視するため
  173.         if ($request->getMethod() === 'GET' ) {
  174.             $request->query->set('pageno'$request->query->get('pageno'''));
  175.         }
  176.         // 通常の商品一覧表示の場合セッションを空にする
  177.         if ($request->getPathInfo() !== $this->generateUrl('product_list_filter') && empty($category_id)) {
  178.             $this->session->remove('eccube.product.filter');
  179.             $this->session->remove('eccube.product.category');
  180.         }
  181.         // searchForm
  182.         $builder $this->formFactory->createNamedBuilder(''SearchProductType::class);
  183.         if ($request->getMethod() === 'GET') {
  184.             $builder->setMethod('GET');
  185.         }
  186.         $event = new EventArgs(
  187.             [
  188.                 'builder' => $builder,
  189.             ],
  190.             $request
  191.         );
  192.         $this->eventDispatcher->dispatch($eventEccubeEvents::FRONT_PRODUCT_INDEX_INITIALIZE);
  193.         $searchForm $builder->getForm();
  194.         $searchForm->handleRequest($request);
  195.         // paginator
  196.         $searchData $searchForm->getData();
  197.         // 商品検索バーのキーワード設置
  198.         if(!is_null($request->query->get('keyword'))){
  199.             $searchData['name'] = $request->query->get('keyword');
  200.         }
  201.         // お悩みから検索
  202.         if (!is_null($request->query->get('concerns_id'))) {
  203.             $searchData['concerns_id'] = $request->query->get('concerns_id');
  204.         }
  205.         // 定期商品検索
  206.         if (!is_null($request->query->get('is_subscription'))) {
  207.             $searchData['is_subscription'] = $request->query->get('is_subscription');
  208.         }
  209.         // 商品フィルターフォーム
  210.         $filterBuilder $this->formFactory->createBuilder(FilterProductType::class);
  211.         $filterProductModalForm $filterBuilder->getForm();
  212.         $filterProductModalForm->handleRequest($request);
  213.         // 商品絞り込みの場合
  214.         if ($filterProductModalForm->isSubmitted()) {
  215.             $filterData $request->request->get('filter_product', []);
  216.             // セッションからカテゴリを取得
  217.             $filterData['category_id'] = $this->session->get('eccube.product.category');
  218.             // セッションに絞り込み条件を保存
  219.             $this->session->set('eccube.product.filter'$filterData);
  220.             $qb $this->productRepository->getQueryBuilderByFilterData($filterData);
  221.             $TargetCategory $this->categoryRepository->findOneby(['id' => $filterData['category_id']]);
  222.             $ChildCategory $TargetCategory $TargetCategory->getChildren() : $this->categoryRepository->selectByParentCategoryIdIsNull();
  223.         } elseif (!empty($category_id)) {
  224.             // セッションに検索カテゴリ条件を保持
  225.             $this->session->set('eccube.product.category'$category_id);
  226.             $Category $this->categoryRepository->find($category_id);
  227.             $searchData['category_id'] = $Category;
  228.             // 絞り込み条件が存在の場合
  229.             if (!empty($this->session->get('eccube.product.filter'))) {
  230.                 $filterData $this->session->get('eccube.product.filter', []);
  231.                 $filterData['category_id'] = $category_id;
  232.                 $qb $this->productRepository->getQueryBuilderByFilterData($filterData);
  233.             } else {
  234.                 $qb $this->productRepository->getQueryBuilderBySearchData($searchData);
  235.             }
  236.             $TargetCategory $Category;
  237.             $ChildCategory $TargetCategory->getChildren();
  238.         } elseif (!empty($this->session->get('eccube.product.filter'))) {
  239.             // セッションから検索条件を取得
  240.             $filterData $this->session->get('eccube.product.filter', []);
  241.             $filterProductModalForm->setData($filterData);
  242.             // セッションからカテゴリを取得
  243.             $filterData['category_id'] = $this->session->get('eccube.product.category');
  244.             $Category $this->categoryRepository->findOneby(['id' => $filterData['category_id']]);
  245.             foreach ($filterData as $key => $value) {
  246.                 if (is_array($value)) {
  247.                     $filterData[$key] = array_map('trim'$value);
  248.                 } else {
  249.                     $filterData[$key] = trim($value);
  250.                 }
  251.             }
  252.             $qb $this->productRepository->getQueryBuilderByFilterData($filterData);
  253.             $TargetCategory $Category;
  254.             $ChildCategory $TargetCategory $TargetCategory->getChildren() : $this->categoryRepository->selectByParentCategoryIdIsNull();
  255.             
  256.         } else {
  257.             $qb $this->productRepository->getQueryBuilderBySearchData($searchData);
  258.             $TargetCategory null;
  259.             $ChildCategory $this->categoryRepository->selectByParentCategoryIdIsNull();
  260.         }
  261.         $event = new EventArgs(
  262.             [
  263.                 'searchData' => $searchData,
  264.                 'qb' => $qb,
  265.             ],
  266.             $request
  267.         );
  268.         $this->eventDispatcher->dispatch($eventEccubeEvents::FRONT_PRODUCT_INDEX_SEARCH);
  269.         $searchData $event->getArgument('searchData');
  270.         $query $qb->getQuery()
  271.             ->enableResultCache(true$this->eccubeConfig['eccube_result_cache_lifetime_short']);
  272.         /** @var SlidingPagination $pagination */
  273.         $pagination $paginator->paginate(
  274.             $query,
  275.             !empty($searchData['pageno']) ? $searchData['pageno'] : 1,
  276.             !empty($searchData['disp_number']) ? $searchData['disp_number']->getId() : $this->productListMaxRepository->findOneBy([], ['sort_no' => 'ASC'])->getId(),
  277.             array('wrap-queries' => true)
  278.         );
  279.         $ids = [];
  280.         foreach ($pagination as $Product) {
  281.             $ids[] = $Product->getId();
  282.         }
  283.         $ProductsAndClassCategories $this->productRepository->findProductsWithSortedClassCategories($ids'p.id');
  284.         // addCart form
  285.         $forms = [];
  286.         $subforms = [];
  287.         $subProducts = [];
  288.         foreach ($pagination as $Product) {
  289.             $builder $this->formFactory->createNamedBuilder(
  290.                 '',
  291.                 AddCartType::class,
  292.                 null,
  293.                 [
  294.                     'product' => $ProductsAndClassCategories[$Product->getId()],
  295.                     'allow_extra_fields' => true,
  296.                 ]
  297.             );
  298.             $addCartForm $builder->getForm();
  299.             $forms[$Product->getId()] = $addCartForm->createView();
  300.             $data $this->productRepository->getProductByNormalId($Product->getId());
  301.             if($data) {
  302.                 $PlanProductsAndClassCategories $this->productRepository->findProductsWithSortedClassCategories([$data->getId()], 'p.id');
  303.                 $subProducts[$Product->getId()] = $data;
  304.                 $builder $this->formFactory->createNamedBuilder(
  305.                     '',
  306.                     AddCartType::class,
  307.                     null,
  308.                     [
  309.                         'product' => $PlanProductsAndClassCategories[$data->getId()],
  310.                         'allow_extra_fields' => true,
  311.                     ]
  312.                 );
  313.                 $addSubCartForm $builder->getForm();
  314.                 $subforms[$Product->getId()] = $addSubCartForm ->createView();
  315.             }
  316.         }
  317.         $Modalbuilder $this->formFactory->createNamedBuilder(
  318.             '',
  319.             AddCartModalType::class,
  320.             null,
  321.             [
  322.             ]
  323.         ); 
  324.         $form2 $Modalbuilder->getForm();
  325.         $Category $searchForm->get('category_id')->getData();
  326.         $specialRoute $this->generateUrl('special');
  327.         return [
  328.             'specialRoute' => $specialRoute,
  329.             'specialArticles' => $specialArticles,
  330.             'title' => $this->getPageTitle($searchData),
  331.             'subtitle' => $this->getPageTitle($searchData),
  332.             'pagination' => $pagination,
  333.             'subProducts' => $subProducts,
  334.             'search_form' => $searchForm->createView(),
  335.             'forms' => $forms,
  336.             'subforms' => $subforms,
  337.             'form2' => $form2->createView(),
  338.             'Category' => $Category,
  339.             'TargetCategory' => $TargetCategory,
  340.             'ChildCategory' => $ChildCategory,
  341.             'customer' => $Customer,
  342.             'filterProductModalForm' => $filterProductModalForm->createView(),
  343.         ];
  344.     }
  345.     /**
  346.      * 商品詳細画面.
  347.      *
  348.      * @Route("/products/detail/{id}", name="product_detail", methods={"GET"}, requirements={"id" = "\d+"})
  349.      * @Template("Product/detail.twig")
  350.      * @ParamConverter("Product", options={"repository_method" = "findWithSortedClassCategories"})
  351.      *
  352.      * @param Request $request
  353.      * @param Product $Product
  354.      * @return array
  355.      * @throws NonUniqueResultException
  356.      * @throws NoResultException
  357.      */
  358.     public function detail(Request $requestProduct $Product)
  359.     {
  360.         if (!$this->checkVisibility($Product)) {
  361.             throw new NotFoundHttpException();
  362.         }
  363.         $builder $this->formFactory->createNamedBuilder(
  364.             '',
  365.             AddCartType::class,
  366.             null,
  367.             [
  368.                 'product' => $Product,
  369.                 'id_add_product_id' => false,
  370.             ]
  371.         );
  372.         $event = new EventArgs(
  373.             [
  374.                 'builder' => $builder,
  375.                 'Product' => $Product,
  376.             ],
  377.             $request
  378.         );
  379.         $this->eventDispatcher->dispatch($eventEccubeEvents::FRONT_PRODUCT_DETAIL_INITIALIZE);
  380.         $form $builder->getForm();
  381.         $is_favorite false;
  382.         // スキンケアの対象カテゴリがあるかのチェックフラグ
  383.         $skinCheckFlag false;
  384.         
  385.         // is login?
  386.         $Customer $this->getUser();
  387.         if ($this->isGranted('ROLE_USER')) {
  388.             $is_favorite $this->customerFavoriteProductRepository->isFavorite($Customer$Product);
  389.         }
  390.         // キャンペーン商品確認
  391.         $campaignProduct $this->productRepository->checkCampaignProduct($Product->getId(), $Customer);
  392.         $campaignInfo = [];
  393.         // キャンペーン情報
  394.         if (!empty($campaignProduct) && $campaignProduct->getCampaignDetails()) {
  395.             // チェック商品
  396.             $originalPrice $Product->getProductClasses()[0]->getPrice02();
  397.             $TaxRule $this->taxRuleRepository->getByRule($Product$Product->getProductClasses()[0]);
  398.             $campaignDetail $campaignProduct->getCampaignDetails()[0];
  399.             $campaign $campaignDetail->getCampaign();
  400.             $campaignDetail $campaignProduct->getCampaignDetails()[0];
  401.             if (!empty($campaignDetail->getAddPoint())) {
  402.                 $campaign_point $campaignDetail->getAddPoint();
  403.             } elseif (!empty($campaignDetail->getDiscountRate())) {
  404.                 $campaign_rate = (int) $campaignDetail->getDiscountRate();
  405.                 $campaign_price $originalPrice * (0.01 $campaign_rate);
  406.             } elseif (!empty($campaignDetail->getDiscountPrice())) {
  407.                 $campaign_price $originalPrice - (int) $campaignDetail->getDiscountPrice();
  408.                 $campaign_rate = ($originalPrice $campaign_price) / $originalPrice 100;
  409.             }
  410.             // キャンペーン価格に対する税込価格計算
  411.             if (!empty($campaign_price)) {
  412.                 $tax $this->taxRuleService->calcTax($campaign_price$TaxRule->getTaxRate(), $TaxRule->getRoundingType()->getId(), $TaxRule->getTaxAdjust());
  413.                 $campaign_price $campaign_price $tax;
  414.             } else {
  415.                 $campaign_price $Product->getPrice02IncTaxMax();
  416.             }
  417.             $campaignInfo['campaign_point'] = $campaign_point ?? 0;
  418.             $campaignInfo['campaign_price'] = $campaign_price;
  419.             $campaignInfo['campaign_rate'] = $campaign_rate ?? 0;
  420.             $campaignInfo['campaign_detail'] = $campaignDetail;
  421.             $campaignInfo['campaign_start_date'] = $campaign $campaign->getStartDate() : null;
  422.             $campaignInfo['campaign_end_date'] = $campaign $campaign->getEndDate() : null;
  423.         }
  424.         // 自身の定期商品取得
  425.         $PlanProduct $this->productRepository->findPlanProductById($Product->getId());
  426.         // スキンケアの対象カテゴリがあるかのチェック
  427.         $skinCheckFlag $this->skinCareDisplayCheck($Product);
  428.         // ページ名取得
  429.         $pageName['product'] = $Product;
  430.         $ids[] = $Product->getId();
  431.         $ProductsAndClassCategories $this->productRepository->findProductsWithSortedClassCategories($ids'p.id');
  432.         $builder $this->formFactory->createNamedBuilder(
  433.             '',
  434.             AddCartType::class,
  435.             null,
  436.             [
  437.                 'product' => $ProductsAndClassCategories[$Product->getId()],
  438.                 'allow_extra_fields' => true,
  439.             ]
  440.         );
  441.         $addCartForm $builder->getForm();
  442.         $form $addCartForm->createView();
  443.         $data $this->productRepository->getProductByNormalId($Product->getId());
  444.         $subform null;
  445.         if($data) {
  446.             $PlanProductsAndClassCategories $this->productRepository->findProductsWithSortedClassCategories([$data->getId()], 'p.id');
  447.             $subProduct $data;
  448.             $builder $this->formFactory->createNamedBuilder(
  449.                 '',
  450.                 AddCartType::class,
  451.                 null,
  452.                 [
  453.                     'product' => $PlanProductsAndClassCategories[$data->getId()],
  454.                     'allow_extra_fields' => true,
  455.                 ]
  456.             );
  457.             $addSubCartForm $builder->getForm();
  458.             $subform $addSubCartForm ->createView();
  459.         }
  460.         $Modalbuilder $this->formFactory->createNamedBuilder(
  461.             '',
  462.             AddCartModalType::class,
  463.             null,
  464.             [
  465.             ]
  466.         ); 
  467.         $form2 $Modalbuilder->getForm();
  468.         return [
  469.             'title' => $this->getPageTitle($pageName),
  470.             'subtitle' => $this->getPageTitle($pageName),
  471.             'form' => $form,
  472.             'form2' => $form2->createView(),
  473.             'subform' => $subform,
  474.             'Product' => $Product,
  475.             'subProduct' => $subProduct ?? null,
  476.             'is_favorite' => $is_favorite,
  477.             'customer' => $Customer,
  478.             'campaignInfo' => $campaignInfo,
  479.             'PlanProduct' => $PlanProduct,
  480.             'skinCheckFlag' => $skinCheckFlag,
  481.         ];
  482.     }
  483.     /**
  484.      * お気に入り追加.
  485.      *
  486.      * @Route("/products/add_favorite/{id}", name="product_add_favorite", requirements={"id" = "\d+"}, methods={"GET", "POST"})
  487.      */
  488.     public function addFavorite(Request $requestProduct $Product)
  489.     {
  490.         $this->checkVisibility($Product);
  491.         $event = new EventArgs(
  492.             [
  493.                 'Product' => $Product,
  494.             ],
  495.             $request
  496.         );
  497.         $this->eventDispatcher->dispatch($eventEccubeEvents::FRONT_PRODUCT_FAVORITE_ADD_INITIALIZE);
  498.         if ($this->isGranted('ROLE_USER')) {
  499.             $Customer $this->getUser();
  500.             $this->customerFavoriteProductRepository->addFavorite($Customer$Product);
  501.             $this->session->getFlashBag()->set('product_detail.just_added_favorite'$Product->getId());
  502.             $event = new EventArgs(
  503.                 [
  504.                     'Product' => $Product,
  505.                 ],
  506.                 $request
  507.             );
  508.             $this->eventDispatcher->dispatch($eventEccubeEvents::FRONT_PRODUCT_FAVORITE_ADD_COMPLETE);
  509.             return $this->redirectToRoute('product_detail', ['id' => $Product->getId()]);
  510.         } else {
  511.             // 非会員の場合、ログイン画面を表示
  512.             //  ログイン後の画面遷移先を設定
  513.             $this->setLoginTargetPath($this->generateUrl('product_add_favorite', ['id' => $Product->getId()], UrlGeneratorInterface::ABSOLUTE_URL));
  514.             $this->session->getFlashBag()->set('eccube.add.favorite'true);
  515.             $event = new EventArgs(
  516.                 [
  517.                     'Product' => $Product,
  518.                 ],
  519.                 $request
  520.             );
  521.             $this->eventDispatcher->dispatch($eventEccubeEvents::FRONT_PRODUCT_FAVORITE_ADD_COMPLETE);
  522.             return $this->redirectToRoute('mypage_login');
  523.         }
  524.     }
  525.     /**
  526.      * @Route("/products/product_info/{id}", name="product_info", methods={"POST"}, requirements={"id" = "\d+"})
  527.      * @param Request $request
  528.      * @param Product $Product
  529.      * @return false|string
  530.      */
  531.     public function getProductInfoByXmlRequest(Request $requestProduct $Product) : mixed
  532.     {
  533.         $productimage = ($Product->getMainFileForFront() !== null && $Product->getMainFileForFront()->getFileName() !== null) ? $Product->getMainFileForFront()->getFileName() : "no_image_product.png";
  534.         $quantity $request->request->get('quantity');
  535.         $subscriptionId $request->request->get('subscription');
  536.         $json =  [
  537.             "subscription" => $subscriptionId,
  538.             "product_name" => $Product->getName(),
  539.             "product_img" => $productimage,
  540.             "product_price" => number_format($Product->getPrice02IncTaxMax()),
  541.             "product_quantity" => number_format($quantity),
  542.         ];
  543.         return $this->json($json);
  544.     }
  545.     /**
  546.      * カートに追加.
  547.      *
  548.      * @Route("/products/add_cart/{id}", name="product_add_cart", methods={"GET", "POST"}, requirements={"id" = "\d+"})
  549.      */
  550.     public function addCart(Request $requestProduct $Product) : mixed
  551.     {
  552.         // エラーメッセージの配列
  553.         $errorMessages = [];
  554.         if (!$this->checkVisibility($Product)) {
  555.             throw new NotFoundHttpException();
  556.         }
  557.         // 販売期間商品チェック
  558.         $response $this->checkProductSalePeriod($Product$request);
  559.         if ($response !== null) {
  560.             return $response ?? $this->redirectToRoute('cart');
  561.         }
  562.         $builder $this->formFactory->createNamedBuilder(
  563.             '',
  564.             AddCartModalType::class,
  565.             null,
  566.             [
  567.             ]
  568.         );
  569.         
  570.         $event = new EventArgs(
  571.             [
  572.             ],
  573.             $request
  574.         );
  575.         $this->eventDispatcher->dispatch($eventEccubeEvents::FRONT_PRODUCT_CART_ADD_INITIALIZE);
  576.         /* @var $form \Symfony\Component\Form\FormInterface */
  577.         try {
  578.             $form $builder->getForm();
  579.         } catch (\Exception $e) {
  580.             $logger->error("Error while creating form: " $e->getMessage());
  581.             
  582.             return $this->render('error.html.twig', [
  583.                 'message' => 'An error occurred while processing the form.',
  584.             ]);
  585.         }
  586.         $intervalId $request->request->get('interval_id') ? $request->request->get('interval_id') : null;
  587.         $form->handleRequest($request);
  588.         if (!$form->isValid()) {
  589.             $this->addError('front.product.invalid_quantity');
  590.             return $this->redirectToRoute('product_detail', ['id' => $Product->getId()]);
  591.         }
  592.         $addCartData $form->getData();
  593.         // 一回購入制限商品チェック
  594.         if ($Product->getOnlyOne()) {
  595.             $Customer $this->getUser();
  596.             $checkResult $this->orderRepository->checkOnlyOneProductOrder($Product$Customer);
  597.             if (empty($Customer) || !empty($checkResult)) {
  598.                 $only_one_message = empty($Customer) ? "登録した会員のみ1回購入商品を購入できます。" "既に購入した1回のみ購入商品は再購入できません。";
  599.                 if ($request->isXmlHttpRequest()) {
  600.                     // ajaxでのリクエストの場合は結果をjson形式で返す。
  601.                     return $this->json(['done' => false'messages' => [$only_one_message]]);
  602.                 } else {
  603.                     // ajax以外でのリクエストの場合はカート画面へリダイレクト
  604.                     $this->addRequestError($only_one_message);
  605.                     return $this->redirectToRoute('cart');
  606.                 }
  607.             }
  608.         }
  609.         // カートへ追加
  610.         $this->cartService->addProduct($request->request->get('ProductClass'), $addCartData['quantity'] ,$intervalId);
  611.         // 明細の正規化
  612.         $Carts $this->cartService->getCarts();
  613.         foreach ($Carts as $Cart) {
  614.             $result $this->purchaseFlow->validate($Cart, new PurchaseContext($Cart$this->getUser()));
  615.             // 復旧不可のエラーが発生した場合は追加した明細を削除.
  616.             if ($result->hasError()) {
  617.                 $this->cartService->removeProduct($addCartData['product_class_id']);
  618.                 foreach ($result->getErrors() as $error) {
  619.                     $errorMessages[] = $error->getMessage();
  620.                 }
  621.             }
  622.             foreach ($result->getWarning() as $warning) {
  623.                 $errorMessages[] = $warning->getMessage();
  624.             }
  625.         }
  626.         $this->cartService->save();
  627.         log_info(
  628.             'カート追加処理完了',
  629.             [
  630.                 // 'product_id' => $Product->getId(),
  631.                 // 'product_class_id' => $addCartData['product_class_id'],
  632.                 // 'quantity' => $addCartData['quantity'],
  633.             ]
  634.         );
  635.         $event = new EventArgs(
  636.             [
  637.                 'form' => $form,
  638.                 'Product' => $Product,
  639.             ],
  640.             $request
  641.         );
  642.         $this->eventDispatcher->dispatch($eventEccubeEvents::FRONT_PRODUCT_CART_ADD_COMPLETE);
  643.         if ($event->getResponse() !== null) {
  644.             return $event->getResponse();
  645.         }
  646.         if ($request->isXmlHttpRequest()) {
  647.             // ajaxでのリクエストの場合は結果をjson形式で返す。
  648.             // 初期化
  649.             $messages = [];
  650.             if (empty($errorMessages)) {
  651.                 // エラーが発生していない場合
  652.                 $done true;
  653.                 array_push($messagestrans('front.product.add_cart_complete'));
  654.             } else {
  655.                 // エラーが発生している場合
  656.                 $done false;
  657.                 $messages $errorMessages;
  658.             }
  659.             return $this->json(['done' => $done'messages' => $messages]);
  660.         } else {
  661.             // ajax以外でのリクエストの場合はカート画面へリダイレクト
  662.             foreach ($errorMessages as $errorMessage) {
  663.                 $this->addRequestError($errorMessage);
  664.             }
  665.             return $this->redirectToRoute('cart');
  666.         }
  667.     }
  668.     /**
  669.      * 購入履歴ページで再購入
  670.      *
  671.      * @Route("/mypage/history/add_cart/{id}", name="history_add_cart", methods={"GET", "POST"}, requirements={"id" = "\d+"})
  672.      */
  673.     public function historyAddCart(Request $requestProduct $Product)
  674.     {
  675.         // エラーメッセージの配列
  676.         $errorMessages = [];
  677.         if (!$this->checkVisibility($Product)) {
  678.             throw new NotFoundHttpException();
  679.         }
  680.         $builder $this->formFactory->createNamedBuilder(
  681.             '',
  682.             HistoryAddCartType::class,
  683.             null,
  684.             [
  685.                 'product' => $Product,
  686.                 'id_add_product_id' => false,
  687.             ]
  688.         );
  689.         $event = new EventArgs(
  690.             [
  691.                 'builder' => $builder,
  692.                 'Product' => $Product,
  693.             ],
  694.             $request
  695.         );
  696.         $this->eventDispatcher->dispatch($eventEccubeEvents::FRONT_PRODUCT_CART_ADD_INITIALIZE);
  697.         $form $builder->getForm();
  698.         $form->handleRequest($request);
  699.         if (!$form->isValid()) {
  700.             throw new NotFoundHttpException();
  701.         }
  702.         $addCartData $form->getData();
  703.         log_info(
  704.             'カート追加処理開始',
  705.             [
  706.                 'product_id' => $Product->getId(),
  707.                 'product_class_id' => $addCartData['product_class_id'],
  708.                 'quantity' => $addCartData['quantity'],
  709.             ]
  710.         );
  711.         // 一回購入制限商品チェック
  712.         if ($Product->getOnlyOne()) {
  713.             $Customer $this->getUser();
  714.             $checkResult $this->orderRepository->checkOnlyOneProductOrder($Product$Customer);
  715.             if (!empty($checkResult)) {
  716.                 $only_one_message "既に購入した1回のみ購入商品は再購入できません。";
  717.                 if ($request->isXmlHttpRequest()) {
  718.                     // ajaxでのリクエストの場合は結果をjson形式で返す。
  719.                     return $this->json(['done' => false'messages' => [$only_one_message]]);
  720.                 } else {
  721.                     // ajax以外でのリクエストの場合はカート画面へリダイレクト
  722.                     $this->addRequestError($only_one_message);
  723.                     return $this->redirectToRoute('mypage_history');
  724.                 }
  725.             }
  726.         }
  727.         // 販売期間商品チェック
  728.         $response $this->checkProductSalePeriod($Product$request);
  729.         if ($response !== null) {
  730.             return $response;
  731.         }
  732.         // カートへ追加
  733.         $this->cartService->addProduct($addCartData['product_class_id'], $addCartData['quantity']);
  734.         // 明細の正規化
  735.         $Carts $this->cartService->getCarts();
  736.         foreach ($Carts as $Cart) {
  737.             $result $this->purchaseFlow->validate($Cart, new PurchaseContext($Cart$this->getUser()));
  738.             // 復旧不可のエラーが発生した場合は追加した明細を削除.
  739.             if ($result->hasError()) {
  740.                 $this->cartService->removeProduct($addCartData['product_class_id']);
  741.                 foreach ($result->getErrors() as $error) {
  742.                     $errorMessages[] = $error->getMessage();
  743.                 }
  744.             }
  745.             foreach ($result->getWarning() as $warning) {
  746.                 $errorMessages[] = $warning->getMessage();
  747.             }
  748.         }
  749.         $this->cartService->save();
  750.         log_info(
  751.             'カート追加処理完了',
  752.             [
  753.                 'product_id' => $Product->getId(),
  754.                 'product_class_id' => $addCartData['product_class_id'],
  755.                 'quantity' => $addCartData['quantity'],
  756.             ]
  757.         );
  758.         $event = new EventArgs(
  759.             [
  760.                 'form' => $form,
  761.                 'Product' => $Product,
  762.             ],
  763.             $request
  764.         );
  765.         $this->eventDispatcher->dispatch($eventEccubeEvents::FRONT_PRODUCT_CART_ADD_COMPLETE);
  766.         if ($event->getResponse() !== null) {
  767.             return $event->getResponse();
  768.         }
  769.         if ($request->isXmlHttpRequest()) {
  770.             // ajaxでのリクエストの場合は結果をjson形式で返す。
  771.             // 初期化
  772.             $messages = [];
  773.             if (empty($errorMessages)) {
  774.                 // エラーが発生していない場合
  775.                 $done true;
  776.                 array_push($messagestrans('front.product.add_cart_complete'));
  777.             } else {
  778.                 // エラーが発生している場合
  779.                 $done false;
  780.                 $messages $errorMessages;
  781.             }
  782.             return $this->json(['done' => $done'messages' => $messages]);
  783.         } else {
  784.             // ajax以外でのリクエストの場合はカート画面へリダイレクト
  785.             foreach ($errorMessages as $errorMessage) {
  786.                 $this->addRequestError($errorMessage);
  787.             }
  788.             return $this->redirectToRoute('cart');
  789.         }
  790.     }
  791.     private function checkProductSalePeriod(Product $ProductRequest $request)
  792.     {
  793.         $now = new \DateTime();
  794.         $startDate $Product->getStartDate();
  795.         $endDate $Product->getEndDate();
  796.         if (($startDate && $now $startDate) || ($endDate && $now $endDate)) {
  797.             $period_message "この商品は販売が終了しています。";
  798.             if ($request->isXmlHttpRequest()) {
  799.                 // ajaxでのリクエストの場合は結果をjson形式で返す。
  800.                 return $this->json(['done' => false'messages' => [$period_message]]);
  801.             } else {
  802.                 // ajax以外でのリクエストの場合はカート画面へリダイレクト
  803.                 $this->addRequestError($period_message);
  804.                 return $this->redirectToRoute('cart');
  805.             }
  806.         }
  807.         return null;
  808.     }
  809.     /**
  810.      * 検索バー絞込検索
  811.      *
  812.      * @Route("/products/list/search", name="product_list_search", methods={"GET"})
  813.      *
  814.      * @param Request $request
  815.      * @return RedirectResponse
  816.      */
  817.     public function productListSearch(Request $request): RedirectResponse
  818.     {
  819.         $keyword $request->query->get('keyword');
  820.         return $this->redirectToRoute('product_list', [
  821.             'keyword' => $keyword ,
  822.         ]);
  823.     }
  824.     /**
  825.      * お気に入り切り替え
  826.      *
  827.      * @Route("/products/favorite", name="change_favorite", methods={"POST"})
  828.      * @param Request $request
  829.      * @return string
  830.      */
  831.     public function changeFavorite(Request $request)
  832.     {
  833.         if (!$request->isXmlHttpRequest()) {
  834.             throw new BadRequestHttpException();//API通信のみ受け付ける
  835.         }
  836.         $Product $this->productRepository->find($request->request->get('product_id'));
  837.         $Customer $this->customerRepository->find($request->request->get('customer_id'));
  838.         if(!is_null($Customer) && !is_null($Product)){
  839.             if($this->customerFavoriteProductRepository->isFavorite($Customer$Product)){
  840.                 $CustomerFavorite  $this->customerFavoriteProductRepository->findOneBy(
  841.                     [
  842.                         'Customer' => $Customer->getId(),
  843.                         'Product' => $Product->getId()]);
  844.                 $this->customerFavoriteProductRepository->delete($CustomerFavorite);
  845.             }else{
  846.                 $customerFavorit = new CustomerFavoriteProduct();
  847.                 $customerFavorit->setProduct($Product);
  848.                 $customerFavorit->setCustomer($Customer);
  849.                 $this->entityManager->persist($customerFavorit);
  850.             }
  851.             $this->entityManager->flush();
  852.             $this->entityManager->commit();
  853.             return $this->json(['success' => true]);
  854.         }else{
  855.             log_info('会員もしくは商品情報を取得できなかっため、お気に入りの変更に失敗しました。');
  856.             return $this->json(['success' => false]);
  857.         }
  858.     }
  859.     /**
  860.      * 商品詳細ページでのお気に入り切り替え
  861.      *
  862.      * @Route("/products/detail/favorite", name="detail_change_favorite", methods={"POST"})
  863.      * @param Request $request
  864.      * @return JsonResponse
  865.      */
  866.     public function detailChangeFavorite(Request $request): JsonResponse
  867.     {
  868.         if (!$request->isXmlHttpRequest()) {
  869.             throw new BadRequestHttpException(); //API通信のみ受け付ける
  870.         }
  871.         $Product $this->productRepository->find($request->request->get('product_id'));
  872.         $Customer $this->customerRepository->find($request->request->get('customer_id'));
  873.         if (!is_null($Customer) && !is_null($Product)) {
  874.             try {
  875.                 $this->entityManager->beginTransaction(); //トランザクション開始
  876.                 if ($this->customerFavoriteProductRepository->isFavorite($Customer$Product)) {
  877.                     $CustomerFavorite $this->customerFavoriteProductRepository->findOneBy(['Customer' => $Customer'Product' => $Product]);
  878.                     $this->customerFavoriteProductRepository->delete($CustomerFavorite);
  879.                 } else {
  880.                     $customerFavoriteProduct = new CustomerFavoriteProduct();
  881.                     $customerFavoriteProduct->setProduct($Product);
  882.                     $customerFavoriteProduct->setCustomer($Customer);
  883.                     $this->entityManager->persist($customerFavoriteProduct);
  884.                 }
  885.                 $this->entityManager->flush();
  886.                 $this->entityManager->commit(); // トランザクションで、レコードの挿入や削除に対して、コミットが必要
  887.                 return $this->json(['success' => true]);
  888.             } catch (\Exception $e) {
  889.                 log_info($e->getMessage());
  890.                 log_info('例外エラーにより、商品詳細ページでの気に入り切り替えに失敗しました。ロールバックします。');
  891.                 $this->entityManager->rollback();
  892.                 return $this->json(['success' => false]);
  893.             }
  894.         } else{
  895.             log_info('商品か顧客情報が足りなくて、商品詳細ページでの気に入り切り替えに失敗しました。');
  896.             return $this->json(['success' => false]);
  897.         }
  898.     }
  899.     /**
  900.      * ページタイトルの設定
  901.      *
  902.      * @param array|null $searchData
  903.      *
  904.      * @return string
  905.      */
  906.     protected function getPageTitle(?array $searchData): string
  907.     {
  908.         if (!empty($searchData['name'])) {
  909.             return trans('front.product.search_result');
  910.         } elseif (isset($searchData['category_id']) && $searchData['category_id']) {
  911.             return $searchData['category_id']->getName() . trans('front.product.list.page_name');
  912.         } elseif (isset($searchData['product']) && $searchData['product']) {
  913.             // (子)カテゴリ名取得
  914.             $productCategories $searchData['product']->getProductCategories();
  915.             if (count($productCategories) > 0) {
  916.                 $hierarchy 0;
  917.                 foreach ($productCategories as $ProductCategory) {
  918.                     $ProductHierarchy $ProductCategory->getCategory()->getHierarchy();
  919.                     if($ProductHierarchy $hierarchy){
  920.                         $category $ProductCategory->getCategory();
  921.                         $hierarchy $ProductHierarchy;
  922.                     }
  923.                 }
  924.             }
  925.             $category_name = isset($category) ? " | ".$category->getName() : "";
  926.             return $searchData['product']->getName() . $category_name trans('front.product.list.page_name');
  927.         } else {
  928.             return trans('front.product.all_products');
  929.         }
  930.     }
  931.     /**
  932.      * 閲覧可能な商品かどうかを判定
  933.      *
  934.      * @param Product $Product
  935.      *
  936.      * @return boolean 閲覧可能な場合はtrue
  937.      */
  938.     protected function checkVisibility(Product $Product)
  939.     {
  940.         $is_admin $this->session->has('_security_admin');
  941.         // 管理ユーザの場合はステータスやオプションにかかわらず閲覧可能.
  942.         if (!$is_admin) {
  943.             // 在庫なし商品の非表示オプションが有効な場合.
  944.             // if ($this->BaseInfo->isOptionNostockHidden()) {
  945.             //     if (!$Product->getStockFind()) {
  946.             //         return false;
  947.             //     }
  948.             // }
  949.             // 公開ステータスでない商品は表示しない.
  950.             if ($Product->getStatus()->getId() !== ProductStatus::DISPLAY_SHOW) {
  951.                 return false;
  952.             }
  953.         }
  954.         return true;
  955.     }
  956.     /**
  957.      * 商品一覧ページで商品キャンペーン情報取得
  958.      *
  959.      * @Route("/products/campaign/detail/{product_id}", name="list_campaign_detail", methods={"GET", "POST"}, requirements={"product_id" = "\d+"})
  960.      * @Template("Product/list_campaign_detail.twig")
  961.      * @param int $product_id
  962.      * @return array
  963.      * @throws NonUniqueResultException
  964.      * @throws NoResultException
  965.      */
  966.     public function campaignDetail(int $product_id): array
  967.     {
  968.         // is login?
  969.         $Customer $this->getUser();
  970.         // キャンペーン詳細
  971.         $campaignDetail = [];
  972.         $campaign_point 0;
  973.         $campaign_price 0;
  974.         $campaign_rate 0;
  975.         $discount_price 0;
  976.         // チェック商品
  977.         $Product $this->productRepository->findOneBy(['id' => $product_id]);
  978.         $originalPrice $Product->getPrice02IncTaxMax();
  979.         // キャンペーン商品確認
  980.         $campaignProduct $this->productRepository->checkCampaignProduct($product_id$Customer);
  981.         // キャンペーン情報
  982.         if (!empty($campaignProduct) && $campaignProduct->getCampaignDetails()) {
  983.             $campaignDetail $campaignProduct->getCampaignDetails()[0];
  984.             if (!empty($campaignDetail->getAddPoint())) {
  985.                 $campaign_point $campaignDetail->getAddPoint();
  986.             } elseif (!empty($campaignDetail->getDiscountRate())) {
  987.                 $campaign_rate = (int) $campaignDetail->getDiscountRate();
  988.                 $campaign_price $originalPrice * (0.01 $campaign_rate);
  989.             } elseif (!empty($campaignDetail->getDiscountPrice())) {
  990.                 $discount_price = (int) $campaignDetail->getDiscountPrice();
  991.                 $campaign_price $originalPrice $discount_price;
  992.             }
  993.         }
  994.         if (empty($campaign_price)) {
  995.             $campaign_price $Product->getPrice02IncTaxMax();
  996.         }
  997.         return [
  998.             'discountprice' => $discount_price,
  999.             'Product' => $Product,
  1000.             'campaignProduct' => $campaignProduct,
  1001.             'campaignDetail' => $campaignDetail,
  1002.             'campaign_point' => $campaign_point,
  1003.             'campaign_price' => $campaign_price,
  1004.             'campaign_rate' => $campaign_rate,
  1005.         ];
  1006.     }
  1007.     /**
  1008.      * スキンケアステップの表示カテゴリチェック
  1009.      *
  1010.      * @param  array|null $searchData
  1011.      *
  1012.      */
  1013.     protected function skinCareDisplayCheck($Product)
  1014.     {
  1015.         $categorys $Product->getCategoryList();
  1016.         $checkFlag false;
  1017.         foreach ($categorys as $category) {
  1018.             if ($category === 'スキンケア > 化粧水' || $category === 'スキンケア > 保湿ジェル' || $category === 'スキンケア > 保湿クリーム' || $category === 'スキンケア > クレンジング&洗顔' || $category === 'トライアルセット > スキンケア') {
  1019.                 // 対象のカテゴリを含む場合
  1020.                 $checkFlag true;
  1021.                 break;
  1022.             }
  1023.         }
  1024.         return $checkFlag;
  1025.     }
  1026. }