<?php
/*
* This file is part of EC-CUBE
*
* Copyright(c) EC-CUBE CO.,LTD. All Rights Reserved.
*
* http://www.ec-cube.co.jp/
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Customize\Controller;
use Customize\Form\Type\FilterProductType;
use Customize\Repository\OrderRepository;
use Doctrine\ORM\NonUniqueResultException;
use Doctrine\ORM\NoResultException;
use Eccube\Entity\BaseInfo;
use Eccube\Entity\CustomerFavoriteProduct;
use Eccube\Entity\Master\ProductStatus;
use Eccube\Entity\Product;
use Eccube\Entity\ProductClass;
use Eccube\Event\EccubeEvents;
use Eccube\Event\EventArgs;
use Customize\Form\Type\AddCartType;
use Customize\Form\Type\AddCartSubscriptionType;
use Customize\Form\Type\AddCartModalType;
use Customize\Form\Type\Front\HistoryAddCartType;
use Eccube\Form\Type\SearchProductType;
use Eccube\Repository\BaseInfoRepository;
use Customize\Repository\CategoryRepository;
use Eccube\Repository\CustomerFavoriteProductRepository;
use Customize\Repository\CustomerRepository;
use Eccube\Repository\Master\ProductListMaxRepository;
use Customize\Repository\SpecialArticleRepository;
use Customize\Repository\ProductRepository;
use Eccube\Repository\TaxRuleRepository;
use Eccube\Service\CartService;
use Eccube\Service\PurchaseFlow\PurchaseContext;
use Eccube\Service\PurchaseFlow\PurchaseFlow;
use Eccube\Service\TaxRuleService;
use Eccube\Util\FormUtil;
use Knp\Bundle\PaginatorBundle\Pagination\SlidingPagination;
use Knp\Component\Pager\PaginatorInterface;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
use Eccube\Controller\AbstractController;
class ProductController extends AbstractController
{
/**
* @var PurchaseFlow
*/
protected $purchaseFlow;
/**
* @var CustomerFavoriteProductRepository
*/
protected $customerFavoriteProductRepository;
/**
* @var CartService
*/
protected $cartService;
/**
* @var ProductRepository
*/
protected $productRepository;
/**
* @var CustomerRepository
*/
protected $customerRepository;
/**
* @var OrderRepository
*/
protected $orderRepository;
/**
* @var BaseInfo
*/
protected $BaseInfo;
/**
* @var AuthenticationUtils
*/
protected $helper;
/**
* @var ProductListMaxRepository
*/
protected $productListMaxRepository;
/**
* @var CategoryRepository
*/
protected $categoryRepository;
/**
* @var TaxRuleRepository
*/
protected $taxRuleRepository;
/**
* @var TaxRuleService
*/
protected $taxRuleService;
private $title = '';
/**
* @var SpecialArticleRepository
*/
protected $specialArticleRepository;
/**
* ProductController constructor.
*
* @param PurchaseFlow $cartPurchaseFlow
* @param CustomerFavoriteProductRepository $customerFavoriteProductRepository
* @param CartService $cartService
* @param ProductRepository $productRepository
* @param BaseInfoRepository $baseInfoRepository
* @param AuthenticationUtils $helper
* @param ProductListMaxRepository $productListMaxRepository
* @param CategoryRepository $categoryRepository
* @param SpecialArticleRepository $specialArticleRepository
* @param CustomerRepository $customerRepository
* @param OrderRepository $orderRepository
* @param TaxRuleRepository $taxRuleRepository
* @param TaxRuleService $taxRuleService
*/
public function __construct(
PurchaseFlow $cartPurchaseFlow,
CustomerFavoriteProductRepository $customerFavoriteProductRepository,
CartService $cartService,
ProductRepository $productRepository,
BaseInfoRepository $baseInfoRepository,
AuthenticationUtils $helper,
ProductListMaxRepository $productListMaxRepository,
CategoryRepository $categoryRepository,
SpecialArticleRepository $specialArticleRepository,
CustomerRepository $customerRepository,
OrderRepository $orderRepository,
TaxRuleRepository $taxRuleRepository,
TaxRuleService $taxRuleService
) {
$this->purchaseFlow = $cartPurchaseFlow;
$this->customerFavoriteProductRepository = $customerFavoriteProductRepository;
$this->cartService = $cartService;
$this->productRepository = $productRepository;
$this->BaseInfo = $baseInfoRepository->get();
$this->helper = $helper;
$this->productListMaxRepository = $productListMaxRepository;
$this->categoryRepository = $categoryRepository;
$this->specialArticleRepository = $specialArticleRepository;
$this->customerRepository = $customerRepository;
$this->orderRepository = $orderRepository;
$this->taxRuleRepository = $taxRuleRepository;
$this->taxRuleService = $taxRuleService;
}
/**
* 商品一覧画面.
*
* @Route("/products/list/{category_id}", name="product_list", methods={"GET"}, requirements={"category_id" = "\d+"})
* @Route("/products/list/filter", name="product_list_filter", methods={"GET", "POST"})
* @Template("Product/list.twig")
*/
public function index(Request $request, PaginatorInterface $paginator = null, int $category_id = null, string $keyword = null): array
{
$specialArticles = $this->specialArticleRepository->getSortSpecialArticleProducts();
// is login?
$Customer = $this->getUser();
// Doctrine SQLFilter
if ($this->BaseInfo->isOptionNostockHidden()) {
$this->entityManager->getFilters()->enable('option_nostock_hidden');
}
// handleRequestは空のqueryの場合は無視するため
if ($request->getMethod() === 'GET' ) {
$request->query->set('pageno', $request->query->get('pageno', ''));
}
// 通常の商品一覧表示の場合セッションを空にする
if ($request->getPathInfo() !== $this->generateUrl('product_list_filter') && empty($category_id)) {
$this->session->remove('eccube.product.filter');
$this->session->remove('eccube.product.category');
}
// searchForm
$builder = $this->formFactory->createNamedBuilder('', SearchProductType::class);
if ($request->getMethod() === 'GET') {
$builder->setMethod('GET');
}
$event = new EventArgs(
[
'builder' => $builder,
],
$request
);
$this->eventDispatcher->dispatch($event, EccubeEvents::FRONT_PRODUCT_INDEX_INITIALIZE);
$searchForm = $builder->getForm();
$searchForm->handleRequest($request);
// paginator
$searchData = $searchForm->getData();
// 商品検索バーのキーワード設置
if(!is_null($request->query->get('keyword'))){
$searchData['name'] = $request->query->get('keyword');
}
// お悩みから検索
if (!is_null($request->query->get('concerns_id'))) {
$searchData['concerns_id'] = $request->query->get('concerns_id');
}
// 定期商品検索
if (!is_null($request->query->get('is_subscription'))) {
$searchData['is_subscription'] = $request->query->get('is_subscription');
}
// 商品フィルターフォーム
$filterBuilder = $this->formFactory->createBuilder(FilterProductType::class);
$filterProductModalForm = $filterBuilder->getForm();
$filterProductModalForm->handleRequest($request);
// 商品絞り込みの場合
if ($filterProductModalForm->isSubmitted()) {
$filterData = $request->request->get('filter_product', []);
// セッションからカテゴリを取得
$filterData['category_id'] = $this->session->get('eccube.product.category');
// セッションに絞り込み条件を保存
$this->session->set('eccube.product.filter', $filterData);
$qb = $this->productRepository->getQueryBuilderByFilterData($filterData);
$TargetCategory = $this->categoryRepository->findOneby(['id' => $filterData['category_id']]);
$ChildCategory = $TargetCategory ? $TargetCategory->getChildren() : $this->categoryRepository->selectByParentCategoryIdIsNull();
} elseif (!empty($category_id)) {
// セッションに検索カテゴリ条件を保持
$this->session->set('eccube.product.category', $category_id);
$Category = $this->categoryRepository->find($category_id);
$searchData['category_id'] = $Category;
// 絞り込み条件が存在の場合
if (!empty($this->session->get('eccube.product.filter'))) {
$filterData = $this->session->get('eccube.product.filter', []);
$filterData['category_id'] = $category_id;
$qb = $this->productRepository->getQueryBuilderByFilterData($filterData);
} else {
$qb = $this->productRepository->getQueryBuilderBySearchData($searchData);
}
$TargetCategory = $Category;
$ChildCategory = $TargetCategory->getChildren();
} elseif (!empty($this->session->get('eccube.product.filter'))) {
// セッションから検索条件を取得
$filterData = $this->session->get('eccube.product.filter', []);
$filterProductModalForm->setData($filterData);
// セッションからカテゴリを取得
$filterData['category_id'] = $this->session->get('eccube.product.category');
$Category = $this->categoryRepository->findOneby(['id' => $filterData['category_id']]);
foreach ($filterData as $key => $value) {
if (is_array($value)) {
$filterData[$key] = array_map('trim', $value);
} else {
$filterData[$key] = trim($value);
}
}
$qb = $this->productRepository->getQueryBuilderByFilterData($filterData);
$TargetCategory = $Category;
$ChildCategory = $TargetCategory ? $TargetCategory->getChildren() : $this->categoryRepository->selectByParentCategoryIdIsNull();
} else {
$qb = $this->productRepository->getQueryBuilderBySearchData($searchData);
$TargetCategory = null;
$ChildCategory = $this->categoryRepository->selectByParentCategoryIdIsNull();
}
$event = new EventArgs(
[
'searchData' => $searchData,
'qb' => $qb,
],
$request
);
$this->eventDispatcher->dispatch($event, EccubeEvents::FRONT_PRODUCT_INDEX_SEARCH);
$searchData = $event->getArgument('searchData');
$query = $qb->getQuery()
->enableResultCache(true, $this->eccubeConfig['eccube_result_cache_lifetime_short']);
/** @var SlidingPagination $pagination */
$pagination = $paginator->paginate(
$query,
!empty($searchData['pageno']) ? $searchData['pageno'] : 1,
!empty($searchData['disp_number']) ? $searchData['disp_number']->getId() : $this->productListMaxRepository->findOneBy([], ['sort_no' => 'ASC'])->getId(),
array('wrap-queries' => true)
);
$ids = [];
foreach ($pagination as $Product) {
$ids[] = $Product->getId();
}
$ProductsAndClassCategories = $this->productRepository->findProductsWithSortedClassCategories($ids, 'p.id');
// addCart form
$forms = [];
$subforms = [];
$subProducts = [];
foreach ($pagination as $Product) {
$builder = $this->formFactory->createNamedBuilder(
'',
AddCartType::class,
null,
[
'product' => $ProductsAndClassCategories[$Product->getId()],
'allow_extra_fields' => true,
]
);
$addCartForm = $builder->getForm();
$forms[$Product->getId()] = $addCartForm->createView();
$data = $this->productRepository->getProductByNormalId($Product->getId());
if($data) {
$PlanProductsAndClassCategories = $this->productRepository->findProductsWithSortedClassCategories([$data->getId()], 'p.id');
$subProducts[$Product->getId()] = $data;
$builder = $this->formFactory->createNamedBuilder(
'',
AddCartType::class,
null,
[
'product' => $PlanProductsAndClassCategories[$data->getId()],
'allow_extra_fields' => true,
]
);
$addSubCartForm = $builder->getForm();
$subforms[$Product->getId()] = $addSubCartForm ->createView();
}
}
$Modalbuilder = $this->formFactory->createNamedBuilder(
'',
AddCartModalType::class,
null,
[
]
);
$form2 = $Modalbuilder->getForm();
$Category = $searchForm->get('category_id')->getData();
$specialRoute = $this->generateUrl('special');
return [
'specialRoute' => $specialRoute,
'specialArticles' => $specialArticles,
'title' => $this->getPageTitle($searchData),
'subtitle' => $this->getPageTitle($searchData),
'pagination' => $pagination,
'subProducts' => $subProducts,
'search_form' => $searchForm->createView(),
'forms' => $forms,
'subforms' => $subforms,
'form2' => $form2->createView(),
'Category' => $Category,
'TargetCategory' => $TargetCategory,
'ChildCategory' => $ChildCategory,
'customer' => $Customer,
'filterProductModalForm' => $filterProductModalForm->createView(),
];
}
/**
* 商品詳細画面.
*
* @Route("/products/detail/{id}", name="product_detail", methods={"GET"}, requirements={"id" = "\d+"})
* @Template("Product/detail.twig")
* @ParamConverter("Product", options={"repository_method" = "findWithSortedClassCategories"})
*
* @param Request $request
* @param Product $Product
* @return array
* @throws NonUniqueResultException
* @throws NoResultException
*/
public function detail(Request $request, Product $Product)
{
if (!$this->checkVisibility($Product)) {
throw new NotFoundHttpException();
}
$builder = $this->formFactory->createNamedBuilder(
'',
AddCartType::class,
null,
[
'product' => $Product,
'id_add_product_id' => false,
]
);
$event = new EventArgs(
[
'builder' => $builder,
'Product' => $Product,
],
$request
);
$this->eventDispatcher->dispatch($event, EccubeEvents::FRONT_PRODUCT_DETAIL_INITIALIZE);
$form = $builder->getForm();
$is_favorite = false;
// スキンケアの対象カテゴリがあるかのチェックフラグ
$skinCheckFlag = false;
// is login?
$Customer = $this->getUser();
if ($this->isGranted('ROLE_USER')) {
$is_favorite = $this->customerFavoriteProductRepository->isFavorite($Customer, $Product);
}
// キャンペーン商品確認
$campaignProduct = $this->productRepository->checkCampaignProduct($Product->getId(), $Customer);
$campaignInfo = [];
// キャンペーン情報
if (!empty($campaignProduct) && $campaignProduct->getCampaignDetails()) {
// チェック商品
$originalPrice = $Product->getProductClasses()[0]->getPrice02();
$TaxRule = $this->taxRuleRepository->getByRule($Product, $Product->getProductClasses()[0]);
$campaignDetail = $campaignProduct->getCampaignDetails()[0];
$campaign = $campaignDetail->getCampaign();
$campaignDetail = $campaignProduct->getCampaignDetails()[0];
if (!empty($campaignDetail->getAddPoint())) {
$campaign_point = $campaignDetail->getAddPoint();
} elseif (!empty($campaignDetail->getDiscountRate())) {
$campaign_rate = (int) $campaignDetail->getDiscountRate();
$campaign_price = $originalPrice * (1 - 0.01 * $campaign_rate);
} elseif (!empty($campaignDetail->getDiscountPrice())) {
$campaign_price = $originalPrice - (int) $campaignDetail->getDiscountPrice();
$campaign_rate = ($originalPrice - $campaign_price) / $originalPrice * 100;
}
// キャンペーン価格に対する税込価格計算
if (!empty($campaign_price)) {
$tax = $this->taxRuleService->calcTax($campaign_price, $TaxRule->getTaxRate(), $TaxRule->getRoundingType()->getId(), $TaxRule->getTaxAdjust());
$campaign_price = $campaign_price + $tax;
} else {
$campaign_price = $Product->getPrice02IncTaxMax();
}
$campaignInfo['campaign_point'] = $campaign_point ?? 0;
$campaignInfo['campaign_price'] = $campaign_price;
$campaignInfo['campaign_rate'] = $campaign_rate ?? 0;
$campaignInfo['campaign_detail'] = $campaignDetail;
$campaignInfo['campaign_start_date'] = $campaign ? $campaign->getStartDate() : null;
$campaignInfo['campaign_end_date'] = $campaign ? $campaign->getEndDate() : null;
}
// 自身の定期商品取得
$PlanProduct = $this->productRepository->findPlanProductById($Product->getId());
// スキンケアの対象カテゴリがあるかのチェック
$skinCheckFlag = $this->skinCareDisplayCheck($Product);
// ページ名取得
$pageName['product'] = $Product;
$ids[] = $Product->getId();
$ProductsAndClassCategories = $this->productRepository->findProductsWithSortedClassCategories($ids, 'p.id');
$builder = $this->formFactory->createNamedBuilder(
'',
AddCartType::class,
null,
[
'product' => $ProductsAndClassCategories[$Product->getId()],
'allow_extra_fields' => true,
]
);
$addCartForm = $builder->getForm();
$form = $addCartForm->createView();
$data = $this->productRepository->getProductByNormalId($Product->getId());
$subform = null;
if($data) {
$PlanProductsAndClassCategories = $this->productRepository->findProductsWithSortedClassCategories([$data->getId()], 'p.id');
$subProduct = $data;
$builder = $this->formFactory->createNamedBuilder(
'',
AddCartType::class,
null,
[
'product' => $PlanProductsAndClassCategories[$data->getId()],
'allow_extra_fields' => true,
]
);
$addSubCartForm = $builder->getForm();
$subform = $addSubCartForm ->createView();
}
$Modalbuilder = $this->formFactory->createNamedBuilder(
'',
AddCartModalType::class,
null,
[
]
);
$form2 = $Modalbuilder->getForm();
return [
'title' => $this->getPageTitle($pageName),
'subtitle' => $this->getPageTitle($pageName),
'form' => $form,
'form2' => $form2->createView(),
'subform' => $subform,
'Product' => $Product,
'subProduct' => $subProduct ?? null,
'is_favorite' => $is_favorite,
'customer' => $Customer,
'campaignInfo' => $campaignInfo,
'PlanProduct' => $PlanProduct,
'skinCheckFlag' => $skinCheckFlag,
];
}
/**
* お気に入り追加.
*
* @Route("/products/add_favorite/{id}", name="product_add_favorite", requirements={"id" = "\d+"}, methods={"GET", "POST"})
*/
public function addFavorite(Request $request, Product $Product)
{
$this->checkVisibility($Product);
$event = new EventArgs(
[
'Product' => $Product,
],
$request
);
$this->eventDispatcher->dispatch($event, EccubeEvents::FRONT_PRODUCT_FAVORITE_ADD_INITIALIZE);
if ($this->isGranted('ROLE_USER')) {
$Customer = $this->getUser();
$this->customerFavoriteProductRepository->addFavorite($Customer, $Product);
$this->session->getFlashBag()->set('product_detail.just_added_favorite', $Product->getId());
$event = new EventArgs(
[
'Product' => $Product,
],
$request
);
$this->eventDispatcher->dispatch($event, EccubeEvents::FRONT_PRODUCT_FAVORITE_ADD_COMPLETE);
return $this->redirectToRoute('product_detail', ['id' => $Product->getId()]);
} else {
// 非会員の場合、ログイン画面を表示
// ログイン後の画面遷移先を設定
$this->setLoginTargetPath($this->generateUrl('product_add_favorite', ['id' => $Product->getId()], UrlGeneratorInterface::ABSOLUTE_URL));
$this->session->getFlashBag()->set('eccube.add.favorite', true);
$event = new EventArgs(
[
'Product' => $Product,
],
$request
);
$this->eventDispatcher->dispatch($event, EccubeEvents::FRONT_PRODUCT_FAVORITE_ADD_COMPLETE);
return $this->redirectToRoute('mypage_login');
}
}
/**
* @Route("/products/product_info/{id}", name="product_info", methods={"POST"}, requirements={"id" = "\d+"})
* @param Request $request
* @param Product $Product
* @return false|string
*/
public function getProductInfoByXmlRequest(Request $request, Product $Product) : mixed
{
$productimage = ($Product->getMainFileForFront() !== null && $Product->getMainFileForFront()->getFileName() !== null) ? $Product->getMainFileForFront()->getFileName() : "no_image_product.png";
$quantity = $request->request->get('quantity');
$subscriptionId = $request->request->get('subscription');
$json = [
"subscription" => $subscriptionId,
"product_name" => $Product->getName(),
"product_img" => $productimage,
"product_price" => number_format($Product->getPrice02IncTaxMax()),
"product_quantity" => number_format($quantity),
];
return $this->json($json);
}
/**
* カートに追加.
*
* @Route("/products/add_cart/{id}", name="product_add_cart", methods={"GET", "POST"}, requirements={"id" = "\d+"})
*/
public function addCart(Request $request, Product $Product) : mixed
{
// エラーメッセージの配列
$errorMessages = [];
if (!$this->checkVisibility($Product)) {
throw new NotFoundHttpException();
}
// 販売期間商品チェック
$response = $this->checkProductSalePeriod($Product, $request);
if ($response !== null) {
return $response ?? $this->redirectToRoute('cart');
}
$builder = $this->formFactory->createNamedBuilder(
'',
AddCartModalType::class,
null,
[
]
);
$event = new EventArgs(
[
],
$request
);
$this->eventDispatcher->dispatch($event, EccubeEvents::FRONT_PRODUCT_CART_ADD_INITIALIZE);
/* @var $form \Symfony\Component\Form\FormInterface */
try {
$form = $builder->getForm();
} catch (\Exception $e) {
$logger->error("Error while creating form: " . $e->getMessage());
return $this->render('error.html.twig', [
'message' => 'An error occurred while processing the form.',
]);
}
$intervalId = $request->request->get('interval_id') ? $request->request->get('interval_id') : null;
$form->handleRequest($request);
if (!$form->isValid()) {
$this->addError('front.product.invalid_quantity');
return $this->redirectToRoute('product_detail', ['id' => $Product->getId()]);
}
$addCartData = $form->getData();
// 一回購入制限商品チェック
if ($Product->getOnlyOne()) {
$Customer = $this->getUser();
$checkResult = $this->orderRepository->checkOnlyOneProductOrder($Product, $Customer);
if (empty($Customer) || !empty($checkResult)) {
$only_one_message = empty($Customer) ? "登録した会員のみ1回購入商品を購入できます。" : "既に購入した1回のみ購入商品は再購入できません。";
if ($request->isXmlHttpRequest()) {
// ajaxでのリクエストの場合は結果をjson形式で返す。
return $this->json(['done' => false, 'messages' => [$only_one_message]]);
} else {
// ajax以外でのリクエストの場合はカート画面へリダイレクト
$this->addRequestError($only_one_message);
return $this->redirectToRoute('cart');
}
}
}
// カートへ追加
$this->cartService->addProduct($request->request->get('ProductClass'), $addCartData['quantity'] ,$intervalId);
// 明細の正規化
$Carts = $this->cartService->getCarts();
foreach ($Carts as $Cart) {
$result = $this->purchaseFlow->validate($Cart, new PurchaseContext($Cart, $this->getUser()));
// 復旧不可のエラーが発生した場合は追加した明細を削除.
if ($result->hasError()) {
$this->cartService->removeProduct($addCartData['product_class_id']);
foreach ($result->getErrors() as $error) {
$errorMessages[] = $error->getMessage();
}
}
foreach ($result->getWarning() as $warning) {
$errorMessages[] = $warning->getMessage();
}
}
$this->cartService->save();
log_info(
'カート追加処理完了',
[
// 'product_id' => $Product->getId(),
// 'product_class_id' => $addCartData['product_class_id'],
// 'quantity' => $addCartData['quantity'],
]
);
$event = new EventArgs(
[
'form' => $form,
'Product' => $Product,
],
$request
);
$this->eventDispatcher->dispatch($event, EccubeEvents::FRONT_PRODUCT_CART_ADD_COMPLETE);
if ($event->getResponse() !== null) {
return $event->getResponse();
}
if ($request->isXmlHttpRequest()) {
// ajaxでのリクエストの場合は結果をjson形式で返す。
// 初期化
$messages = [];
if (empty($errorMessages)) {
// エラーが発生していない場合
$done = true;
array_push($messages, trans('front.product.add_cart_complete'));
} else {
// エラーが発生している場合
$done = false;
$messages = $errorMessages;
}
return $this->json(['done' => $done, 'messages' => $messages]);
} else {
// ajax以外でのリクエストの場合はカート画面へリダイレクト
foreach ($errorMessages as $errorMessage) {
$this->addRequestError($errorMessage);
}
return $this->redirectToRoute('cart');
}
}
/**
* 購入履歴ページで再購入
*
* @Route("/mypage/history/add_cart/{id}", name="history_add_cart", methods={"GET", "POST"}, requirements={"id" = "\d+"})
*/
public function historyAddCart(Request $request, Product $Product)
{
// エラーメッセージの配列
$errorMessages = [];
if (!$this->checkVisibility($Product)) {
throw new NotFoundHttpException();
}
$builder = $this->formFactory->createNamedBuilder(
'',
HistoryAddCartType::class,
null,
[
'product' => $Product,
'id_add_product_id' => false,
]
);
$event = new EventArgs(
[
'builder' => $builder,
'Product' => $Product,
],
$request
);
$this->eventDispatcher->dispatch($event, EccubeEvents::FRONT_PRODUCT_CART_ADD_INITIALIZE);
$form = $builder->getForm();
$form->handleRequest($request);
if (!$form->isValid()) {
throw new NotFoundHttpException();
}
$addCartData = $form->getData();
log_info(
'カート追加処理開始',
[
'product_id' => $Product->getId(),
'product_class_id' => $addCartData['product_class_id'],
'quantity' => $addCartData['quantity'],
]
);
// 一回購入制限商品チェック
if ($Product->getOnlyOne()) {
$Customer = $this->getUser();
$checkResult = $this->orderRepository->checkOnlyOneProductOrder($Product, $Customer);
if (!empty($checkResult)) {
$only_one_message = "既に購入した1回のみ購入商品は再購入できません。";
if ($request->isXmlHttpRequest()) {
// ajaxでのリクエストの場合は結果をjson形式で返す。
return $this->json(['done' => false, 'messages' => [$only_one_message]]);
} else {
// ajax以外でのリクエストの場合はカート画面へリダイレクト
$this->addRequestError($only_one_message);
return $this->redirectToRoute('mypage_history');
}
}
}
// 販売期間商品チェック
$response = $this->checkProductSalePeriod($Product, $request);
if ($response !== null) {
return $response;
}
// カートへ追加
$this->cartService->addProduct($addCartData['product_class_id'], $addCartData['quantity']);
// 明細の正規化
$Carts = $this->cartService->getCarts();
foreach ($Carts as $Cart) {
$result = $this->purchaseFlow->validate($Cart, new PurchaseContext($Cart, $this->getUser()));
// 復旧不可のエラーが発生した場合は追加した明細を削除.
if ($result->hasError()) {
$this->cartService->removeProduct($addCartData['product_class_id']);
foreach ($result->getErrors() as $error) {
$errorMessages[] = $error->getMessage();
}
}
foreach ($result->getWarning() as $warning) {
$errorMessages[] = $warning->getMessage();
}
}
$this->cartService->save();
log_info(
'カート追加処理完了',
[
'product_id' => $Product->getId(),
'product_class_id' => $addCartData['product_class_id'],
'quantity' => $addCartData['quantity'],
]
);
$event = new EventArgs(
[
'form' => $form,
'Product' => $Product,
],
$request
);
$this->eventDispatcher->dispatch($event, EccubeEvents::FRONT_PRODUCT_CART_ADD_COMPLETE);
if ($event->getResponse() !== null) {
return $event->getResponse();
}
if ($request->isXmlHttpRequest()) {
// ajaxでのリクエストの場合は結果をjson形式で返す。
// 初期化
$messages = [];
if (empty($errorMessages)) {
// エラーが発生していない場合
$done = true;
array_push($messages, trans('front.product.add_cart_complete'));
} else {
// エラーが発生している場合
$done = false;
$messages = $errorMessages;
}
return $this->json(['done' => $done, 'messages' => $messages]);
} else {
// ajax以外でのリクエストの場合はカート画面へリダイレクト
foreach ($errorMessages as $errorMessage) {
$this->addRequestError($errorMessage);
}
return $this->redirectToRoute('cart');
}
}
private function checkProductSalePeriod(Product $Product, Request $request)
{
$now = new \DateTime();
$startDate = $Product->getStartDate();
$endDate = $Product->getEndDate();
if (($startDate && $now < $startDate) || ($endDate && $now > $endDate)) {
$period_message = "この商品は販売が終了しています。";
if ($request->isXmlHttpRequest()) {
// ajaxでのリクエストの場合は結果をjson形式で返す。
return $this->json(['done' => false, 'messages' => [$period_message]]);
} else {
// ajax以外でのリクエストの場合はカート画面へリダイレクト
$this->addRequestError($period_message);
return $this->redirectToRoute('cart');
}
}
return null;
}
/**
* 検索バー絞込検索
*
* @Route("/products/list/search", name="product_list_search", methods={"GET"})
*
* @param Request $request
* @return RedirectResponse
*/
public function productListSearch(Request $request): RedirectResponse
{
$keyword = $request->query->get('keyword');
return $this->redirectToRoute('product_list', [
'keyword' => $keyword ,
]);
}
/**
* お気に入り切り替え
*
* @Route("/products/favorite", name="change_favorite", methods={"POST"})
* @param Request $request
* @return string
*/
public function changeFavorite(Request $request)
{
if (!$request->isXmlHttpRequest()) {
throw new BadRequestHttpException();//API通信のみ受け付ける
}
$Product = $this->productRepository->find($request->request->get('product_id'));
$Customer = $this->customerRepository->find($request->request->get('customer_id'));
if(!is_null($Customer) && !is_null($Product)){
if($this->customerFavoriteProductRepository->isFavorite($Customer, $Product)){
$CustomerFavorite = $this->customerFavoriteProductRepository->findOneBy(
[
'Customer' => $Customer->getId(),
'Product' => $Product->getId()]);
$this->customerFavoriteProductRepository->delete($CustomerFavorite);
}else{
$customerFavorit = new CustomerFavoriteProduct();
$customerFavorit->setProduct($Product);
$customerFavorit->setCustomer($Customer);
$this->entityManager->persist($customerFavorit);
}
$this->entityManager->flush();
$this->entityManager->commit();
return $this->json(['success' => true]);
}else{
log_info('会員もしくは商品情報を取得できなかっため、お気に入りの変更に失敗しました。');
return $this->json(['success' => false]);
}
}
/**
* 商品詳細ページでのお気に入り切り替え
*
* @Route("/products/detail/favorite", name="detail_change_favorite", methods={"POST"})
* @param Request $request
* @return JsonResponse
*/
public function detailChangeFavorite(Request $request): JsonResponse
{
if (!$request->isXmlHttpRequest()) {
throw new BadRequestHttpException(); //API通信のみ受け付ける
}
$Product = $this->productRepository->find($request->request->get('product_id'));
$Customer = $this->customerRepository->find($request->request->get('customer_id'));
if (!is_null($Customer) && !is_null($Product)) {
try {
$this->entityManager->beginTransaction(); //トランザクション開始
if ($this->customerFavoriteProductRepository->isFavorite($Customer, $Product)) {
$CustomerFavorite = $this->customerFavoriteProductRepository->findOneBy(['Customer' => $Customer, 'Product' => $Product]);
$this->customerFavoriteProductRepository->delete($CustomerFavorite);
} else {
$customerFavoriteProduct = new CustomerFavoriteProduct();
$customerFavoriteProduct->setProduct($Product);
$customerFavoriteProduct->setCustomer($Customer);
$this->entityManager->persist($customerFavoriteProduct);
}
$this->entityManager->flush();
$this->entityManager->commit(); // トランザクションで、レコードの挿入や削除に対して、コミットが必要
return $this->json(['success' => true]);
} catch (\Exception $e) {
log_info($e->getMessage());
log_info('例外エラーにより、商品詳細ページでの気に入り切り替えに失敗しました。ロールバックします。');
$this->entityManager->rollback();
return $this->json(['success' => false]);
}
} else{
log_info('商品か顧客情報が足りなくて、商品詳細ページでの気に入り切り替えに失敗しました。');
return $this->json(['success' => false]);
}
}
/**
* ページタイトルの設定
*
* @param array|null $searchData
*
* @return string
*/
protected function getPageTitle(?array $searchData): string
{
if (!empty($searchData['name'])) {
return trans('front.product.search_result');
} elseif (isset($searchData['category_id']) && $searchData['category_id']) {
return $searchData['category_id']->getName() . trans('front.product.list.page_name');
} elseif (isset($searchData['product']) && $searchData['product']) {
// (子)カテゴリ名取得
$productCategories = $searchData['product']->getProductCategories();
if (count($productCategories) > 0) {
$hierarchy = 0;
foreach ($productCategories as $ProductCategory) {
$ProductHierarchy = $ProductCategory->getCategory()->getHierarchy();
if($ProductHierarchy > $hierarchy){
$category = $ProductCategory->getCategory();
$hierarchy = $ProductHierarchy;
}
}
}
$category_name = isset($category) ? " | ".$category->getName() : "";
return $searchData['product']->getName() . $category_name . trans('front.product.list.page_name');
} else {
return trans('front.product.all_products');
}
}
/**
* 閲覧可能な商品かどうかを判定
*
* @param Product $Product
*
* @return boolean 閲覧可能な場合はtrue
*/
protected function checkVisibility(Product $Product)
{
$is_admin = $this->session->has('_security_admin');
// 管理ユーザの場合はステータスやオプションにかかわらず閲覧可能.
if (!$is_admin) {
// 在庫なし商品の非表示オプションが有効な場合.
// if ($this->BaseInfo->isOptionNostockHidden()) {
// if (!$Product->getStockFind()) {
// return false;
// }
// }
// 公開ステータスでない商品は表示しない.
if ($Product->getStatus()->getId() !== ProductStatus::DISPLAY_SHOW) {
return false;
}
}
return true;
}
/**
* 商品一覧ページで商品キャンペーン情報取得
*
* @Route("/products/campaign/detail/{product_id}", name="list_campaign_detail", methods={"GET", "POST"}, requirements={"product_id" = "\d+"})
* @Template("Product/list_campaign_detail.twig")
* @param int $product_id
* @return array
* @throws NonUniqueResultException
* @throws NoResultException
*/
public function campaignDetail(int $product_id): array
{
// is login?
$Customer = $this->getUser();
// キャンペーン詳細
$campaignDetail = [];
$campaign_point = 0;
$campaign_price = 0;
$campaign_rate = 0;
$discount_price = 0;
// チェック商品
$Product = $this->productRepository->findOneBy(['id' => $product_id]);
$originalPrice = $Product->getPrice02IncTaxMax();
// キャンペーン商品確認
$campaignProduct = $this->productRepository->checkCampaignProduct($product_id, $Customer);
// キャンペーン情報
if (!empty($campaignProduct) && $campaignProduct->getCampaignDetails()) {
$campaignDetail = $campaignProduct->getCampaignDetails()[0];
if (!empty($campaignDetail->getAddPoint())) {
$campaign_point = $campaignDetail->getAddPoint();
} elseif (!empty($campaignDetail->getDiscountRate())) {
$campaign_rate = (int) $campaignDetail->getDiscountRate();
$campaign_price = $originalPrice * (1 - 0.01 * $campaign_rate);
} elseif (!empty($campaignDetail->getDiscountPrice())) {
$discount_price = (int) $campaignDetail->getDiscountPrice();
$campaign_price = $originalPrice - $discount_price;
}
}
if (empty($campaign_price)) {
$campaign_price = $Product->getPrice02IncTaxMax();
}
return [
'discountprice' => $discount_price,
'Product' => $Product,
'campaignProduct' => $campaignProduct,
'campaignDetail' => $campaignDetail,
'campaign_point' => $campaign_point,
'campaign_price' => $campaign_price,
'campaign_rate' => $campaign_rate,
];
}
/**
* スキンケアステップの表示カテゴリチェック
*
* @param array|null $searchData
*
*/
protected function skinCareDisplayCheck($Product)
{
$categorys = $Product->getCategoryList();
$checkFlag = false;
foreach ($categorys as $category) {
if ($category === 'スキンケア > 化粧水' || $category === 'スキンケア > 保湿ジェル' || $category === 'スキンケア > 保湿クリーム' || $category === 'スキンケア > クレンジング&洗顔' || $category === 'トライアルセット > スキンケア') {
// 対象のカテゴリを含む場合
$checkFlag = true;
break;
}
}
return $checkFlag;
}
}