<?php
namespace Customize\Controller;
use Eccube\Entity\BaseInfo;
use Eccube\Entity\Master\ProductStatus;
use Eccube\Entity\Product;
use Eccube\Event\EccubeEvents;
use Eccube\Event\EventArgs;
use Eccube\Form\Type\AddCartType;
use Eccube\Form\Type\SearchProductType;
use Eccube\Repository\BaseInfoRepository;
use Eccube\Repository\CustomerFavoriteProductRepository;
use Eccube\Repository\Master\ProductListMaxRepository;
use Customize\Repository\ProductRepository;
use Eccube\Service\CartService;
use Eccube\Service\PurchaseFlow\PurchaseContext;
use Eccube\Service\PurchaseFlow\PurchaseFlow;
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\Request;
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;
use Customize\Repository\CategoryRepository;
use Customize\Repository\VisualRepository;
class ProductController extends AbstractController
{
protected $visualRepository;
protected $categoryRepository;
protected $purchaseFlow;
protected $customerFavoriteProductRepository;
protected $cartService;
protected $productRepository;
protected $BaseInfo;
protected $helper;
protected $productListMaxRepository;
public function __construct(
VisualRepository $visualRepository,
CategoryRepository $categoryRepository,
PurchaseFlow $cartPurchaseFlow,
CustomerFavoriteProductRepository $customerFavoriteProductRepository,
CartService $cartService,
ProductRepository $productRepository,
BaseInfoRepository $baseInfoRepository,
AuthenticationUtils $helper,
ProductListMaxRepository $productListMaxRepository
) {
$this->visualRepository = $visualRepository;
$this->categoryRepository = $categoryRepository;
$this->purchaseFlow = $cartPurchaseFlow;
$this->customerFavoriteProductRepository = $customerFavoriteProductRepository;
$this->cartService = $cartService;
$this->productRepository = $productRepository;
$this->BaseInfo = $baseInfoRepository->get();
$this->helper = $helper;
$this->productListMaxRepository = $productListMaxRepository;
}
/**
* 商品一覧画面.
*
* @Route("/products/cartlist/{cateid}", name="product_list", methods={"GET"})
* @Route("/products/searchlist", name="search_list", methods={"GET","POST"})
* @Route("/products/{catename}", name="product_list_by_catename", methods={"GET"})
* @Template("Product/catelist.twig")
*/
public function index(Request $request, PaginatorInterface $paginator, $cateid = 0, $catename = "", $name = "")
{
// メニュー用カテゴリー一覧
$Cate1st = $this->categoryRepository->findOneBy(['id' => 1]);
$sortedChildren = $Cate1st ? $Cate1st->getChildren()->toArray() : [];
usort($sortedChildren, function($a, $b) { // sort_no 昇順でソート
return $a->getSortNo() <=> $b->getSortNo();
});
if(!empty($cateid) || !empty($catename)){
if(!empty($cateid)){
// カテゴリー
$Category = $this->categoryRepository->find($cateid);
} else if(!empty($catename)){
$Category = $this->categoryRepository->findByUrl($catename);
}
//商品一覧
$ProductQb = $this->productRepository->getListByCategory($Category);
// 親カテゴリー情報を取得する
if ($Category) {
$parents = $this->categoryRepository->getPath($Category);
$Category->parents = $parents;
}
$page_no = $request->query->get('pageno', '1');
$pagination = $paginator->paginate(
$ProductQb,
$page_no,
20 // 表示件数/Page 本番では20
);
$pagination->setPageRange(5); //ページャーに表示するページ番号の数
// ページネーションのリンクを手動で作成
$paginationLinks = [];
// 現在のページ番号を取得
$currentPage = $pagination->getCurrentPageNumber();
// ページ範囲の設定(例: 5ページ分)
$pageRange = 5;
// 範囲を計算
$startPage = max(1, $currentPage - floor($pageRange / 2));
$endPage = min($pagination->getPageCount(), $startPage + $pageRange - 1);
// 必要に応じて範囲を調整(最初や最後のページが範囲外にならないように)
if ($endPage - $startPage + 1 < $pageRange) {
$startPage = max(1, $endPage - $pageRange + 1);
}
for ($i = $startPage; $i <= $endPage; $i++) {
if(!empty($cateid)){
$paginationLinks[$i] = $this->generateUrl('product_list', [
'cateid' => $cateid,
'pageno' => $i
]);
} else if(!empty($catename)){
$paginationLinks[$i] = $this->generateUrl('product_list_by_catename', [
'catename' => $catename,
'pageno' => $i
]);
}
}
// 最初へ、前へ、次へ、最後へのリンクを作成
if(!empty($cateid)){
$firstPageLink = $this->generateUrl('product_list', [
'cateid' => $cateid,
'pageno' => 1
]);
} else if(!empty($catename)){
$firstPageLink = $this->generateUrl('product_list_by_catename', [
'catename' => $catename,
'pageno' => 1
]);
}
if(!empty($cateid)){
$previousPageLink = $pagination->getCurrentPageNumber() > 1 ? $this->generateUrl('product_list', [
'cateid' => $cateid,
'pageno' => $pagination->getCurrentPageNumber() - 1
]) : null;
} else if(!empty($catename)){
$previousPageLink = $pagination->getCurrentPageNumber() > 1 ? $this->generateUrl('product_list_by_catename', [
'catename' => $catename,
'pageno' => $pagination->getCurrentPageNumber() - 1
]) : null;
}
if(!empty($cateid)){
$nextPageLink = $pagination->getCurrentPageNumber() < $pagination->getPageCount() ? $this->generateUrl('product_list', [
'cateid' => $cateid,
'pageno' => $pagination->getCurrentPageNumber() + 1
]) : null;
} else if(!empty($catename)){
$nextPageLink = $pagination->getCurrentPageNumber() < $pagination->getPageCount() ? $this->generateUrl('product_list_by_catename', [
'catename' => $catename,
'pageno' => $pagination->getCurrentPageNumber() + 1
]) : null;
}
if(!empty($cateid)){
$lastPageLink = $this->generateUrl('product_list', [
'cateid' => $cateid,
'pageno' => $pagination->getPageCount()
]);
} else if(!empty($catename)){
$lastPageLink = $this->generateUrl('product_list_by_catename', [
'catename' => $catename,
'pageno' => $pagination->getPageCount()
]);
}
if(!empty($Category) && empty($cateid)){
$cateid = $Category->getId();
}
// BookCartバナー
$Banners = $this->visualRepository->findByCategory($cateid);
return [
'pagination' => $pagination,
'Category' => $Category,
'Banners' => $Banners,
'paginationLinks' => $paginationLinks,
'firstPageLink' => $firstPageLink,
'previousPageLink' => $previousPageLink,
'nextPageLink' => $nextPageLink,
'lastPageLink' => $lastPageLink,
'sortedChildren' => $sortedChildren,
];
} else {
// Doctrine SQLFilter
if ($this->BaseInfo->isOptionNostockHidden()) {
$this->entityManager->getFilters()->enable('option_nostock_hidden');
}
$builder = $this->formFactory->createNamedBuilder('', SearchProductType::class);
if ($request->getMethod() === 'GET') {
$builder->setMethod('GET');
}
$searchForm = $builder->getForm();
$searchForm->handleRequest($request);
$searchData = $searchForm->getData();
if(!empty($searchData['name'])){
$name = $searchData['name'];
} else {
$name = "";
}
//商品一覧
$ProductQb = $this->productRepository->getQueryBuilderBySearchData($searchData);
$page_no = $request->query->get('pageno', '1');
$pagination = $paginator->paginate(
$ProductQb,
$page_no,
20 // 表示件数/Page 本番では20
);
$pagination->setPageRange(5); //ページャーに表示するページ番号の数
// ページネーションのリンクを手動で作成
$paginationLinks = [];
// 現在のページ番号を取得
$currentPage = $pagination->getCurrentPageNumber();
// ページ範囲の設定(例: 5ページ分)
$pageRange = 5;
// 範囲を計算
$startPage = max(1, $currentPage - floor($pageRange / 2));
$endPage = min($pagination->getPageCount(), $startPage + $pageRange - 1);
// 必要に応じて範囲を調整(最初や最後のページが範囲外にならないように)
if ($endPage - $startPage + 1 < $pageRange) {
$startPage = max(1, $endPage - $pageRange + 1);
}
for ($i = $startPage; $i <= $endPage; $i++) {
$paginationLinks[$i] = $this->generateUrl('search_list', [
'name' => $name,
'pageno' => $i
]);
}
// 最初へ、前へ、次へ、最後へのリンクを作成
$firstPageLink = $this->generateUrl('search_list', [
'name' => $name,
'pageno' => 1
]);
$previousPageLink = $pagination->getCurrentPageNumber() > 1 ? $this->generateUrl('search_list', [
'name' => $name,
'pageno' => $pagination->getCurrentPageNumber() - 1
]) : null;
$nextPageLink = $pagination->getCurrentPageNumber() < $pagination->getPageCount() ? $this->generateUrl('search_list', [
'name' => $name,
'pageno' => $pagination->getCurrentPageNumber() + 1
]) : null;
$lastPageLink = $this->generateUrl('search_list', [
'name' => $name,
'pageno' => $pagination->getPageCount()
]);
return $this->render(
'Product/searchlist.twig',
[
'pagination' => $pagination,
'subtitle' => $this->getPageTitle($searchData),
'search_form' => $searchForm->createView(),
'paginationLinks' => $paginationLinks,
'firstPageLink' => $firstPageLink,
'previousPageLink' => $previousPageLink,
'nextPageLink' => $nextPageLink,
'lastPageLink' => $lastPageLink,
'sortedChildren' => $sortedChildren,
]
);
}
}
/**
* 商品詳細画面.
*
* @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
*/
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);
$is_favorite = false;
if ($this->isGranted('ROLE_USER')) {
$Customer = $this->getUser();
$is_favorite = $this->customerFavoriteProductRepository->isFavorite($Customer, $Product);
}
$Cate2nd = null;
if (!$Product->getProductCategories()->isEmpty()) {
foreach ($Product->getProductCategories() as $productCategory) {
$Category = $productCategory->getCategory();
if ($Category->getHierarchy() > 1) {
if ($Category->getHierarchy() == 2) {
$Cate2nd = $Category;
break; // 最初に見つけたものを取得したらループ終了
}
}
}
}
// BookCartバナー
if(!empty($Cate2nd)){
$Banners = $this->visualRepository->findByCategory($Cate2nd->getId());
} else {
$Banners = array();
}
// メニュー用カテゴリー一覧
$Cate1st = $this->categoryRepository->findOneBy(['id' => 1]);
$sortedChildren = $Cate1st ? $Cate1st->getChildren()->toArray() : [];
usort($sortedChildren, function($a, $b) { // sort_no 昇順でソート
return $a->getSortNo() <=> $b->getSortNo();
});
// おすすめ商品
$RecommendList = array();
$Cate3rd = null;
if (!$Product->getProductCategories()->isEmpty()) {
foreach ($Product->getProductCategories() as $productCategory) {
$Category = $productCategory->getCategory();
if ($Category->getHierarchy() > 1) {
if ($Category->getHierarchy() == 3) {
$Cate3rd = $Category;
break; // 最初に見つけたものを取得したらループ終了
}
}
}
}
$qb = $this->productRepository->getListByCategory($Cate3rd, 3, $Product);
if(!empty($qb)){
$RecommendList = $qb->getQuery()->getResult();
}
return [
'form' => $builder->getForm()->createView(),
'Product' => $Product,
'Banners' => $Banners,
'sortedChildren' => $sortedChildren,
'RecommendList' => $RecommendList,
];
}
/**
* お気に入り追加.
*
* @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/add_cart/{id}", name="product_add_cart", methods={"POST"}, requirements={"id" = "\d+"})
*/
public function addCart(Request $request, Product $Product)
{
// エラーメッセージの配列
$errorMessages = [];
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_CART_ADD_INITIALIZE);
/* @var $form \Symfony\Component\Form\FormInterface */
$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'],
]
);
// カートへ追加
$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');
}
}
/**
* ページタイトルの設定
*
* @param array|null $searchData
*
* @return str
*/
protected function getPageTitle($searchData)
{
if (isset($searchData['name']) && !empty($searchData['name'])) {
return trans('front.product.search_result');
} elseif (isset($searchData['category_id']) && $searchData['category_id']) {
return $searchData['category_id']->getName();
} 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;
}
}