<?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 Eccube\Controller\Admin\Product;
use Customize\Entity\ProductDetailImage;
use Customize\Entity\ProductGallery;
use Customize\Entity\ProductNaireImage;
use Customize\Repository\ProductDetailImageRepository;
use Customize\Repository\ProductGalleryRepository;
use Customize\Repository\ProductNaireImageRepository;
use Doctrine\Common\Collections\ArrayCollection;
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\ParamConverter;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
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 ProductGalleryRepository
*/
protected $productGalleryRepository;
/**
* @var ProductDetailImageRepository
*/
protected $productDetailImageRepository;
/**
* @var ProductNaireImageRepository
*/
protected $productNaireImageRepository;
/**
* ProductController constructor.
*/
public function __construct(
CsvExportService $csvExportService,
ProductClassRepository $productClassRepository,
ProductImageRepository $productImageRepository,
TaxRuleRepository $taxRuleRepository,
CategoryRepository $categoryRepository,
ProductRepository $productRepository,
BaseInfoRepository $baseInfoRepository,
PageMaxRepository $pageMaxRepository,
ProductStatusRepository $productStatusRepository,
TagRepository $tagRepository,
ProductGalleryRepository $productGalleryRepository,
ProductDetailImageRepository $productDetailImageRepository,
ProductNaireImageRepository $productNaireImageRepository
) {
$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->productGalleryRepository = $productGalleryRepository;
$this->productDetailImageRepository = $productDetailImageRepository;
$this->productNaireImageRepository = $productNaireImageRepository;
}
/**
* @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);
}
}
$qb = $this->productRepository->getQueryBuilderBySearchDataForAdmin($searchData);
$event = new EventArgs(
[
'qb' => $qb,
'searchData' => $searchData,
],
$request
);
$this->eventDispatcher->dispatch($event, EccubeEvents::ADMIN_PRODUCT_INDEX_SEARCH);
$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]
);
}
return [
'searchForm' => $searchForm->createView(),
'pagination' => $pagination,
'pageMaxis' => $pageMaxis,
'page_no' => $page_no,
'page_count' => $page_count,
'has_errors' => false,
];
}
/**
* @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"})
* @Route("/%eccube_admin_route%/product/product/image/process/{key_name}", name="admin_product_image_process_key", defaults={"entity_name": null, "key_name": null}, methods={"POST"})
* @Route("/%eccube_admin_route%/product/image/process/{entity_name}/{key_name}", name="admin_product_image_process_entity_key", methods={"POST"})
*/
public function imageProcess(Request $request, $entity_name = null, $key_name = null)
{
if (!$request->isXmlHttpRequest() && $this->isTokenValid()) {
throw new BadRequestHttpException();
}
$images = $request->files->get('admin_product');
$allowExtensions = ['gif', 'jpg', 'jpeg', 'png'];
$files = [];
if ($key_name) {
if ($entity_name) {
if (isset($images[$entity_name])) {
foreach ($images[$entity_name] as $key => $img) {
$image = $img[$key_name];
// ファイルフォーマット検証
$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->getParameter('eccube_temp_image_dir'), $filename);
$files[] = $filename;
}
}
} else {
if (isset($images[$key_name])) {
$image = $images[$key_name];
// ファイルフォーマット検証
$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->getParameter('eccube_temp_image_dir'), $filename);
$files[] = $filename;
}
}
} else {
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/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;
/** @var Product|null */
$Product = null;
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();
}
}
// 編集前の特徴情報を保持
$OriginProductFeatures = new ArrayCollection();
foreach ($Product->getProductFeatures() as $ProductFeature) {
$OriginProductFeatures->add($ProductFeature);
}
// 編集前のよくある質問の情報を保持
$OriginProductFaqs = new ArrayCollection();
foreach ($Product->getProductFaqs() as $ProductFaq) {
$OriginProductFaqs->add($ProductFaq);
}
// 編集前の関連特集の情報を保持
$OriginProductRelateFeatures = new ArrayCollection();
foreach ($Product->getProductRelateFeatures() as $ProductRelateFeature) {
$OriginProductRelateFeatures->add($ProductRelateFeature);
}
// 編集前のムービーアンドギャラリーの動画URLの情報を保持
$OriginProductMovies = new ArrayCollection();
foreach ($Product->getProductMovies() as $ProductMovie) {
$OriginProductMovies->add($ProductMovie);
}
// 編集前の商品詳細のスペック項目とスペック詳細の情報を保持
$OriginProductSpecs = new ArrayCollection();
foreach ($Product->getProductSpecs() as $ProductSpec) {
$OriginProductSpecs->add($ProductSpec);
}
// 編集前のハッシュタグの情報を保持
$OriginProductHashTags = new ArrayCollection();
foreach ($Product->getProductHashTags() as $ProductHashTag) {
$OriginProductHashTags->add($ProductHashTag);
}
// 編集前のムービー&ギャラリーの情報を保持
$OriginProductGalleries = new ArrayCollection();
foreach ($Product->getProductGalleries() as $ProductGallery) {
$OriginProductGalleries->add($ProductGallery);
}
// 編集前の商品仕様(詳細)の画像情報を保持
$OriginProductDetailImages = new ArrayCollection();
foreach ($Product->getProductDetailImages() as $ProductDetailImage) {
$OriginProductDetailImages->add($ProductDetailImage);
}
// 編集前の商品仕様(名入れ)の画像情報を保持
$OriginProductNaireImages = new ArrayCollection();
foreach ($Product->getProductNaireImages() as $ProductNaireImage) {
$OriginProductNaireImages->add($ProductNaireImage);
}
$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);
$gallery_images = [];
$ProductGalleries = $Product->getProductGalleries();
foreach ($ProductGalleries as $ProductGallery) {
$gallery_images[] = $ProductGallery->getFileName();
}
$form['gallery_images']->setData($gallery_images);
$detail_images = [];
$ProductDetailImages = $Product->getProductDetailImages();
foreach ($ProductDetailImages as $ProductDetailImage) {
$detail_images[] = $ProductDetailImage->getFileName();
}
$form['detail_images']->setData($detail_images);
$naire_images = [];
$ProductNaireImages = $Product->getProductNaireImages();
foreach ($ProductNaireImages as $ProductNaireImage) {
$naire_images[] = $ProductNaireImage->getFileName();
}
$form['naire_images']->setData($naire_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();
}
// コンセプト画像アップロード
$concept_files = [
$form->get('concept_file_name_pc')->getData(),
$form->get('concept_file_name_sp')->getData(),
];
foreach ($concept_files as $concept_file) {
if (!$concept_file) {
continue;
}
$fs = new Filesystem();
if ($concept_files && strpos($concept_file, '..') === false && $fs->exists($this->getParameter('eccube_temp_image_dir').'/'.$concept_file)) {
$fs->rename(
$this->getParameter('eccube_temp_image_dir').'/'.$concept_file,
$this->getParameter('eccube_save_image_dir').'/'.$concept_file
);
}
}
// 特徴画像アップロード
$ProductFeatures = $form->get('ProductFeatures')->getData();
foreach ($ProductFeatures as $ProductFeature) {
$fs = new Filesystem();
if ($ProductFeature->getFileName() && strpos($ProductFeature->getFileName(), '..') === false && $fs->exists($this->getParameter('eccube_temp_image_dir').'/'.$ProductFeature->getFileName())) {
$fs->rename(
$this->getParameter('eccube_temp_image_dir').'/'.$ProductFeature->getFileName(),
$this->getParameter('eccube_save_image_dir').'/'.$ProductFeature->getFileName()
);
}
}
// 商品タグの登録
// 商品タグを一度クリア
$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);
}
$this->entityManager->flush();
// 特徴の登録
$ProductFeatures = $form->get('ProductFeatures')->getData();
foreach ($ProductFeatures as $ProductFeature) {
$ProductFeature->setProduct($Product);
$Product->addProductFeature($ProductFeature);
$this->entityManager->persist($ProductFeature);
}
foreach ($OriginProductFeatures as $ProductFeature) {
if (!$ProductFeatures->contains($ProductFeature)) {
$Product->removeProductFeature($ProductFeature);
$this->entityManager->remove($ProductFeature);
}
}
// よくあるご質問の登録
$ProductFaqs = $form->get('ProductFaqs')->getData();
foreach ($ProductFaqs as $ProductFaq) {
$ProductFaq->setProduct($Product);
$Product->addProductFaq($ProductFaq);
$this->entityManager->persist($ProductFaq);
}
foreach ($OriginProductFaqs as $ProductFaq) {
if (!$ProductFaqs->contains($ProductFaq)) {
$Product->removeProductFaq($ProductFaq);
$this->entityManager->remove($ProductFaq);
}
}
// 関連特集の登録
$ProductRelateFeatures = $form->get('ProductRelateFeatures')->getData();
foreach ($ProductRelateFeatures as $ProductRelateFeature) {
$ProductRelateFeature->setProduct($Product);
$Product->addProductRelateFeature($ProductRelateFeature);
$this->entityManager->persist($ProductRelateFeature);
}
foreach ($OriginProductRelateFeatures as $ProductRelateFeature) {
if (!$ProductRelateFeatures->contains($ProductRelateFeature)) {
$Product->removeProductRelateFeature($ProductRelateFeature);
$this->entityManager->remove($ProductRelateFeature);
}
}
// ムービーアンドギャラリーの画像の登録
$gallery_add_images = $form->get('gallery_add_images')->getData();
foreach ($gallery_add_images as $gallery_add_image) {
$ProductGallery = new ProductGallery();
$ProductGallery
->setFileName($gallery_add_image)
->setProduct($Product)
->setSortNo(1);
$Product->addProductGallery($ProductGallery);
$this->entityManager->persist($ProductGallery);
// 移動
$file = new File($this->eccubeConfig['eccube_temp_image_dir'].'/'.$gallery_add_image);
$file->move($this->eccubeConfig['eccube_save_image_dir']);
}
// ムービーアンドギャラリーの画像の削除
$gallery_delete_images = $form->get('gallery_delete_images')->getData();
foreach ($gallery_delete_images as $gallery_delete_image) {
$ProductGallery = $this->productGalleryRepository->findOneBy([
'Product' => $Product,
'file_name' => $gallery_delete_image,
]);
if ($ProductGallery instanceof ProductGallery) {
$Product->removeProductGallery($ProductGallery);
$this->entityManager->remove($ProductGallery);
$this->entityManager->flush();
// 他に同じ画像を参照する商品がなければ画像ファイルを削除
if (!$this->productGalleryRepository->findOneBy(['file_name' => $gallery_delete_image])) {
$fs->remove($this->eccubeConfig['eccube_save_image_dir'].'/'.$gallery_delete_image);
}
} else {
// 追加してすぐに削除した画像は、Entityに追加されない
$fs->remove($this->eccubeConfig['eccube_temp_image_dir'].'/'.$gallery_delete_image);
}
}
$this->entityManager->flush();
if (array_key_exists('product_gallery', $request->request->get('admin_product'))) {
$product_gallery = $request->request->get('admin_product')['product_gallery'];
foreach ($product_gallery as $sortNo => $filename) {
$ProductGallery = $this->productGalleryRepository
->findOneBy([
'file_name' => pathinfo($filename, PATHINFO_BASENAME),
'Product' => $Product,
]);
if ($ProductGallery !== null) {
$ProductGallery->setSortNo($sortNo);
$this->entityManager->persist($ProductGallery);
}
}
$this->entityManager->flush();
}
// ムービーアンドギャラリーの動画URLの登録
$ProductMovies = $form->get('ProductMovies')->getData();
foreach ($ProductMovies as $ProductMovie) {
$ProductMovie->setProduct($Product);
$Product->addProductMovie($ProductMovie);
$this->entityManager->persist($ProductMovie);
}
foreach ($OriginProductMovies as $ProductMovie) {
if (!$ProductMovies->contains($ProductMovie)) {
$Product->removeProductMovie($ProductMovie);
$this->entityManager->remove($ProductMovie);
}
}
// ムービー&ギャラリーの画像alt_textの登録
$ProductGalleries = $form->get('ProductGalleries')->getData();
foreach ($ProductGalleries as $ProductGallery) {
$ProductGallery->setProduct($Product);
$Product->addProductGallery($ProductGallery);
$this->entityManager->persist($ProductGallery);
}
foreach ($OriginProductGalleries as $ProductGallery) {
if (!$ProductGalleries->contains($ProductGallery)) {
$Product->removeProductGallery($ProductGallery);
$this->entityManager->remove($ProductGallery);
}
}
// 商品仕様詳細のスペック項目とスペック詳細の登録
$ProductSpecs = $form->get('ProductSpecs')->getData();
foreach ($ProductSpecs as $ProductSpec) {
$ProductSpec->setProduct($Product);
$Product->addProductSpec($ProductSpec);
$this->entityManager->persist($ProductSpec);
}
foreach ($OriginProductSpecs as $ProductSpec) {
if (!$ProductSpecs->contains($ProductSpec)) {
$Product->removeProductSpec($ProductSpec);
$this->entityManager->remove($ProductSpec);
}
}
// 商品仕様詳細の画像alt_textの登録
$ProductDetailImages = $form->get('ProductDetailImages')->getData();
foreach ($ProductDetailImages as $ProductDetailImage) {
$ProductDetailImage->setProduct($Product);
$Product->addProductDetailImage($ProductDetailImage);
$this->entityManager->persist($ProductDetailImage);
}
foreach ($OriginProductDetailImages as $ProductDetailImage) {
if (!$ProductDetailImages->contains($ProductDetailImage)) {
$Product->removeProductDetailImage($ProductDetailImage);
$this->entityManager->remove($ProductDetailImage);
}
}
// 商品仕様(詳細)の画像の登録
$detail_add_images = $form->get('detail_add_images')->getData();
foreach ($detail_add_images as $detail_add_image) {
$ProductDetailImage = new ProductDetailImage();
$ProductDetailImage
->setFileName($detail_add_image)
->setProduct($Product)
->setSortNo(1);
$Product->addProductDetailImage($ProductDetailImage);
$this->entityManager->persist($ProductDetailImage);
// 移動
$file = new File($this->eccubeConfig['eccube_temp_image_dir'].'/'.$detail_add_image);
$file->move($this->eccubeConfig['eccube_save_image_dir']);
}
// 商品仕様(詳細)の画像の削除
$detail_delete_images = $form->get('detail_delete_images')->getData();
foreach ($detail_delete_images as $detail_delete_image) {
$ProductDetailImage = $this->productDetailImageRepository->findOneBy([
'Product' => $Product,
'file_name' => $detail_delete_image,
]);
if ($ProductDetailImage instanceof ProductDetailImage) {
$Product->removeProductDetailImage($ProductDetailImage);
$this->entityManager->remove($ProductDetailImage);
$this->entityManager->flush();
// 他に同じ画像を参照する商品がなければ画像ファイルを削除
if (!$this->productDetailImageRepository->findOneBy(['file_name' => $detail_delete_image])) {
$fs->remove($this->eccubeConfig['eccube_save_image_dir'].'/'.$detail_delete_image);
}
} else {
// 追加してすぐに削除した画像は、Entityに追加されない
$fs->remove($this->eccubeConfig['eccube_temp_image_dir'].'/'.$detail_delete_image);
}
}
$this->entityManager->flush();
if (array_key_exists('product_detail_image', $request->request->get('admin_product'))) {
$product_detail_image = $request->request->get('admin_product')['product_detail_image'];
foreach ($product_detail_image as $sortNo => $filename) {
$ProductDetailImage = $this->productDetailImageRepository
->findOneBy([
'file_name' => pathinfo($filename, PATHINFO_BASENAME),
'Product' => $Product,
]);
if ($ProductDetailImage !== null) {
$ProductDetailImage->setSortNo($sortNo);
$this->entityManager->persist($ProductDetailImage);
}
}
$this->entityManager->flush();
}
// 商品仕様(名入れ)の画像の登録
$naire_add_images = $form->get('naire_add_images')->getData();
foreach ($naire_add_images as $naire_add_image) {
$ProductNaireImage = new ProductNaireImage();
$ProductNaireImage
->setFileName($naire_add_image)
->setProduct($Product)
->setSortNo(1);
$Product->addProductNaireImage($ProductNaireImage);
$this->entityManager->persist($ProductNaireImage);
// 移動
$file = new File($this->eccubeConfig['eccube_temp_image_dir'].'/'.$naire_add_image);
$file->move($this->eccubeConfig['eccube_save_image_dir']);
}
// 商品仕様(名入れ)の画像の削除
$naire_delete_images = $form->get('naire_delete_images')->getData();
foreach ($naire_delete_images as $naire_delete_image) {
$ProductNaireImage = $this->productNaireImageRepository->findOneBy([
'Product' => $Product,
'file_name' => $naire_delete_image,
]);
if ($ProductNaireImage instanceof ProductNaireImage) {
$Product->removeProductNaireImage($ProductNaireImage);
$this->entityManager->remove($ProductNaireImage);
$this->entityManager->flush();
// 他に同じ画像を参照する商品がなければ画像ファイルを削除
if (!$this->productNaireImageRepository->findOneBy(['file_name' => $naire_delete_image])) {
$fs->remove($this->eccubeConfig['eccube_save_image_dir'].'/'.$naire_delete_image);
}
} else {
// 追加してすぐに削除した画像は、Entityに追加されない
$fs->remove($this->eccubeConfig['eccube_temp_image_dir'].'/'.$naire_delete_image);
}
}
// 商品仕様(名入れ)の画像alt_textの登録
$ProductNaireImages = $form->get('ProductNaireImages')->getData();
foreach ($ProductNaireImages as $ProductNaireImage) {
$ProductNaireImage->setProduct($Product);
$Product->addProductNaireImage($ProductNaireImage);
$this->entityManager->persist($ProductNaireImage);
}
foreach ($OriginProductNaireImages as $ProductNaireImage) {
if (!$ProductNaireImages->contains($ProductNaireImage)) {
$Product->removeProductNaireImage($ProductNaireImage);
$this->entityManager->remove($ProductNaireImage);
}
}
$this->entityManager->flush();
if (array_key_exists('product_naire_image', $request->request->get('admin_product'))) {
$product_naire_image = $request->request->get('admin_product')['product_naire_image'];
foreach ($product_naire_image as $sortNo => $filename) {
$ProductNaireImage = $this->productNaireImageRepository
->findOneBy([
'file_name' => pathinfo($filename, PATHINFO_BASENAME),
'Product' => $Product,
]);
if ($ProductNaireImage !== null) {
$ProductNaireImage->setSortNo($sortNo);
$this->entityManager->persist($ProductNaireImage);
}
}
$this->entityManager->flush();
}
// 製作者紹介画像アップロード
$producer_file = $form->get('producer_file')->getData();
if ($producer_file && strpos($producer_file, '..') === false && $fs->exists($this->getParameter('eccube_temp_image_dir').'/'.$producer_file)) {
$fs->rename(
$this->getParameter('eccube_temp_image_dir').'/'.$producer_file,
$this->getParameter('eccube_save_image_dir').'/'.$producer_file
);
}
// ハッシュタグの登録
$ProductHashTags = $form->get('ProductHashTags')->getData();
foreach ($ProductHashTags as $ProductHashTag) {
$ProductHashTag->setProduct($Product);
$Product->addProductHashTag($ProductHashTag);
$this->entityManager->persist($ProductHashTag);
}
foreach ($OriginProductHashTags as $ProductHashTag) {
if (!$ProductHashTags->contains($ProductHashTag)) {
$Product->removeProductHashTag($ProductHashTag);
$this->entityManager->remove($ProductHashTag);
}
}
// 関連特集の登録
$ProductRelateFeatures = $form->get('ProductRelateFeatures')->getData();
foreach ($ProductRelateFeatures as $ProductRelateFeature) {
$fs = new Filesystem();
if ($ProductRelateFeature->getFileName() && strpos($ProductRelateFeature->getFileName(), '..') === false && $fs->exists($this->getParameter('eccube_temp_image_dir').'/'.$ProductRelateFeature->getFileName())) {
$fs->rename(
$this->getParameter('eccube_temp_image_dir').'/'.$ProductRelateFeature->getFileName(),
$this->getParameter('eccube_save_image_dir').'/'.$ProductRelateFeature->getFileName()
);
}
}
$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,
];
}
/**
* @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]);
$deleteImages = $Product->getProductImage();
$ProductClasses = $Product->getProductClasses();
try {
$this->productRepository->delete($Product);
$this->entityManager->flush();
$event = new EventArgs(
[
'Product' => $Product,
'ProductClass' => $ProductClasses,
'deleteImages' => $deleteImages,
],
$request
);
$this->eventDispatcher->dispatch($event, EccubeEvents::ADMIN_PRODUCT_DELETE_COMPLETE);
$deleteImages = $event->getArgument('deleteImages');
// 画像ファイルの削除(commit後に削除させる)
/** @var ProductImage $deleteImage */
foreach ($deleteImages as $deleteImage) {
if ($this->productImageRepository->findOneBy(['file_name' => $deleteImage->getFileName()])) {
continue;
}
try {
$fs = new Filesystem();
$fs->remove($this->eccubeConfig['eccube_save_image_dir'].'/'.$deleteImage);
} catch (\Exception $e) {
// エラーが発生しても無視する
}
}
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);
// ヘッダ行の出力.
$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;
}
}
// 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) {
$Csvs = $csvService->getCsvs();
/** @var $Product \Eccube\Entity\Product */
$Product = $entity;
/** @var $ProductClasses \Eccube\Entity\ProductClass[] */
$ProductClasses = $Product->getProductClasses();
foreach ($ProductClasses as $ProductClass) {
$ExportCsvRow = new ExportCsvRow();
// CSV出力項目と合致するデータを取得.
foreach ($Csvs as $Csv) {
// 商品データを検索.
$ExportCsvRow->setData($csvService->getData($Csv, $Product));
if ($ExportCsvRow->isDataNull()) {
// 商品規格情報を検索.
$ExportCsvRow->setData($csvService->getData($Csv, $ProductClass));
}
$event = new EventArgs(
[
'csvService' => $csvService,
'Csv' => $Csv,
'ProductClass' => $ProductClass,
'ExportCsvRow' => $ExportCsvRow,
],
$request
);
$this->eventDispatcher->dispatch($event, EccubeEvents::ADMIN_PRODUCT_CSV_EXPORT);
$ExportCsvRow->pushData();
}
// $row[] = number_format(memory_get_usage(true));
// 出力.
$csvService->fputcsv($ExportCsvRow->getRow());
}
});
});
$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;
}
/**
* 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/product/audio", name="admin_product_audio_file", methods={"POST"})
*/
public function addAudio(Request $request)
{
if (!$request->isXmlHttpRequest() && $this->isTokenValid()) {
throw new BadRequestHttpException();
}
$audio = $request->files->get('audio_file');
$allowExtensions = ['mpeg', 'mp3', 'wav','ogg', 'midi'];
//ファイルフォーマット検証
$mimeType = $audio->getMimeType();
if (0 !== strpos($mimeType, 'audio')) {
throw new UnsupportedMediaTypeHttpException();
}
// 拡張子
$extension = $audio->getClientOriginalExtension();
if (!in_array(strtolower($extension), $allowExtensions)) {
throw new UnsupportedMediaTypeHttpException();
}
$fileName = date('mdHis').uniqid('_').'.'.$extension;
$audio->move($this->getParameter('eccube_temp_image_dir'), $fileName);
return new Response($fileName);
}
/**
* 音声ファイル削除ボタンを押したときに呼び出されるメソッド.
*
* @Route("/%eccube_admin_route%/product/product/audio/delete/{id}", requirements={"id" = "\d+"}, name="admin_product_audio_delete", methods={"DELETE"})
*/
public function deleteAudio(Request $request, $id = null)
{
if (!$request->isXmlHttpRequest() && $this->isTokenValid()) {
throw new BadRequestHttpException();
}
/** @var Product|null */
$Product = null;
$Product = $this->productRepository->findWithSortedClassCategories($id);
$fileName = $Product->getAudioFileName();
return new Response($fileName);
}
}