<?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 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\Master\ProductListMaxType;
use Eccube\Form\Type\Master\ProductListOrderByType;
use Eccube\Form\Type\SearchProductType;
use Eccube\Repository\BaseInfoRepository;
use Eccube\Repository\CustomerFavoriteProductRepository;
use Eccube\Repository\Master\ProductListMaxRepository;
use Eccube\Repository\ProductRepository;
use Eccube\Repository\ProductCategoryRepository;
use Eccube\Service\CartService;
use Eccube\Service\PurchaseFlow\PurchaseContext;
use Eccube\Service\PurchaseFlow\PurchaseFlow;
use Knp\Bundle\PaginatorBundle\Pagination\SlidingPagination;
use Knp\Component\Pager\Paginator;
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 Doctrine\ORM\EntityManagerInterface;
//以下Customize様Use
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 BaseInfo
*/
protected $BaseInfo;
/**
* @var AuthenticationUtils
*/
protected $helper;
/**
* @var ProductListMaxRepository
*/
protected $productListMaxRepository;
/**
* @var EntityManagerInterface
*/
protected $entityManager;
private $title = '';
/**
* ProductController constructor.
*
* @param PurchaseFlow $cartPurchaseFlow
* @param CustomerFavoriteProductRepository $customerFavoriteProductRepository
* @param CartService $cartService
* @param ProductRepository $productRepository
* @param ProductCategoryRepository $productRepository
* @param BaseInfoRepository $baseInfoRepository
* @param AuthenticationUtils $helper
* @param ProductListMaxRepository $productListMaxRepository
*/
public function __construct(
PurchaseFlow $cartPurchaseFlow,
CustomerFavoriteProductRepository $customerFavoriteProductRepository,
CartService $cartService,
ProductRepository $productRepository,
ProductCategoryRepository $productCategoryRepository,
BaseInfoRepository $baseInfoRepository,
AuthenticationUtils $helper,
ProductListMaxRepository $productListMaxRepository,
EntityManagerInterface $entityManager
) {
$this->purchaseFlow = $cartPurchaseFlow;
$this->customerFavoriteProductRepository = $customerFavoriteProductRepository;
$this->cartService = $cartService;
$this->productRepository = $productRepository;
$this->BaseInfo = $baseInfoRepository->get();
$this->helper = $helper;
$this->productListMaxRepository = $productListMaxRepository;
$this->entityManager = $entityManager;
$this->productCategoryRepository = $productCategoryRepository;
}
/**
* 商品一覧画面.
*
* @Route("/products/list", name="product_list")
* @Template("Product/list.twig")
*/
public function index(Request $request, Paginator $paginator)
{
// 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', ''));
}
// searchForm
/* @var $builder \Symfony\Component\Form\FormBuilderInterface */
$builder = $this->formFactory->createNamedBuilder('', SearchProductType::class);
if ($request->getMethod() === 'GET') {
$builder->setMethod('GET');
}
$event = new EventArgs(
[
'builder' => $builder,
],
$request
);
$this->eventDispatcher->dispatch(EccubeEvents::FRONT_PRODUCT_INDEX_INITIALIZE, $event);
/* @var $searchForm \Symfony\Component\Form\FormInterface */
$searchForm = $builder->getForm();
$searchForm->handleRequest($request);
// paginator
$searchData = $searchForm->getData();
$qb = $this->productRepository->getQueryBuilderBySearchData($searchData);
$event = new EventArgs(
[
'searchData' => $searchData,
'qb' => $qb,
],
$request
);
$this->eventDispatcher->dispatch(EccubeEvents::FRONT_PRODUCT_INDEX_SEARCH, $event);
$searchData = $event->getArgument('searchData');
$query = $qb->getQuery()
->useResultCache(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()
);
$ids = [];
foreach ($pagination as $Product) {
$ids[] = $Product->getId();
}
$ProductsAndClassCategories = $this->productRepository->findProductsWithSortedClassCategories($ids, 'p.id');
// addCart form
$forms = [];
foreach ($pagination as $Product) {
/* @var $builder \Symfony\Component\Form\FormBuilderInterface */
$builder = $this->formFactory->createNamedBuilder(
'',
AddCartType::class,
null,
[
'product' => $ProductsAndClassCategories[$Product->getId()],
'allow_extra_fields' => true,
]
);
$addCartForm = $builder->getForm();
$forms[$Product->getId()] = $addCartForm->createView();
}
// 表示件数
$builder = $this->formFactory->createNamedBuilder(
'disp_number',
ProductListMaxType::class,
null,
[
'required' => false,
'allow_extra_fields' => true,
]
);
if ($request->getMethod() === 'GET') {
$builder->setMethod('GET');
}
$event = new EventArgs(
[
'builder' => $builder,
],
$request
);
$this->eventDispatcher->dispatch(EccubeEvents::FRONT_PRODUCT_INDEX_DISP, $event);
$dispNumberForm = $builder->getForm();
$dispNumberForm->handleRequest($request);
// ソート順
$builder = $this->formFactory->createNamedBuilder(
'orderby',
ProductListOrderByType::class,
null,
[
'required' => false,
'allow_extra_fields' => true,
]
);
if ($request->getMethod() === 'GET') {
$builder->setMethod('GET');
}
$event = new EventArgs(
[
'builder' => $builder,
],
$request
);
$this->eventDispatcher->dispatch(EccubeEvents::FRONT_PRODUCT_INDEX_ORDER, $event);
$orderByForm = $builder->getForm();
$orderByForm->handleRequest($request);
$Category = $searchForm->get('category_id')->getData();
// print_r($Category);
/*
*mv画像のパスの設定
*/
if(!empty($_GET['category_id'])){
$mv_path_pc = "/html/template/raspia/img/low/low_cate_id_".$_GET['category_id'].".webp";
$mv_path_sp = "/html/template/raspia/img/low/low_cate_id_".$_GET['category_id']."_sp.webp";
}else{
$mv_path_pc = "/html/template/raspia/img/low/low_cate_allitem.webp";
$mv_path_sp = "/html/template/raspia/img/low/low_cate_allitem_sp.webp";
}
//ファイルがある場合は対応する命名規則のファイルない場合はsample画像
if(!file_exists($_SERVER['DOCUMENT_ROOT'].$mv_path_pc)){
$mv_path_pc = "/html/template/raspia/img/low/low_cate_noimage.webp";
}
if(!file_exists($_SERVER['DOCUMENT_ROOT'].$mv_path_sp)){
$mv_path_sp = "/html/template/raspia/img/low/low_cate_noimage_sp.webp";
}
$Categories = [];
if($request->query->get('categories')){
$subtitle = "";
$categories = $this->getCategories(explode(',',$request->query->get('categories')));
$category_names = [];
foreach($categories as $key => $category){
if($category!==null){
$category_names[] = $category->getName();
} else{
return $this->redirect('https://raspia.com/products/list',301);
}
}
$Categories['title'] = implode(" ",$category_names);
$Categories['keyword'] = implode(",",$category_names);
}
$subtitle = $this->getPageTitle($searchData);
if($pagination->count() <= 5 && isset($request->query->all()['categories'])){
if($request->query->all()['categories'] != ""
){
$ids = explode(',',$request->query->all()['categories']);
$highPriorityIds = [];
foreach($ids as $key => $id){
$category = $this->getCategory($id);
if(!empty($category->getParent())){
if(in_array($category->getParent()->getId(),[12,24,44])){
$highPriorityIds[] = $id;
unset($ids[$key]);
}
}
}
$ignoreIds = [];
foreach($pagination as $key => $product){
$ignoreIds[] = $product->getId();
}
$HighPrioritySimilarProducts = [];
foreach($highPriorityIds as $key => $id){
$qb = $this->entityManager->createQueryBuilder([]);
$query = $qb->select("p")
->from("Eccube\\Entity\\Product","p")
->innerJoin("p.ProductCategories","pc")
->andWhere("p.Status = 1")
->andWhere($qb->expr()->in('pc.Category', ':Categories'))
->setParameter("Categories",[$id])
->groupBy("p.id")
;
if(count($ignoreIds)) $query->andWhere($qb->expr()->notIn("p.id",$ignoreIds));
$HighPrioritySimilarProducts = array_merge($HighPrioritySimilarProducts,$query->getQuery()->getResult());
}
foreach($HighPrioritySimilarProducts as $key => $product){
$ignoreIds[] = $product->getId();
}
$SimilarProducts = [];
$max = (count($HighPrioritySimilarProducts) > 15)?0: 15 - count($HighPrioritySimilarProducts) ;
$qb = $this->entityManager->createQueryBuilder([]);
$query = $qb->select("p")
->from("Eccube\\Entity\\Product","p")
->innerJoin("p.ProductCategories","pc")
->andWhere($qb->expr()->in('pc.Category', ':Categories'))
->andWhere('p.Status = 1')
->setParameter("Categories",$ids)
->groupBy("p.id")
;
if(count($ignoreIds)) $query->andWhere($qb->expr()->notIn("p.id",$ignoreIds));
$SimilarProducts = $query->getQuery()->getResult();
shuffle($SimilarProducts);
// unset();
$SimilarProducts = array_unique($SimilarProducts);
$SimilarProducts = array_values($SimilarProducts);
$SimilarProducts = array_slice($SimilarProducts,0,$max);
$SimilarProducts = array_merge($HighPrioritySimilarProducts,$SimilarProducts);
}else{
$SimilarProducts = "";
}
}else{
$SimilarProducts = "";
}
$Params = $this->getParams();
$SearchKeywords = implode(', ',$this->getSearchKeywords($Params));
return [
'subtitle' => $subtitle,
'pagination' => $pagination,
'mv_path_pc' => $mv_path_pc,
'mv_path_sp' => $mv_path_sp,
'search_form' => $searchForm->createView(),
'disp_number_form' => $dispNumberForm->createView(),
'order_by_form' => $orderByForm->createView(),
'forms' => $forms,
'Category' => $Category,
'Categories' => $Categories,
'SimilarProducts' => $SimilarProducts,
'Params' => $Params,
'SearchKeywords' => $SearchKeywords,
];
}
private function getParams(){
// $Params = $_GET;
if(isset($_GET['category_id']) && !empty($_GET['category_id'])){
$_GET['multi_category'][$_GET['category_id']] = $_GET['category_id'];
}
if(isset($_GET['categories']) && !empty($_GET['categories'])){
$categories = explode(',',$_GET['categories']);
foreach ($categories as $key => $category) {
$_GET['multi_category'][$category] = $category;
}
}
// if(isset($Params['mu']))
return $_GET;
}
private function getSearchKeywords($params){
$qb = $this->entityManager->createQueryBuilder([]);
$query = $qb->select("c")
->from("Eccube\\Entity\\Category","c")
->getQuery();
$allCategories = $query->getResult();
$keywords = [];
//カテゴリの取得
if(isset($params['multi_category']) && !empty($params['multi_category'])){
foreach($allCategories as $key => $category){
if(in_array($category->getId(),$params['multi_category'])){
$keywords[] = $category->getName();
}
}
}
//キーワード
if(isset($params['name']) && !empty($params['name'])){$keywords[] = $params['name'];}
//価格の絞り込み
if(isset($params['pricerange'])){
foreach($params['pricerange'] as $key => $pricerange){
if(empty($pricerange)) break;
$range = explode('-',$pricerange);
$keywords[] = number_format($range[0])."円 〜 ".number_format($range[1])."円";
}
}
if(count($keywords) == 0)$keywords[] ="全ての商品";
return $keywords;
}
private function getSearchCategories(){
$qb = $this->entityManager->createQueryBuilder([]);
$query = $qb->select("c")
->from("Eccube\\Entity\\Category","c")
->getQuery();
$allCategories = $query->getResult();
$SearchCategories = [];
foreach( $allCategories as $keyu => $category ){
if(in_array($category->getId(),[7,137,14,113,115])){
$SearchCategories['item'][$category->getId()] = $category;
}elseif($category->getId() == 24){
$SearchCategories['jewely'][$category->getId()] = $category;
}elseif($category->getId() == 44){
$SearchCategories['birthStone'][$category->getId()] = $category;
}elseif($category->getId() == 12){
$SearchCategories['color'][$category->getId()] = $category;
}elseif($category->getId() == 6){
$SearchCategories['series'][$category->getId()] = $category;
}elseif($category->getId() == 41){
$SearchCategories['material'][$category->getId()] = $category;
}
}
return $SearchCategories;
}
/*
* カテゴリの名前を配列で取得
*
*/
private function getCategories($category_ids = []){
$categories = [];
foreach($category_ids as $key => $category_id){
$qb = $this->entityManager->createQueryBuilder([]);
$query = $qb->select("c")
->from("Eccube\\Entity\\Category","c")
->where('c.id = :id')
->setParameter('id',$category_id)
->getQuery();
$categories[] = $query->getResult()[0];
}
return $categories;
}
/*
* カテゴリの名前を配列で取得
*
*/
private function getCategory($category_id = ""){
$qb = $this->entityManager->createQueryBuilder([]);
$query = $qb->select("c")
->from("Eccube\\Entity\\Category","c")
->where('c.id = :id')
->setParameter('id',$category_id)
->getQuery();
return $query->getResult()[0];
}
/**
* 商品詳細画面.
*
* @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(EccubeEvents::FRONT_PRODUCT_DETAIL_INITIALIZE, $event);
$is_favorite = false;
if ($this->isGranted('ROLE_USER')) {
$Customer = $this->getUser();
$is_favorite = $this->customerFavoriteProductRepository->isFavorite($Customer, $Product);
}
//スライダー画像の
$SliderImages = [];
if($Product->getSlider()){
$SliderImages = explode("\n",$Product->getSlider());
}
return [
'title' => $this->title,
'subtitle' => $Product->getName(),
'form' => $builder->getForm()->createView(),
'Product' => $Product,
'is_favorite' => $is_favorite,
'SliderImages' => $SliderImages,
];
}
/**
* お気に入り追加.
*
* @Route("/products/add_favorite/{id}", name="product_add_favorite", requirements={"id" = "\d+"})
*/
public function addFavorite(Request $request, Product $Product)
{
$this->checkVisibility($Product);
$event = new EventArgs(
[
'Product' => $Product,
],
$request
);
$this->eventDispatcher->dispatch(EccubeEvents::FRONT_PRODUCT_FAVORITE_ADD_INITIALIZE, $event);
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(EccubeEvents::FRONT_PRODUCT_FAVORITE_ADD_COMPLETE, $event);
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(EccubeEvents::FRONT_PRODUCT_FAVORITE_ADD_COMPLETE, $event);
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(EccubeEvents::FRONT_PRODUCT_CART_ADD_INITIALIZE, $event);
/* @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(EccubeEvents::FRONT_PRODUCT_CART_ADD_COMPLETE, $event);
if ($event->getResponse() !== null) {
return $event->getResponse();
}
if ($request->isXmlHttpRequest()) {
// ajaxでのリクエストの場合は結果をjson形式で返す。
// 初期化
$done = null;
$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 null|array $searchData
*
* @return str
*/
protected function getPageTitle($searchData)
{
if (
(isset($searchData['name']) && !empty($searchData['name'])) ||
(isset($_GET['multi_category']) && !empty($_GET['multi_category']))
) {
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;
}
}