<?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\SearchProductType;
use Eccube\Repository\BaseInfoRepository;
use Eccube\Repository\CustomerFavoriteProductRepository;
use Eccube\Repository\Master\ProductListMaxRepository;
use Eccube\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\ProductController as BaseController;
use Customize\Repository\CustomizeProductRepository;
use Plugin\RecentlyViewedProducts\Service\RecentlyViewedProductService;
use Eccube\Entity\Master\Pref;
use Doctrine\ORM\EntityManagerInterface;
use Customize\Repository\CustomizeStripeRecOrderRepository;
class CustomizePhotoProductController extends BaseController
{
/**
* @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;
private $title = '';
/**
* @var RecentlyViewedProductService
*/
protected $recentlyViewedProductService;
protected CustomizeStripeRecOrderRepository $customizeStripeRecOrderRepository;
/**
* ProductController constructor.
*
* @param PurchaseFlow $cartPurchaseFlow
* @param CustomerFavoriteProductRepository $customerFavoriteProductRepository
* @param CartService $cartService
* @param ProductRepository $productRepository
* @param BaseInfoRepository $baseInfoRepository
* @param AuthenticationUtils $helper
* @param ProductListMaxRepository $productListMaxRepository
*/
public function __construct(
PurchaseFlow $cartPurchaseFlow,
CustomerFavoriteProductRepository $customerFavoriteProductRepository,
CartService $cartService,
ProductRepository $productRepository,
BaseInfoRepository $baseInfoRepository,
AuthenticationUtils $helper,
ProductListMaxRepository $productListMaxRepository,
RecentlyViewedProductService $recentlyViewedProductService,
CustomizeStripeRecOrderRepository $customizeStripeRecOrderRepository
) {
$this->purchaseFlow = $cartPurchaseFlow;
$this->customerFavoriteProductRepository = $customerFavoriteProductRepository;
$this->cartService = $cartService;
$this->productRepository = $productRepository;
$this->BaseInfo = $baseInfoRepository->get();
$this->helper = $helper;
$this->productListMaxRepository = $productListMaxRepository;
$this->recentlyViewedProductService = $recentlyViewedProductService;
$this->customizeStripeRecOrderRepository = $customizeStripeRecOrderRepository;
}
/**
* 商品一覧画面.
*
* @Route("/photo-products/list", name="photo-product_list", methods={"GET"})
* @Template("@user_data/photo-product_list.twig")
*/
public function index(Request $request, PaginatorInterface $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($event, EccubeEvents::FRONT_PRODUCT_INDEX_INITIALIZE);
/* @var $searchForm \Symfony\Component\Form\FormInterface */
$searchForm = $builder->getForm();
$searchForm->handleRequest($request);
//ここからカスタマイズ
// 撮影アングル(チェックボックスは配列なので all() で)
$selectedAngles = $request->query->all('camera_angles');
if (empty($selectedAngles)) {
// 互換(もし過去に vehicle_types[] だった場合)
$selectedAngles = $request->query->all('vehicle_types');
}
$selectedAngles = array_values(array_filter(array_map('trim', $selectedAngles), fn($v) => $v !== ''));
// カラー種別
$selectedColors = $request->query->all('color_types');
$selectedColors = array_values(array_filter(array_map('trim', $selectedColors), fn($v) => $v !== ''));
//ここまでカスタマイズ
// paginator
$searchData = $searchForm->getData();
$qb = $this->productRepository->getQueryBuilderBySearchData($searchData);
//ここからカスタマイズ
//paginationに、SaleType.idが2(画像商品)しか渡さないようにする
$sub = $this->entityManager->createQueryBuilder()
->select('MIN(pc2.id)')
->from(\Eccube\Entity\ProductClass::class, 'pc2')
->where('pc2.Product = p')
->getDQL();
$qb
->innerJoin('p.ProductClasses', 'pc3')
->innerJoin('pc3.SaleType', 'st')
->andWhere($qb->expr()->eq('pc3.id', '(' . $sub . ')'))
->andWhere('st.id = 2');
// ─ 撮影アングル(複数チェック → OR LIKE) ─
if (!empty($selectedAngles)) {
$orX = $qb->expr()->orX();
foreach ($selectedAngles as $i => $val) {
$param = "ang{$i}";
$orX->add($qb->expr()->like('p.camera_angle', ":{$param}"));
$qb->setParameter($param, '%'.$val.'%');
}
$qb->andWhere($orX);
}
// ─ カラー種別(参考:同じパターン) ─
if (!empty($selectedColors)) {
$orY = $qb->expr()->orX();
foreach ($selectedColors as $i => $val) {
$param = "col{$i}";
$orY->add($qb->expr()->like('p.color_type', ":{$param}"));
$qb->setParameter($param, '%'.$val.'%');
}
$qb->andWhere($orY);
}
//ここまでカスタマイズ
$event = new EventArgs(
[
'searchData' => $searchData,
'qb' => $qb,
],
$request
);
$this->eventDispatcher->dispatch($event, EccubeEvents::FRONT_PRODUCT_INDEX_SEARCH);
$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();
}
$Category = $searchForm->get('category_id')->getData();
//カスタマイズ開始
//category_idを元に、検索欄を動的に変更
//operator(運行会社)
$operators = [];
if ($Category) {
// 該当カテゴリを持つ商品の ProductClass を取得
$qbOperator = $this->entityManager->createQueryBuilder()
->select('DISTINCT p.operator') // 重複しない operator を抽出
->from(\Eccube\Entity\ProductClass::class, 'pc')
->join('pc.Product', 'p')
->join('p.ProductCategories', 'pcat')
->where('pcat.Category = :category')
->setParameter('category', $Category);
$operatorResults = $qbOperator->getQuery()->getResult();
foreach ($operatorResults as $row) {
// operator は string の場合と null の場合があるので検査
if (isset($row['operator']) && $row['operator'] !== '') {
$operators[] = $row['operator'];
}
}
}
//photographing_period(撮影年代)
$photographing_periods = [];
if ($Category) {
// 該当カテゴリを持つ商品の ProductClass を取得
$qbPhotographingPeriod = $this->entityManager->createQueryBuilder()
->select('DISTINCT p.photographing_period') // 重複しない photographing_period を抽出
->from(\Eccube\Entity\ProductClass::class, 'pc')
->join('pc.Product', 'p')
->join('p.ProductCategories', 'pcat')
->where('pcat.Category = :category')
->setParameter('category', $Category);
$photographingPeriodrResults = $qbPhotographingPeriod->getQuery()->getResult();
foreach ($photographingPeriodrResults as $row) {
// photographing_period は string の場合と null の場合があるので検査
if (isset($row['photographing_period']) && $row['photographing_period'] !== '') {
$photographing_periods[] = $row['photographing_period'];
}
}
}
//photographing_period(撮影年代)
$photographing_periods = [];
if ($Category) {
// 該当カテゴリを持つ商品の ProductClass を取得
$qbPhotographingPeriod = $this->entityManager->createQueryBuilder()
->select('DISTINCT p.photographing_period') // 重複しない operator を抽出
->from(\Eccube\Entity\ProductClass::class, 'pc')
->join('pc.Product', 'p')
->join('p.ProductCategories', 'pcat')
->where('pcat.Category = :category')
->setParameter('category', $Category);
$photographingPeriodrResults = $qbPhotographingPeriod->getQuery()->getResult();
foreach ($photographingPeriodrResults as $row) {
// photographing_period は string の場合と null の場合があるので検査
if (isset($row['photographing_period']) && $row['photographing_period'] !== '') {
$photographing_periods[] = $row['photographing_period'];
}
}
}
//location(撮影地)
$locations = [];
if ($Category) {
// 該当カテゴリを持つ商品の ProductClass を取得
$qbVehicleType = $this->entityManager->createQueryBuilder()
->select('DISTINCT p.location') // 重複しない location を抽出
->from(\Eccube\Entity\ProductClass::class, 'pc')
->join('pc.Product', 'p')
->join('p.ProductCategories', 'pcat')
->where('pcat.Category = :category')
->setParameter('category', $Category);
$vehicleTypeResults = $qbVehicleType->getQuery()->getResult();
foreach ($vehicleTypeResults as $row) {
// location は string の場合と null の場合があるので検査
if (isset($row['location']) && $row['location'] !== '') {
$locations[] = $row['location'];
}
}
}
//weather(撮影天候)
$weathers = [];
if ($Category) {
// 該当カテゴリを持つ商品の ProductClass を取得
$qbTrainType = $this->entityManager->createQueryBuilder()
->select('DISTINCT p.weather') // 重複しない weather を抽出
->from(\Eccube\Entity\ProductClass::class, 'pc')
->join('pc.Product', 'p')
->join('p.ProductCategories', 'pcat')
->where('pcat.Category = :category')
->setParameter('category', $Category);
$trainTypeResults = $qbTrainType->getQuery()->getResult();
foreach ($trainTypeResults as $row) {
// weather は string の場合と null の場合があるので検査
if (isset($row['weather']) && $row['weather'] !== '') {
$weathers[] = $row['weather'];
}
}
}
//camera_angle(撮影アングル)
$camera_angles = [];
if ($Category) {
// 該当カテゴリを持つ商品の ProductClass を取得
$qbAngleCand = $this->entityManager->createQueryBuilder()
->select('DISTINCT p.camera_angle AS angle') // 重複しない camera_angle を抽出
->from(\Eccube\Entity\ProductClass::class, 'pc')
->join('pc.Product', 'p')
->join('p.ProductCategories', 'pcat')
->where('pcat.Category = :category')
->setParameter('category', $Category);
$rows = $qbAngleCand->getQuery()->getResult();
$tmp = [];
foreach ($rows as $r) {
$raw = $r['angle'] ?? null;
if (!is_string($raw) || $raw === '') continue;
// 1レコードに複数語("正面, 俯瞰" など)が入る可能性に対応
foreach (preg_split('/[,\s、]+/u', $raw, -1, PREG_SPLIT_NO_EMPTY) as $name) {
$name = trim($name);
if ($name === '') continue;
$tmp[] = $name;
}
}
// 重複排除
$camera_angles = array_values(array_unique($tmp));
// (任意)表示順を固定したい場合
$order = ['右頭','左頭','正面','側面','俯瞰'];
$pos = array_flip($order);
usort($camera_angles, fn($a,$b)=>($pos[$a]??PHP_INT_MAX)<=>($pos[$b]??PHP_INT_MAX) ?: strcmp($a,$b));
}
//color_type(撮影方式)
$color_types = [];
if ($Category) {
// 該当カテゴリを持つ商品の ProductClass を取得
$qbColorType = $this->entityManager->createQueryBuilder()
->select('DISTINCT p.color_type') // 重複しない color_type を抽出
->from(\Eccube\Entity\ProductClass::class, 'pc')
->join('pc.Product', 'p')
->join('p.ProductCategories', 'pcat')
->where('pcat.Category = :category')
->setParameter('category', $Category);
$colorTypeResults = $qbColorType->getQuery()->getResult();
foreach ($colorTypeResults as $row) {
// color_type は string の場合と null の場合があるので検査
if (isset($row['color_type']) && $row['color_type'] !== '') {
$color_types[] = $row['color_type'];
}
}
}
//feature(一押し)
$features = [];
if ($Category) {
// 該当カテゴリを持つ商品の ProductClass を取得
$qbTrainType = $this->entityManager->createQueryBuilder()
->select('DISTINCT p.feature') // 重複しない feature を抽出
->from(\Eccube\Entity\ProductClass::class, 'pc')
->join('pc.Product', 'p')
->join('p.ProductCategories', 'pcat')
->where('pcat.Category = :category')
->setParameter('category', $Category);
$trainTypeResults = $qbTrainType->getQuery()->getResult();
foreach ($trainTypeResults as $row) {
// feature は string の場合と null の場合があるので検査
if (isset($row['feature']) && $row['feature'] !== '') {
$features[] = $row['feature'];
}
}
}
//prefectures(撮影地(都道府県))
$prefectures = [];
if ($Category) {
// 該当カテゴリを持つ商品の ProductClass を取得
$qbPref = $this->entityManager->createQueryBuilder()
->select('DISTINCT p.prefectures') // 重複しない prefectures を抽出
->from(\Eccube\Entity\ProductClass::class, 'pc')
->join('pc.Product', 'p')
->join('p.ProductCategories', 'pcat')
->where('pcat.Category = :category')
->setParameter('category', $Category);
$prefResults = $qbPref->getQuery()->getResult();
$existingSet = []; // 集合(キーのみ使用)
foreach ($prefResults as $row) {
// prefectures は string の場合と null の場合があるので検査
$raw = $row['prefectures'] ?? null;
if (!is_string($raw) || $raw === '') {
continue;
}
// 全角スペース→半角、前後空白除去
$raw = trim(mb_convert_kana($raw, 's')); // s: スペースを半角に
// 区切りで分割(, / スペース / 全角読点)
$parts = preg_split('/[,\s、]+/u', $raw, -1, PREG_SPLIT_NO_EMPTY);
foreach ($parts as $name) {
$name = trim($name);
if ($name === '') continue;
// 表記ゆれ補正
if (isset($aliasMap[$name])) {
$name = $aliasMap[$name];
}
$existingSet[$name] = true; // 集合化(重複排除)
}
}
// 3) JIS順マスター(この順序を“常に”使う)
$jisPrefNames = [
'北海道','青森県','岩手県','宮城県','秋田県','山形県','福島県',
'茨城県','栃木県','群馬県','埼玉県','千葉県','東京都','神奈川県',
'新潟県','富山県','石川県','福井県','山梨県','長野県',
'岐阜県','静岡県','愛知県','三重県','滋賀県','京都府','大阪府','兵庫県','奈良県','和歌山県',
'鳥取県','島根県','岡山県','広島県','山口県',
'徳島県','香川県','愛媛県','高知県',
'福岡県','佐賀県','長崎県','熊本県','大分県','宮崎県','鹿児島県','沖縄県',
];
// 4) マスター順で「存在する県だけ」を抽出 → これが最終配列
$prefectures = [];
foreach ($jisPrefNames as $name) {
if (isset($existingSet[$name])) {
$prefectures[] = $name;
}
}
}
//カスタマイズ終了
return [
'subtitle' => $this->getPageTitle($searchData),
'pagination' => $pagination,
'search_form' => $searchForm->createView(),
'forms' => $forms,
'Category' => $Category,
'operators' => $operators,
'photographing_periods' => $photographing_periods,
'locations' => $locations,
'weathers' => $weathers,
'camera_angles' => $camera_angles,
'color_types' => $color_types,
'features' => $features,
'prefectures' => $prefectures,
];
}
//カスタマイズ開始
// 関連商品を取得する共通関数
public function getRelatedProducts(array $Products, array $currentKeywords, int $limit = 10)
{
$productScores = [];
foreach ($Products as $product) {
$keywords = [];
// operator
if (!empty($product->getOperator())) {
$keywords[] = trim($product->getOperator());
}
// route_name
if (!empty($product->getRouteName())) {
$keywords[] = trim($product->getRouteName());
}
// location
if (!empty($product->getLocation())) {
$keywords[] = trim($product->getLocation());
}
// weather
if (!empty($product->getWeather())) {
$keywords[] = trim($product->getWeather());
}
// camera_angle
if (!empty($product->getCameraAngle())) {
$keywords[] = trim($product->getCameraAngle());
}
// feature
if (!empty($product->getFeature())) {
$keywords[] = trim($product->getFeature());
}
// prefectures
if (!empty($product->getPrefectures())) {
$keywords[] = trim($product->getPrefectures());
}
// name
if (!empty($product->getName())) {
$keywords[] = trim($product->getName());
}
// search_word(カンマ区切り → 各単語を 1 倍で参照)
if ($product->getSearchWord()) {
$words = array_map('trim', explode(',', $product->getSearchWord()));
$keywords = array_merge($keywords, $words);
}
// category_name
foreach ($product->getProductCategories() as $ProductCategory) {
$category = $ProductCategory->getCategory();
if ($category && $category->getName()) {
$keywords[] = trim($category->getName());
}
}
$keywords = array_unique($keywords);
// スコア計算(重み付き)
$score = 0;
foreach ($currentKeywords as $keyword) {
if (in_array($keyword, $keywords, true)) {
// 重み判定
if ($keyword === trim($product->getOperator())) {
$score += 10;
} elseif ($keyword === trim($product->getRouteName())) {
$score += 10;
} elseif ($keyword === trim($product->getName())) {
$score += 10;
} else {
// search_word, location, related_station, category_name → +1
$score += 1;
}
}
}
$productScores[] = [
'product' => $product,
'score' => $score,
];
}
usort($productScores, function($a, $b) {
return $b['score'] <=> $a['score'];
});
return array_map(function($item) {
return $item['product'];
}, array_slice($productScores, 0, $limit));
}
//カスタマイズ終了
/**
* 商品詳細画面.
*
* @Route("/photo-products/detail/{id}", name="photo-product_detail", methods={"GET"}, requirements={"id" = "\d+"})
* @Template("@user_data/photo-detail.twig")
* @ParamConverter("Product", options={"repository_method" = "findWithSortedClassCategories"})
*
* @param Request $request
* @param Product $Product
*
* @return array
*/
public function detail(Request $request, Product $Product)
{
$Customer = $this->getUser();
if (!$this->checkVisibility($Product)) {
throw new NotFoundHttpException();
}
// Stripe 定期購入申し込み履歴取得
if(!$Customer){
return $this->redirectToRoute('subscription_detail', [
'id' => 2004,
]);
}else{
$qb_rec = $this->customizeStripeRecOrderRepository->getRecOrdersQueryBuilderByCustomer($Customer);
$qb_rec
->leftJoin('ro.OrderItems', 'roi')
->addSelect('roi');
// qb実行(サブスクステータス)
$stripe_rec_orders = $qb_rec->getQuery()->getResult();
// ① 空なら return
if (empty($stripe_rec_orders)) {
return $this->redirectToRoute('subscription_detail', [
'id' => 2004,
]);
}
// ② 最初の要素を取得
$firstRecOrder = reset($stripe_rec_orders);
// ③ RecStatus 判定
if (in_array($firstRecOrder->getRecStatus(), ['canceled', 'scheduled_canceled'], true) && $Customer->getPoint() == 0) {
return $this->redirectToRoute('subscription_detail', [
'id' => 2004,
]);
}
}
$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);
}
//あなたへのおすすめ機能実装
$productRepository = $this->getDoctrine()->getRepository(\Eccube\Entity\Product::class);
// ProductにTagもJOINして取得する
$qb = $productRepository->createQueryBuilder('p')
->leftJoin('p.ProductTag', 'pt')
->leftJoin('pt.Tag', 't')
->leftJoin('p.ProductCategories', 'pc')
->leftJoin('pc.Category', 'c')
->addSelect('pt')
->addSelect('t')
->addSelect('pc')
->addSelect('c')
->where('p.Status = 1')
->orderBy('p.id', 'DESC');
$Products = $qb->getQuery()->getResult();
//カスタマイズ開始
// 関連書籍の取得
$rqb = $productRepository->createQueryBuilder('p')
->innerJoin('p.ProductClasses', 'pc')
->innerJoin('p.ProductCategories', 'pcat')
->innerJoin('pcat.Category', 'c')
->where('p.Status = :status')
->andWhere('pc.SaleType = :saleType')
->andWhere('pc.stock > 0')
->setParameter('status', 1)
->setParameter('saleType', 1);
$BookProducts = $rqb->getQuery()->getResult();
// 関連写真の取得
$rqp = $productRepository->createQueryBuilder('p')
->innerJoin('p.ProductClasses', 'pc')
->innerJoin('p.ProductCategories', 'pcat')
->innerJoin('pcat.Category', 'c')
->where('p.Status = :status')
->andWhere('pc.SaleType = :saleType')
->setParameter('status', 1)
->setParameter('saleType', 2);
$PhotoProducts = $rqp->getQuery()->getResult();
// 現在の商品から特徴語を作成
$currentKeywords = [];
foreach ([
$Product->getOperator(),
$Product->getRouteName(),
$Product->getLocation(),
$Product->getWeather(),
$Product->getCameraAngle(),
$Product->getFeature(),
$Product->getName(),
] as $val) {
if (!empty($val)) {
$currentKeywords[] = trim($val);
}
}
if ($Product->getSearchWord()) {
$words = array_map('trim', explode(',', $Product->getSearchWord()));
$currentKeywords = array_merge($currentKeywords, $words);
}
foreach ($Product->getProductCategories() as $ProductCategory) {
$category = $ProductCategory->getCategory();
if ($category && $category->getName()) {
$currentKeywords[] = trim($category->getName());
}
}
$currentKeywords = array_unique($currentKeywords);
// 関数で relatedBooks, relatedPhotos を作成
$relatedBooks = $this->getRelatedProducts($BookProducts, $currentKeywords);
$relatedPhotos = $this->getRelatedProducts($PhotoProducts, $currentKeywords);
//カスタマイズ終了
//カスタマイズ開始(購入履歴があるかないかで購入ボタンの表記を変える)
$purchaseDate = null;
if ($this->getUser()) {
$Customer = $this->getUser();
$purchaseqb = $this->entityManager->createQueryBuilder();
$purchaseqb->select('o')
->from(\Eccube\Entity\Order::class, 'o')
->leftJoin('o.OrderItems', 'oi')
->leftJoin('oi.Product', 'p')
->where('o.Customer = :Customer')
->andWhere('p.id = :ProductId')
->andWhere('o.OrderStatus NOT IN (:excludedStatus)')
->orderBy('o.order_date', 'DESC')
->setMaxResults(1)
->setParameter('Customer', $Customer)
->setParameter('ProductId', $Product->getId())
->setParameter('excludedStatus', [7]);
$order = $purchaseqb->getQuery()->getOneOrNullResult();
if ($order) {
$purchaseDate = $order->getOrderDate();
}
}
//カスタマイズ終了
return [
'title' => $this->title,
'subtitle' => $Product->getName(),
'form' => $builder->getForm()->createView(),
'Product' => $Product,
'is_favorite' => $is_favorite,
'relatedBooks' => $relatedBooks, //← 追加
'relatedPhotos' => $relatedPhotos, //← 追加
'purchaseDate' => $purchaseDate, //← 追加
'Customer' => $Customer, //← 追加
];
}
/**
* お気に入り追加.
*
* @Route("/stockphoto-products/add_favorite/{id}", name="stockphoto-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('photo-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("/stockphoto-products/add_cart/{id}", name="stockphoto-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;
}
}