<?php
namespace Eccube\Controller\Admin\Product;
use Doctrine\DBAL\Exception\ForeignKeyConstraintViolationException;
use Eccube\Common\Constant;
use Eccube\Controller\AbstractController;
use Eccube\Entity\BaseInfo;
use Eccube\Entity\ExportCsvRow;
use Eccube\Entity\Master\CsvType;
use Eccube\Entity\Master\ProductStatus;
use Eccube\Entity\Product;
use Eccube\Entity\ProductCategory;
use Eccube\Entity\ProductClass;
use Eccube\Entity\ProductImage;
use Eccube\Entity\ProductStock;
use Eccube\Entity\ProductTag;
use Eccube\Event\EccubeEvents;
use Eccube\Event\EventArgs;
use Eccube\Form\Type\Admin\ProductType;
use Eccube\Form\Type\Admin\SearchProductType;
use Eccube\Repository\BaseInfoRepository;
use Eccube\Repository\CategoryRepository;
use Eccube\Repository\Master\PageMaxRepository;
use Eccube\Repository\Master\ProductStatusRepository;
use Eccube\Repository\ProductClassRepository;
use Eccube\Repository\ProductImageRepository;
use Eccube\Repository\ProductRepository;
use Eccube\Repository\TagRepository;
use Eccube\Repository\TaxRuleRepository;
use Eccube\Service\CsvExportService;
use Eccube\Util\CacheUtil;
use Eccube\Util\FormUtil;
use Knp\Component\Pager\PaginatorInterface;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use Symfony\Component\Asset\Packages;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\StreamedResponse;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpKernel\Exception\UnsupportedMediaTypeHttpException;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Routing\RouterInterface;
class ProductController extends AbstractController {
/**
* @var CsvExportService
*/
protected $csvExportService;
/**
* @var ProductClassRepository
*/
protected $productClassRepository;
/**
* @var ProductImageRepository
*/
protected $productImageRepository;
/**
* @var TaxRuleRepository
*/
protected $taxRuleRepository;
/**
* @var CategoryRepository
*/
protected $categoryRepository;
/**
* @var ProductRepository
*/
protected $productRepository;
/**
* @var BaseInfo
*/
protected $BaseInfo;
/**
* @var PageMaxRepository
*/
protected $pageMaxRepository;
/**
* @var ProductStatusRepository
*/
protected $productStatusRepository;
/**
* @var TagRepository
*/
protected $tagRepository;
/**
* @var \Eccube\Repository\CustomerFavoriteProductRepository
*/
protected $customerFavoriteProductRepository;
/**
* @var \Eccube\Repository\OrderRepository
*/
protected $orderRepository;
/**
* @var \Plugin\ProductPlus42\Repository\ProductDataDetailRepository
*/
protected $productDataDetailRepository;
/**
* @var \Plugin\CustomerRank42\Repository\CustomerPriceRepository
*/
protected $customerPriceRepository;
/**
* @var \Plugin\CustomerPlus\Repository\CustomerDataDetailRepository
*/
protected $customerPlusDataDetailRepository;
/**
* @var \Plugin\CustomerPlus\Repository\CustomerItemOptionRepository
*/
protected $customerPlusItemOptionRepository;
/**
* @var Packages
*/
protected $packages;
/**
* ProductController constructor.
*
* @param CsvExportService $csvExportService
* @param ProductClassRepository $productClassRepository
* @param ProductImageRepository $productImageRepository
* @param TaxRuleRepository $taxRuleRepository
* @param CategoryRepository $categoryRepository
* @param ProductRepository $productRepository
* @param BaseInfoRepository $baseInfoRepository
* @param PageMaxRepository $pageMaxRepository
* @param ProductStatusRepository $productStatusRepository
* @param TagRepository $tagRepository
* @param \Eccube\Repository\CustomerFavoriteProductRepository $customerFavoriteProductRepository
* @param \Eccube\Repository\OrderRepository $orderRepository
* @param \Plugin\ProductPlus42\Repository\ProductDataDetailRepository $productDataDetailRepository
* @param \Plugin\CustomerRank42\Repository\CustomerPriceRepository $customerPriceRepository
*/
public function __construct(
CsvExportService $csvExportService,
ProductClassRepository $productClassRepository,
ProductImageRepository $productImageRepository,
TaxRuleRepository $taxRuleRepository,
CategoryRepository $categoryRepository,
ProductRepository $productRepository,
BaseInfoRepository $baseInfoRepository,
PageMaxRepository $pageMaxRepository,
ProductStatusRepository $productStatusRepository,
TagRepository $tagRepository,
\Eccube\Repository\CustomerFavoriteProductRepository $customerFavoriteProductRepository,
\Eccube\Repository\OrderRepository $orderRepository,
\Plugin\ProductPlus42\Repository\ProductDataDetailRepository $productDataDetailRepository,
\Plugin\CustomerRank42\Repository\CustomerPriceRepository $customerPriceRepository,
\Plugin\CustomerPlus\Repository\CustomerDataDetailRepository $customerPlusDataDetailRepository,
\Plugin\CustomerPlus\Repository\CustomerItemOptionRepository $customerPlusItemOptionRepository,
Packages $packages
) {
$this->csvExportService = $csvExportService;
$this->productClassRepository = $productClassRepository;
$this->productImageRepository = $productImageRepository;
$this->taxRuleRepository = $taxRuleRepository;
$this->categoryRepository = $categoryRepository;
$this->productRepository = $productRepository;
$this->BaseInfo = $baseInfoRepository->get();
$this->pageMaxRepository = $pageMaxRepository;
$this->productStatusRepository = $productStatusRepository;
$this->tagRepository = $tagRepository;
$this->customerFavoriteProductRepository = $customerFavoriteProductRepository;
$this->orderRepository = $orderRepository;
$this->productDataDetailRepository = $productDataDetailRepository;
$this->customerPriceRepository = $customerPriceRepository;
$this->customerPlusDataDetailRepository = $customerPlusDataDetailRepository;
$this->customerPlusItemOptionRepository = $customerPlusItemOptionRepository;
$this->packages = $packages;
}
/**
* @Route("/%eccube_admin_route%/product", name="admin_product", methods={"GET", "POST"})
* @Route("/%eccube_admin_route%/product/page/{page_no}", requirements={"page_no" = "\d+"}, name="admin_product_page", methods={"GET", "POST"})
* @Template("@admin/Product/index.twig")
*/
public function index(Request $request, PaginatorInterface $paginator, $page_no = null) {
$builder = $this->formFactory
->createBuilder(SearchProductType::class);
$event = new EventArgs(
[
'builder' => $builder,
], $request
);
$this->eventDispatcher->dispatch($event, EccubeEvents::ADMIN_PRODUCT_INDEX_INITIALIZE);
$searchForm = $builder->getForm();
/**
* ページの表示件数は, 以下の順に優先される.
* - リクエストパラメータ
* - セッション
* - デフォルト値
* また, セッションに保存する際は mtb_page_maxと照合し, 一致した場合のみ保存する.
* */
$page_count = $this->session->get('eccube.admin.product.search.page_count', $this->eccubeConfig->get('eccube_default_page_count'));
$page_count_param = (int) $request->get('page_count');
$pageMaxis = $this->pageMaxRepository->findAll();
if ($page_count_param) {
foreach ($pageMaxis as $pageMax) {
if ($page_count_param == $pageMax->getName()) {
$page_count = $pageMax->getName();
$this->session->set('eccube.admin.product.search.page_count', $page_count);
break;
}
}
}
if ('POST' === $request->getMethod()) {
$searchForm->handleRequest($request);
if ($searchForm->isValid()) {
/**
* 検索が実行された場合は, セッションに検索条件を保存する.
* ページ番号は最初のページ番号に初期化する.
*/
$page_no = 1;
$searchData = $searchForm->getData();
// 検索条件, ページ番号をセッションに保持.
$this->session->set('eccube.admin.product.search', FormUtil::getViewData($searchForm));
$this->session->set('eccube.admin.product.search.page_no', $page_no);
} else {
// 検索エラーの際は, 詳細検索枠を開いてエラー表示する.
return [
'searchForm' => $searchForm->createView(),
'pagination' => [],
'pageMaxis' => $pageMaxis,
'page_no' => $page_no,
'page_count' => $page_count,
'has_errors' => true,
];
}
} else {
if (null !== $page_no || $request->get('resume')) {
/*
* ページ送りの場合または、他画面から戻ってきた場合は, セッションから検索条件を復旧する.
*/
if ($page_no) {
// ページ送りで遷移した場合.
$this->session->set('eccube.admin.product.search.page_no', (int)$page_no);
} else {
// 他画面から遷移した場合.
$page_no = $this->session->get('eccube.admin.product.search.page_no', 1);
}
$viewData = $this->session->get('eccube.admin.product.search', []);
$searchData = FormUtil::submitAndGetData($searchForm, $viewData);
} else {
/**
* 初期表示の場合.
*/
$page_no = 1;
// submit default value
$viewData = FormUtil::getViewData($searchForm);
$searchData = FormUtil::submitAndGetData($searchForm, $viewData);
// セッション中の検索条件, ページ番号を初期化.
$this->session->set('eccube.admin.product.search', $viewData);
$this->session->set('eccube.admin.product.search.page_no', $page_no);
}
}
// ヘッダー検索(multiパラメータ)の対応
if(!empty($_GET["multi"])) {
$searchData["status"] = "";
$searchData["id"] = $_GET["multi"]; // 商品名・商品ID・商品コード・検索ワード・鑑定番号で検索
}
$qb = $this->productRepository->getQueryBuilderBySearchDataForAdmin($searchData);
$event = new EventArgs(
[
'qb' => $qb,
'searchData' => $searchData,
], $request
);
$this->eventDispatcher->dispatch($event, EccubeEvents::ADMIN_PRODUCT_INDEX_SEARCH);
$qb->andWhere('pc.SaleType != :saleType')
->setParameter('saleType', 2);
$sortKey = $searchData['sortkey'];
if (empty($this->productRepository::COLUMNS[$sortKey]) || $sortKey == 'code' || $sortKey == 'status') {
$pagination = $paginator->paginate(
$qb, $page_no, $page_count
);
} else {
$pagination = $paginator->paginate(
$qb, $page_no, $page_count, ['wrap-queries' => true]
);
}
// 各種データをハッシュで取得
$htKantei = $this->productDataDetailRepository->getHash(2); // 鑑定番号
$htPrice = $this->customerPriceRepository->getHash(2); // レギュラー会員価格
$htOwner = $this->productDataDetailRepository->getHash(40); // オーナー名
$htOwnerId = $this->productDataDetailRepository->getHash(6); // オーナーID
$htPrice2 = $this->productDataDetailRepository->getHash(55); // 目安価格
$htOrder = $this->orderRepository->getHashCustomerName(); // 購入者
$htFavorite = $this->customerFavoriteProductRepository->getHashNum(); // お気に入り数
$htType = $this->productRepository->getHashType(); // 商品種別
$htJigane = $this->productRepository->getHashJigane(); // 地金型コイン
$htMibun = $this->customerPlusDataDetailRepository->getHash(20); // 身分証明書の種別 (customer_id → option_id)
$htMibunNm = $this->customerPlusItemOptionRepository->getHash(20); // option_id → 種別名
$htProductOrderCnt = $this->orderRepository->getHashOrderCountByProduct(); // 受注件数
return [
'searchForm' => $searchForm->createView(),
'pagination' => $pagination,
'pageMaxis' => $pageMaxis,
'page_no' => $page_no,
'page_count' => $page_count,
'has_errors' => false,
'htKantei' => $htKantei,
'htPrice' => $htPrice,
'htOwner' => $htOwner,
'htOwnerId' => $htOwnerId,
'htPrice2' => $htPrice2,
'htOrder' => $htOrder,
'htFavorite' => $htFavorite,
'htType' => $htType,
'htJigane' => $htJigane,
'htMibun' => $htMibun,
'htMibunNm' => $htMibunNm,
'htProductOrderCnt' => $htProductOrderCnt,
];
}
/**
* @Route("/%eccube_admin_route%/product/classes/{id}/load", name="admin_product_classes_load", methods={"GET"}, requirements={"id" = "\d+"}, methods={"GET"})
* @Template("@admin/Product/product_class_popup.twig")
* @ParamConverter("Product", options={"repository_method":"findWithSortedClassCategories"})
*/
public function loadProductClasses(Request $request, Product $Product) {
if (!$request->isXmlHttpRequest() && $this->isTokenValid()) {
throw new BadRequestHttpException();
}
$data = [];
/** @var $Product ProductRepository */
if (!$Product) {
throw new NotFoundHttpException();
}
if ($Product->hasProductClass()) {
$class = $Product->getProductClasses();
foreach ($class as $item) {
$data[] = $item;
}
}
return [
'data' => $data,
];
}
/**
* 画像アップロード時にリクエストされるメソッド.
*
* @see https://pqina.nl/filepond/docs/api/server/#process
* @Route("/%eccube_admin_route%/product/product/image/process", name="admin_product_image_process", methods={"POST"})
*/
public function imageProcess(Request $request) {
if (!$request->isXmlHttpRequest() && $this->isTokenValid()) {
throw new BadRequestHttpException();
}
$images = $request->files->get('admin_product');
$allowExtensions = ['gif', 'jpg', 'jpeg', 'png', 'webp'];
$files = [];
if (count($images) > 0) {
foreach ($images as $img) {
foreach ($img as $image) {
// ファイルフォーマット検証
$mimeType = $image->getMimeType();
if (0 !== strpos($mimeType, 'image')) {
throw new UnsupportedMediaTypeHttpException();
}
// 拡張子
$extension = $image->getClientOriginalExtension();
if (!in_array(strtolower($extension), $allowExtensions)) {
throw new UnsupportedMediaTypeHttpException();
}
$filename = date('mdHis') . uniqid('_') . '.' . $extension;
$image->move($this->eccubeConfig['eccube_temp_image_dir'], $filename);
$files[] = $filename;
}
}
}
$event = new EventArgs(
[
'images' => $images,
'files' => $files,
], $request
);
$this->eventDispatcher->dispatch($event, EccubeEvents::ADMIN_PRODUCT_ADD_IMAGE_COMPLETE);
$files = $event->getArgument('files');
return new Response(array_shift($files));
}
/**
* アップロード画像を取得する際にコールされるメソッド.
*
* @see https://pqina.nl/filepond/docs/api/server/#load
* @Route("/%eccube_admin_route%/product/product/image/load", name="admin_product_image_load", methods={"GET"})
*/
public function imageLoad(Request $request) {
if (!$request->isXmlHttpRequest()) {
throw new BadRequestHttpException();
}
$dirs = [
$this->eccubeConfig['eccube_save_image_dir'],
$this->eccubeConfig['eccube_temp_image_dir'],
];
foreach ($dirs as $dir) {
if (strpos($request->query->get('source'), '..') !== false) {
throw new NotFoundHttpException();
}
$image = \realpath($dir . '/' . $request->query->get('source'));
$dir = \realpath($dir);
if (\is_file($image) && \str_starts_with($image, $dir)) {
$file = new \SplFileObject($image);
return $this->file($file, $file->getBasename());
}
}
throw new NotFoundHttpException();
}
/**
* アップロード画像をすぐ削除する際にコールされるメソッド.
*
* @see https://pqina.nl/filepond/docs/api/server/#revert
* @Route("/%eccube_admin_route%/product/product/image/revert", name="admin_product_image_revert", methods={"DELETE"})
*/
public function imageRevert(Request $request) {
if (!$request->isXmlHttpRequest() && $this->isTokenValid()) {
throw new BadRequestHttpException();
}
$tempFile = $this->eccubeConfig['eccube_temp_image_dir'] . '/' . $request->getContent();
if (is_file($tempFile) && stripos(realpath($tempFile), $this->eccubeConfig['eccube_temp_image_dir']) === 0) {
$fs = new Filesystem();
$fs->remove($tempFile);
return new Response(null, Response::HTTP_NO_CONTENT);
}
throw new NotFoundHttpException();
}
/**
* 商品説明(リッチテキストエディタ)に挿入する画像をアップロードする.
*
* @Route("/%eccube_admin_route%/product/product/description/image", name="admin_product_description_image_upload", methods={"POST"})
* @IsGranted("ROLE_ADMIN")
*/
public function uploadDescriptionImage(Request $request) {
if (!$request->isXmlHttpRequest()) {
throw new BadRequestHttpException();
}
$this->isTokenValid();
$image = $request->files->get('file');
if (!$image) {
throw new BadRequestHttpException();
}
$allowExtensions = ['gif', 'jpg', 'jpeg', 'png', 'webp'];
$mimeType = $image->getMimeType();
if (0 !== strpos((string) $mimeType, 'image')) {
throw new UnsupportedMediaTypeHttpException();
}
$extension = strtolower($image->getClientOriginalExtension());
if (!in_array($extension, $allowExtensions, true)) {
throw new UnsupportedMediaTypeHttpException();
}
$filename = date('mdHis') . uniqid('_') . '.' . $extension;
$image->move($this->eccubeConfig['eccube_save_image_dir'], $filename);
return $this->json(['location' => $this->packages->getUrl($filename, 'save_image')]);
}
/**
* @Route("/%eccube_admin_route%/product/product/new", name="admin_product_product_new", methods={"GET", "POST"})
* @Route("/%eccube_admin_route%/product/product/{id}/edit", requirements={"id" = "\d+"}, name="admin_product_product_edit", methods={"GET", "POST"})
* @Template("@admin/Product/product.twig")
*/
public function edit(Request $request, RouterInterface $router, CacheUtil $cacheUtil, $id = null) {
$has_class = false;
if (is_null($id)) {
$Product = new Product();
$ProductClass = new ProductClass();
$ProductStatus = $this->productStatusRepository->find(ProductStatus::DISPLAY_HIDE);
$Product
->addProductClass($ProductClass)
->setStatus($ProductStatus);
$ProductClass
->setVisible(true)
->setStockUnlimited(true)
->setProduct($Product);
$ProductStock = new ProductStock();
$ProductClass->setProductStock($ProductStock);
$ProductStock->setProductClass($ProductClass);
} else {
$Product = $this->productRepository->findWithSortedClassCategories($id);
$ProductClass = null;
$ProductStock = null;
if (!$Product) {
throw new NotFoundHttpException();
}
// 規格無しの商品の場合は、デフォルト規格を表示用に取得する
$has_class = $Product->hasProductClass();
if (!$has_class) {
$ProductClasses = $Product->getProductClasses();
foreach ($ProductClasses as $pc) {
if (!is_null($pc->getClassCategory1())) {
continue;
}
if ($pc->isVisible()) {
$ProductClass = $pc;
break;
}
}
if ($this->BaseInfo->isOptionProductTaxRule() && $ProductClass->getTaxRule()) {
$ProductClass->setTaxRate($ProductClass->getTaxRule()->getTaxRate());
}
$ProductStock = $ProductClass->getProductStock();
}
}
$builder = $this->formFactory
->createBuilder(ProductType::class, $Product);
// 規格あり商品の場合、規格関連情報をFormから除外
if ($has_class) {
$builder->remove('class');
}
$event = new EventArgs(
[
'builder' => $builder,
'Product' => $Product,
], $request
);
$this->eventDispatcher->dispatch($event, EccubeEvents::ADMIN_PRODUCT_EDIT_INITIALIZE);
$form = $builder->getForm();
if (!$has_class) {
$ProductClass->setStockUnlimited($ProductClass->isStockUnlimited());
$form['class']->setData($ProductClass);
}
// ファイルの登録
$images = [];
$ProductImages = $Product->getProductImage();
foreach ($ProductImages as $ProductImage) {
$images[] = $ProductImage->getFileName();
}
$form['images']->setData($images);
$categories = [];
$ProductCategories = $Product->getProductCategories();
foreach ($ProductCategories as $ProductCategory) {
/* @var $ProductCategory \Eccube\Entity\ProductCategory */
$categories[] = $ProductCategory->getCategory();
}
$form['Category']->setData($categories);
$Tags = $Product->getTags();
$form['Tag']->setData($Tags);
if ('POST' === $request->getMethod()) {
$form->handleRequest($request);
if ($form->isValid()) {
log_info('商品登録開始', [$id]);
$Product = $form->getData();
if (!$has_class) {
$ProductClass = $form['class']->getData();
// 個別消費税
if ($this->BaseInfo->isOptionProductTaxRule()) {
if ($ProductClass->getTaxRate() !== null) {
if ($ProductClass->getTaxRule()) {
$ProductClass->getTaxRule()->setTaxRate($ProductClass->getTaxRate());
} else {
$taxrule = $this->taxRuleRepository->newTaxRule();
$taxrule->setTaxRate($ProductClass->getTaxRate());
$taxrule->setApplyDate(new \DateTime());
$taxrule->setProduct($Product);
$taxrule->setProductClass($ProductClass);
$ProductClass->setTaxRule($taxrule);
}
$ProductClass->getTaxRule()->setTaxRate($ProductClass->getTaxRate());
} else {
if ($ProductClass->getTaxRule()) {
$this->taxRuleRepository->delete($ProductClass->getTaxRule());
$ProductClass->setTaxRule(null);
}
}
}
$this->entityManager->persist($ProductClass);
// 在庫情報を作成
if (!$ProductClass->isStockUnlimited()) {
$ProductStock->setStock($ProductClass->getStock());
} else {
// 在庫無制限時はnullを設定
$ProductStock->setStock(null);
}
$this->entityManager->persist($ProductStock);
}
// カテゴリの登録
// 一度クリア
/* @var $Product \Eccube\Entity\Product */
foreach ($Product->getProductCategories() as $ProductCategory) {
$Product->removeProductCategory($ProductCategory);
$this->entityManager->remove($ProductCategory);
}
$this->entityManager->persist($Product);
$this->entityManager->flush();
$count = 1;
$Categories = $form->get('Category')->getData();
$categoriesIdList = [];
foreach ($Categories as $Category) {
foreach ($Category->getPath() as $ParentCategory) {
if (!isset($categoriesIdList[$ParentCategory->getId()])) {
$ProductCategory = $this->createProductCategory($Product, $ParentCategory, $count);
$this->entityManager->persist($ProductCategory);
$count++;
/* @var $Product \Eccube\Entity\Product */
$Product->addProductCategory($ProductCategory);
$categoriesIdList[$ParentCategory->getId()] = true;
}
}
if (!isset($categoriesIdList[$Category->getId()])) {
$ProductCategory = $this->createProductCategory($Product, $Category, $count);
$this->entityManager->persist($ProductCategory);
$count++;
/* @var $Product \Eccube\Entity\Product */
$Product->addProductCategory($ProductCategory);
$categoriesIdList[$Category->getId()] = true;
}
}
// 画像の登録
$add_images = $form->get('add_images')->getData();
foreach ($add_images as $add_image) {
$ProductImage = new \Eccube\Entity\ProductImage();
$ProductImage
->setFileName($add_image)
->setProduct($Product)
->setSortNo(1);
$Product->addProductImage($ProductImage);
$this->entityManager->persist($ProductImage);
// 移動
$file = new File($this->eccubeConfig['eccube_temp_image_dir'] . '/' . $add_image);
$file->move($this->eccubeConfig['eccube_save_image_dir']);
}
// 画像の削除
$delete_images = $form->get('delete_images')->getData();
$fs = new Filesystem();
foreach ($delete_images as $delete_image) {
$ProductImage = $this->productImageRepository->findOneBy([
'Product' => $Product,
'file_name' => $delete_image,
]);
if ($ProductImage instanceof ProductImage) {
$Product->removeProductImage($ProductImage);
$this->entityManager->remove($ProductImage);
$this->entityManager->flush();
// 他に同じ画像を参照する商品がなければ画像ファイルを削除
if (!$this->productImageRepository->findOneBy(['file_name' => $delete_image])) {
$fs->remove($this->eccubeConfig['eccube_save_image_dir'] . '/' . $delete_image);
}
} else {
// 追加してすぐに削除した画像は、Entityに追加されない
$fs->remove($this->eccubeConfig['eccube_temp_image_dir'] . '/' . $delete_image);
}
}
$this->entityManager->flush();
if (array_key_exists('product_image', $request->request->get('admin_product'))) {
$product_image = $request->request->get('admin_product')['product_image'];
foreach ($product_image as $sortNo => $filename) {
$ProductImage = $this->productImageRepository
->findOneBy([
'file_name' => pathinfo($filename, PATHINFO_BASENAME),
'Product' => $Product,
]);
if ($ProductImage !== null) {
$ProductImage->setSortNo($sortNo);
$this->entityManager->persist($ProductImage);
}
}
$this->entityManager->flush();
}
// 商品タグの登録
// 商品タグを一度クリア
$ProductTags = $Product->getProductTag();
foreach ($ProductTags as $ProductTag) {
$Product->removeProductTag($ProductTag);
$this->entityManager->remove($ProductTag);
}
// 商品タグの登録
$Tags = $form->get('Tag')->getData();
foreach ($Tags as $Tag) {
$ProductTag = new ProductTag();
$ProductTag
->setProduct($Product)
->setTag($Tag);
$Product->addProductTag($ProductTag);
$this->entityManager->persist($ProductTag);
}
$Product->setUpdateDate(new \DateTime());
$this->entityManager->flush();
log_info('商品登録完了', [$id]);
$event = new EventArgs(
[
'form' => $form,
'Product' => $Product,
], $request
);
$this->eventDispatcher->dispatch($event, EccubeEvents::ADMIN_PRODUCT_EDIT_COMPLETE);
$this->addSuccess('admin.common.save_complete', 'admin');
if ($returnLink = $form->get('return_link')->getData()) {
try {
// $returnLinkはpathの形式で渡される. pathが存在するかをルータでチェックする.
$pattern = '/^' . preg_quote($request->getBasePath(), '/') . '/';
$returnLink = preg_replace($pattern, '', $returnLink);
$result = $router->match($returnLink);
// パラメータのみ抽出
$params = array_filter($result, function ($key) {
return 0 !== \strpos($key, '_');
}, ARRAY_FILTER_USE_KEY);
// pathからurlを再構築してリダイレクト.
return $this->redirectToRoute($result['_route'], $params);
} catch (\Exception $e) {
// マッチしない場合はログ出力してスキップ.
log_warning('URLの形式が不正です。');
}
}
$cacheUtil->clearDoctrineCache();
return $this->redirectToRoute('admin_product_product_edit', ['id' => $Product->getId()]);
}
}
// 検索結果の保持
$builder = $this->formFactory
->createBuilder(SearchProductType::class);
$event = new EventArgs(
[
'builder' => $builder,
'Product' => $Product,
], $request
);
$this->eventDispatcher->dispatch($event, EccubeEvents::ADMIN_PRODUCT_EDIT_SEARCH);
$searchForm = $builder->getForm();
if ('POST' === $request->getMethod()) {
$searchForm->handleRequest($request);
}
// Get Tags
$TagsList = $this->tagRepository->getList();
// ツリー表示のため、ルートからのカテゴリを取得
$TopCategories = $this->categoryRepository->getList(null);
$ChoicedCategoryIds = array_map(function ($Category) {
return $Category->getId();
}, $form->get('Category')->getData());
return [
'Product' => $Product,
'Tags' => $Tags,
'TagsList' => $TagsList,
'form' => $form->createView(),
'searchForm' => $searchForm->createView(),
'has_class' => $has_class,
'id' => $id,
'TopCategories' => $TopCategories,
'ChoicedCategoryIds' => $ChoicedCategoryIds,
'orderCount' => $id ? $this->orderRepository->getOrderCountByProductId((int)$id) : 0,
];
}
/**
* @Route("/%eccube_admin_route%/product/product/{id}/delete", requirements={"id" = "\d+"}, name="admin_product_product_delete", methods={"DELETE"})
*/
public function delete(Request $request, CacheUtil $cacheUtil, $id = null) {
$this->isTokenValid();
$session = $request->getSession();
$page_no = intval($session->get('eccube.admin.product.search.page_no'));
$page_no = $page_no ? $page_no : Constant::ENABLED;
$success = false;
if (!is_null($id)) {
/* @var $Product \Eccube\Entity\Product */
$Product = $this->productRepository->find($id);
if (!$Product) {
if ($request->isXmlHttpRequest()) {
$message = trans('admin.common.delete_error_already_deleted');
return $this->json(['success' => $success, 'message' => $message]);
} else {
$this->deleteMessage();
$rUrl = $this->generateUrl('admin_product_page', ['page_no' => $page_no]) . '?resume=' . Constant::ENABLED;
return $this->redirect($rUrl);
}
}
if ($Product instanceof Product) {
log_info('商品削除開始', [$id]);
$ProductClasses = $Product->getProductClasses();
try {
// 論理削除(del_flg = 1)
$Product->setDelFlg(1);
$this->entityManager->persist($Product);
$this->entityManager->flush();
$event = new EventArgs(
[
'Product' => $Product,
'ProductClass' => $ProductClasses,
], $request
);
$this->eventDispatcher->dispatch($event, EccubeEvents::ADMIN_PRODUCT_DELETE_COMPLETE);
// 論理削除のため画像ファイルは削除しない
log_info('商品削除完了(論理削除)', [$id]);
$success = true;
$message = trans('admin.common.delete_complete');
$cacheUtil->clearDoctrineCache();
} catch (ForeignKeyConstraintViolationException $e) {
log_info('商品削除エラー', [$id]);
$message = trans('admin.common.delete_error_foreign_key', ['%name%' => $Product->getName()]);
}
} else {
log_info('商品削除エラー', [$id]);
$message = trans('admin.common.delete_error');
}
} else {
log_info('商品削除エラー', [$id]);
$message = trans('admin.common.delete_error');
}
if ($request->isXmlHttpRequest()) {
return $this->json(['success' => $success, 'message' => $message]);
} else {
if ($success) {
$this->addSuccess($message, 'admin');
} else {
$this->addError($message, 'admin');
}
$rUrl = $this->generateUrl('admin_product_page', ['page_no' => $page_no]) . '?resume=' . Constant::ENABLED;
return $this->redirect($rUrl);
}
}
/**
* @Route("/%eccube_admin_route%/product/product/{id}/copy", requirements={"id" = "\d+"}, name="admin_product_product_copy", methods={"POST"})
*/
public function copy(Request $request, $id = null) {
$this->isTokenValid();
if (!is_null($id)) {
$Product = $this->productRepository->find($id);
if ($Product instanceof Product) {
$CopyProduct = clone $Product;
$CopyProduct->copy();
$ProductStatus = $this->productStatusRepository->find(ProductStatus::DISPLAY_HIDE);
$CopyProduct->setStatus($ProductStatus);
$CopyProductCategories = $CopyProduct->getProductCategories();
foreach ($CopyProductCategories as $Category) {
$this->entityManager->persist($Category);
}
// 規格あり商品の場合は, デフォルトの商品規格を取得し登録する.
if ($CopyProduct->hasProductClass()) {
$dummyClass = $this->productClassRepository->findOneBy([
'visible' => false,
'ClassCategory1' => null,
'ClassCategory2' => null,
'Product' => $Product,
]);
$dummyClass = clone $dummyClass;
$dummyClass->setProduct($CopyProduct);
$CopyProduct->addProductClass($dummyClass);
}
$CopyProductClasses = $CopyProduct->getProductClasses();
foreach ($CopyProductClasses as $Class) {
$Stock = $Class->getProductStock();
$CopyStock = clone $Stock;
$CopyStock->setProductClass($Class);
$this->entityManager->persist($CopyStock);
$TaxRule = $Class->getTaxRule();
if ($TaxRule) {
$CopyTaxRule = clone $TaxRule;
$CopyTaxRule->setProductClass($Class);
$CopyTaxRule->setProduct($CopyProduct);
$this->entityManager->persist($CopyTaxRule);
}
$this->entityManager->persist($Class);
}
$Images = $CopyProduct->getProductImage();
foreach ($Images as $Image) {
// 画像ファイルを新規作成
$extension = pathinfo($Image->getFileName(), PATHINFO_EXTENSION);
$filename = date('mdHis') . uniqid('_') . '.' . $extension;
try {
$fs = new Filesystem();
$fs->copy($this->eccubeConfig['eccube_save_image_dir'] . '/' . $Image->getFileName(), $this->eccubeConfig['eccube_save_image_dir'] . '/' . $filename);
} catch (\Exception $e) {
// エラーが発生しても無視する
}
$Image->setFileName($filename);
$this->entityManager->persist($Image);
}
$Tags = $CopyProduct->getProductTag();
foreach ($Tags as $Tag) {
$this->entityManager->persist($Tag);
}
$this->entityManager->persist($CopyProduct);
$this->entityManager->flush();
$event = new EventArgs(
[
'Product' => $Product,
'CopyProduct' => $CopyProduct,
'CopyProductCategories' => $CopyProductCategories,
'CopyProductClasses' => $CopyProductClasses,
'images' => $Images,
'Tags' => $Tags,
], $request
);
$this->eventDispatcher->dispatch($event, EccubeEvents::ADMIN_PRODUCT_COPY_COMPLETE);
$this->addSuccess('admin.product.copy_complete', 'admin');
return $this->redirectToRoute('admin_product_product_edit', ['id' => $CopyProduct->getId()]);
} else {
$this->addError('admin.product.copy_error', 'admin');
}
} else {
$msg = trans('admin.product.copy_error');
$this->addError($msg, 'admin');
}
return $this->redirectToRoute('admin_product');
}
/**
* 商品CSVの出力.
*
* @Route("/%eccube_admin_route%/product/export", name="admin_product_export", methods={"GET"})
*
* @param Request $request
*
* @return StreamedResponse
*/
public function export(Request $request) {
// タイムアウトを無効にする.
set_time_limit(0);
// sql loggerを無効にする.
$em = $this->entityManager;
$em->getConfiguration()->setSQLLogger(null);
$response = new StreamedResponse();
$response->setCallback(function () use ($request) {
// CSV種別を元に初期化.
$this->csvExportService->initCsvType(CsvType::CSV_TYPE_PRODUCT);
// 商品データ検索用のクエリビルダを取得.
$qb = $this->csvExportService->getProductQueryBuilder($request);
// パレス商品のみに絞り込み(sale_type_id = NULL or 1 or 3)
$qb->andWhere('pc.SaleType IS NULL OR pc.SaleType IN (:saleTypes)')
->setParameter('saleTypes', [1, 3]);
// チェックボックスで選択された商品IDで絞り込み
$ids = $request->get('ids');
if (!empty($ids)) {
$qb->andWhere($qb->expr()->in('p.id', ':ids'))
->setParameter('ids', $ids);
}
// ヘッダ行の出力.
$this->csvExportService->exportHeader();
// Get stock status
$isOutOfStock = 0;
$session = $request->getSession();
if ($session->has('eccube.admin.product.search')) {
$searchData = $session->get('eccube.admin.product.search', []);
if (isset($searchData['stock_status']) && $searchData['stock_status'] === 0) {
$isOutOfStock = 1;
}
}
// ★ ProductPlus項目を一括取得(最適化)
$conn = $this->entityManager->getConnection();
// product_item_value用(テキスト値): item_id → product_id → value
$sql = "SELECT pd.product_id, pd.product_item_id, pdd.value
FROM plg_productplus_dtb_product_data pd
JOIN plg_productplus_dtb_product_data_detail pdd
ON pd.id = pdd.product_data_id";
$rows = $conn->executeQuery($sql)->fetchAllAssociative();
$hashProductPlusValue = [];
foreach ($rows as $row) {
$itemId = $row['product_item_id'];
$productId = $row['product_id'];
// 前後の空白を削除(半角・全角両方)
$value = preg_replace('/^[\s ]+|[\s ]+$/u', '', $row['value'] ?? '');
if (!isset($hashProductPlusValue[$itemId])) {
$hashProductPlusValue[$itemId] = [];
}
// 同じitem_id/product_idに複数の値がある場合はカンマ区切りで結合
if (isset($hashProductPlusValue[$itemId][$productId])) {
$hashProductPlusValue[$itemId][$productId] .= ',' . $value;
} else {
$hashProductPlusValue[$itemId][$productId] = $value;
}
}
// product_item_id用(選択肢ID): item_id → product_id → num_value
$sql = "SELECT pd.product_id, pd.product_item_id, pdd.num_value
FROM plg_productplus_dtb_product_data pd
JOIN plg_productplus_dtb_product_data_detail pdd
ON pd.id = pdd.product_data_id
WHERE pdd.num_value IS NOT NULL";
$rows = $conn->executeQuery($sql)->fetchAllAssociative();
$hashProductPlusItemId = [];
foreach ($rows as $row) {
$itemId = $row['product_item_id'];
$productId = $row['product_id'];
if (!isset($hashProductPlusItemId[$itemId])) {
$hashProductPlusItemId[$itemId] = [];
}
// 同じitem_id/product_idに複数の値がある場合はカンマ区切りで結合
if (isset($hashProductPlusItemId[$itemId][$productId])) {
$hashProductPlusItemId[$itemId][$productId] .= ',' . $row['num_value'];
} else {
$hashProductPlusItemId[$itemId][$productId] = $row['num_value'];
}
}
// ★ CustomerRank価格を一括取得(最適化)
$sql = "SELECT product_class_id, customer_rank_id, price
FROM plg_customerrank_dtb_customer_price";
$rows = $conn->executeQuery($sql)->fetchAllAssociative();
$hashCustomerRank = [];
foreach ($rows as $row) {
$rankId = $row['customer_rank_id'];
$classId = $row['product_class_id'];
if (!isset($hashCustomerRank[$rankId])) {
$hashCustomerRank[$rankId] = [];
}
$hashCustomerRank[$rankId][$classId] = $row['price'];
}
// joinする場合はiterateが使えないため, select句をdistinctする.
// http://qiita.com/suin/items/2b1e98105fa3ef89beb7
// distinctのmysqlとpgsqlの挙動をあわせる.
// http://uedatakeshi.blogspot.jp/2010/04/distinct-oeder-by-postgresmysql.html
$qb->resetDQLPart('select');
if ($isOutOfStock) {
$qb->select('p, pc')
->distinct();
} else {
$qb->select('p')
->distinct();
}
// データ行の出力.
$this->csvExportService->setExportQueryBuilder($qb);
$this->csvExportService->exportData(function ($entity, CsvExportService $csvService) use ($request, $hashProductPlusValue, $hashProductPlusItemId, $hashCustomerRank) {
$Csvs = $csvService->getCsvs();
/** @var $Product \Eccube\Entity\Product */
$Product = $entity;
$productId = $Product->getId();
/** @var $ProductClasses \Eccube\Entity\ProductClass[] */
$ProductClasses = $Product->getProductClasses();
foreach ($ProductClasses as $ProductClass) {
$productClassId = $ProductClass->getId();
$ExportCsvRow = new ExportCsvRow();
// CSV出力項目と合致するデータを取得.
foreach ($Csvs as $Csv) {
// 商品データを検索.
$ExportCsvRow->setData($csvService->getData($Csv, $Product));
if ($ExportCsvRow->isDataNull()) {
// 商品規格情報を検索.
$ExportCsvRow->setData($csvService->getData($Csv, $ProductClass));
}
// ★ ProductPlus項目を直接処理(イベント不要)
if ($ExportCsvRow->isDataNull()) {
$csvEntityName = str_replace('\\\\', '\\', $Csv->getEntityName());
if ($csvEntityName == 'Plugin\ProductPlus42\Entity\ProductData') {
$product_item_id = (int)$Csv->getReferenceFieldName();
$fieldName = $Csv->getFieldName();
if ($fieldName == 'product_item_id') {
// 選択肢ID(num_value)
$value = $hashProductPlusItemId[$product_item_id][$productId] ?? null;
$ExportCsvRow->setData($value);
} elseif ($fieldName == 'product_item_value') {
// テキスト値(value)
$value = $hashProductPlusValue[$product_item_id][$productId] ?? null;
$ExportCsvRow->setData($value);
}
}
// ★ CustomerRank価格を直接処理(イベント不要)
if ($csvEntityName == 'Plugin\CustomerRank42\Entity\CustomerPrice') {
$fieldName = $Csv->getFieldName();
// customerrank_price_2 → rank_id = 2
if (preg_match('/customerrank_price_(\d+)/', $fieldName, $matches)) {
$rankId = (int)$matches[1];
$value = $hashCustomerRank[$rankId][$productClassId] ?? null;
$ExportCsvRow->setData($value);
}
}
}
$ExportCsvRow->pushData();
}
// $row[] = number_format(memory_get_usage(true));
// 出力.
$row = $ExportCsvRow->getRow();
foreach($row as $k => $v){
if (empty($v)) {
$row[$k] = "";
} else {
// 前後の空白を削除(半角・全角両方)
$v = preg_replace('/^[\s ]+|[\s ]+$/u', '', $v);
// 末尾の.00を削除
$row[$k] = preg_replace('/\.00$/', '', $v);
}
}
$csvService->fputcsv($row);
}
});
});
$now = new \DateTime();
$filename = 'product_' . $now->format('YmdHis') . '.csv';
$response->headers->set('Content-Type', 'application/octet-stream');
$response->headers->set('Content-Disposition', 'attachment; filename=' . $filename);
log_info('商品CSV出力ファイル名', [$filename]);
return $response;
}
/**
* 商品CSV2の出力.
*
* @Route("/%eccube_admin_route%/product/export2", name="admin_product_export2", methods={"GET"})
*
* @param Request $request
*
* @return StreamedResponse
*/
public function export2(Request $request) {
set_time_limit(0);
$em = $this->entityManager;
$em->getConfiguration()->setSQLLogger(null);
$response = new StreamedResponse();
$response->setCallback(function () use ($request) {
$this->csvExportService->initCsvType(CsvType::CSV_TYPE_PRODUCT2);
$qb = $this->csvExportService->getProductQueryBuilder($request);
$qb->andWhere('pc.SaleType IS NULL OR pc.SaleType IN (:saleTypes)')
->setParameter('saleTypes', [1, 3]);
$ids = $request->get('ids');
if (!empty($ids)) {
$qb->andWhere($qb->expr()->in('p.id', ':ids'))
->setParameter('ids', $ids);
}
$this->csvExportService->exportHeader();
$isOutOfStock = 0;
$session = $request->getSession();
if ($session->has('eccube.admin.product.search')) {
$searchData = $session->get('eccube.admin.product.search', []);
if (isset($searchData['stock_status']) && $searchData['stock_status'] === 0) {
$isOutOfStock = 1;
}
}
$conn = $this->entityManager->getConnection();
$sql = "SELECT pd.product_id, pd.product_item_id, pdd.value
FROM plg_productplus_dtb_product_data pd
JOIN plg_productplus_dtb_product_data_detail pdd
ON pd.id = pdd.product_data_id";
$rows = $conn->executeQuery($sql)->fetchAllAssociative();
$hashProductPlusValue = [];
foreach ($rows as $row) {
$itemId = $row['product_item_id'];
$productId = $row['product_id'];
$value = preg_replace('/^[\s ]+|[\s ]+$/u', '', $row['value'] ?? '');
if (!isset($hashProductPlusValue[$itemId])) {
$hashProductPlusValue[$itemId] = [];
}
if (isset($hashProductPlusValue[$itemId][$productId])) {
$hashProductPlusValue[$itemId][$productId] .= ',' . $value;
} else {
$hashProductPlusValue[$itemId][$productId] = $value;
}
}
$sql = "SELECT pd.product_id, pd.product_item_id, pdd.num_value
FROM plg_productplus_dtb_product_data pd
JOIN plg_productplus_dtb_product_data_detail pdd
ON pd.id = pdd.product_data_id
WHERE pdd.num_value IS NOT NULL";
$rows = $conn->executeQuery($sql)->fetchAllAssociative();
$hashProductPlusItemId = [];
foreach ($rows as $row) {
$itemId = $row['product_item_id'];
$productId = $row['product_id'];
if (!isset($hashProductPlusItemId[$itemId])) {
$hashProductPlusItemId[$itemId] = [];
}
if (isset($hashProductPlusItemId[$itemId][$productId])) {
$hashProductPlusItemId[$itemId][$productId] .= ',' . $row['num_value'];
} else {
$hashProductPlusItemId[$itemId][$productId] = $row['num_value'];
}
}
$sql = "SELECT product_class_id, customer_rank_id, price
FROM plg_customerrank_dtb_customer_price";
$rows = $conn->executeQuery($sql)->fetchAllAssociative();
$hashCustomerRank = [];
foreach ($rows as $row) {
$rankId = $row['customer_rank_id'];
$classId = $row['product_class_id'];
if (!isset($hashCustomerRank[$rankId])) {
$hashCustomerRank[$rankId] = [];
}
$hashCustomerRank[$rankId][$classId] = $row['price'];
}
$qb->resetDQLPart('select');
if ($isOutOfStock) {
$qb->select('p, pc')
->distinct();
} else {
$qb->select('p')
->distinct();
}
$this->csvExportService->setExportQueryBuilder($qb);
$this->csvExportService->exportData(function ($entity, CsvExportService $csvService) use ($request, $hashProductPlusValue, $hashProductPlusItemId, $hashCustomerRank) {
$Csvs = $csvService->getCsvs();
/** @var $Product \Eccube\Entity\Product */
$Product = $entity;
$productId = $Product->getId();
/** @var $ProductClasses \Eccube\Entity\ProductClass[] */
$ProductClasses = $Product->getProductClasses();
foreach ($ProductClasses as $ProductClass) {
$productClassId = $ProductClass->getId();
$ExportCsvRow = new ExportCsvRow();
foreach ($Csvs as $Csv) {
$ExportCsvRow->setData($csvService->getData($Csv, $Product));
if ($ExportCsvRow->isDataNull()) {
$ExportCsvRow->setData($csvService->getData($Csv, $ProductClass));
}
if ($ExportCsvRow->isDataNull()) {
$csvEntityName = str_replace('\\\\', '\\', $Csv->getEntityName());
if ($csvEntityName == 'Plugin\ProductPlus42\Entity\ProductData') {
$product_item_id = (int)$Csv->getReferenceFieldName();
$fieldName = $Csv->getFieldName();
if ($fieldName == 'product_item_id') {
$value = $hashProductPlusItemId[$product_item_id][$productId] ?? null;
$ExportCsvRow->setData($value);
} elseif ($fieldName == 'product_item_value') {
$value = $hashProductPlusValue[$product_item_id][$productId] ?? null;
$ExportCsvRow->setData($value);
}
}
if ($csvEntityName == 'Plugin\CustomerRank42\Entity\CustomerPrice') {
$fieldName = $Csv->getFieldName();
if (preg_match('/customerrank_price_(\d+)/', $fieldName, $matches)) {
$rankId = (int)$matches[1];
$value = $hashCustomerRank[$rankId][$productClassId] ?? null;
$ExportCsvRow->setData($value);
}
}
}
$ExportCsvRow->pushData();
}
$row = $ExportCsvRow->getRow();
foreach($row as $k => $v){
if (empty($v)) {
$row[$k] = "";
} else {
$v = preg_replace('/^[\s ]+|[\s ]+$/u', '', $v);
$row[$k] = preg_replace('/\.00$/', '', $v);
}
}
$csvService->fputcsv($row);
}
});
});
$now = new \DateTime();
$filename = 'product2_' . $now->format('YmdHis') . '.csv';
$response->headers->set('Content-Type', 'application/octet-stream');
$response->headers->set('Content-Disposition', 'attachment; filename=' . $filename);
log_info('商品CSV2出力ファイル名', [$filename]);
return $response;
}
/**
* ProductCategory作成
*
* @param \Eccube\Entity\Product $Product
* @param \Eccube\Entity\Category $Category
* @param integer $count
*
* @return \Eccube\Entity\ProductCategory
*/
private function createProductCategory($Product, $Category, $count) {
$ProductCategory = new ProductCategory();
$ProductCategory->setProduct($Product);
$ProductCategory->setProductId($Product->getId());
$ProductCategory->setCategory($Category);
$ProductCategory->setCategoryId($Category->getId());
return $ProductCategory;
}
/**
* Bulk public action
*
* @Route("/%eccube_admin_route%/product/bulk/product-status/{id}", requirements={"id" = "\d+"}, name="admin_product_bulk_product_status", methods={"POST"})
*
* @param Request $request
* @param ProductStatus $ProductStatus
*
* @return RedirectResponse
*/
public function bulkProductStatus(Request $request, ProductStatus $ProductStatus, CacheUtil $cacheUtil) {
$this->isTokenValid();
/** @var Product[] $Products */
$Products = $this->productRepository->findBy(['id' => $request->get('ids')]);
$count = 0;
foreach ($Products as $Product) {
try {
$Product->setStatus($ProductStatus);
$this->productRepository->save($Product);
$count++;
} catch (\Exception $e) {
$this->addError($e->getMessage(), 'admin');
}
}
try {
if ($count) {
$this->entityManager->flush();
$msg = $this->translator->trans('admin.product.bulk_change_status_complete', [
'%count%' => $count,
'%status%' => $ProductStatus->getName(),
]);
$this->addSuccess($msg, 'admin');
$cacheUtil->clearDoctrineCache();
}
} catch (\Exception $e) {
$this->addError($e->getMessage(), 'admin');
}
return $this->redirectToRoute('admin_product', ['resume' => Constant::ENABLED]);
}
/**
* 目安価格を保存
*
* @Route("/%eccube_admin_route%/product/update_price2", name="admin_product_update_price2", methods={"POST"})
*/
public function updatePrice2(Request $request) {
$productId = $request->get('product_id');
$price2 = $request->get('price2');
try {
// EntityManagerを取得
$em = $this->entityManager;
$conn = $em->getConnection();
// product_data_idを取得(plg_productplus_dtb_product_dataから)
$pdSql = "SELECT id FROM plg_productplus_dtb_product_data WHERE product_id = :product_id AND product_item_id = 55";
$pdStmt = $conn->prepare($pdSql);
$pdStmt->bindValue('product_id', $productId);
$pdResultSet = $pdStmt->executeQuery();
$productData = $pdResultSet->fetchAssociative();
if ($productData) {
// product_dataが存在する場合、detailを確認
$pddSql = "SELECT id FROM plg_productplus_dtb_product_data_detail WHERE product_data_id = :product_data_id";
$pddStmt = $conn->prepare($pddSql);
$pddStmt->bindValue('product_data_id', $productData['id']);
$pddResultSet = $pddStmt->executeQuery();
$existing = $pddResultSet->fetchAssociative();
if ($existing) {
// 更新
$updateSql = "UPDATE plg_productplus_dtb_product_data_detail SET value = :price2 WHERE id = :id";
$updateStmt = $conn->prepare($updateSql);
$updateStmt->bindValue('price2', $price2);
$updateStmt->bindValue('id', $existing['id']);
$updateStmt->executeQuery();
} else {
// detail追加
$insertDetailSql = "INSERT INTO plg_productplus_dtb_product_data_detail (product_data_id, value, sort_no) VALUES (:product_data_id, :price2, 0)";
$insertDetailStmt = $conn->prepare($insertDetailSql);
$insertDetailStmt->bindValue('product_data_id', $productData['id']);
$insertDetailStmt->bindValue('price2', $price2);
$insertDetailStmt->executeQuery();
}
} else {
// product_dataから作成
$insertDataSql = "INSERT INTO plg_productplus_dtb_product_data (product_id, product_item_id, create_date) VALUES (:product_id, 55, NOW())";
$insertDataStmt = $conn->prepare($insertDataSql);
$insertDataStmt->bindValue('product_id', $productId);
$insertDataStmt->executeQuery();
$newProductDataId = $conn->lastInsertId();
// detail追加
$insertDetailSql = "INSERT INTO plg_productplus_dtb_product_data_detail (product_data_id, value, sort_no) VALUES (:product_data_id, :price2, 0)";
$insertDetailStmt = $conn->prepare($insertDetailSql);
$insertDetailStmt->bindValue('product_data_id', $newProductDataId);
$insertDetailStmt->bindValue('price2', $price2);
$insertDetailStmt->executeQuery();
}
return $this->json(['success' => true, 'message' => '目安価格を反映しました。']);
} catch (\Exception $e) {
return $this->json(['success' => false, 'message' => 'エラーが発生しました: ' . $e->getMessage()], 500);
}
}
}