app/Customize/Controller/Admin/Product/ProductController.php line 605

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of EC-CUBE
  4.  *
  5.  * Copyright(c) EC-CUBE CO.,LTD. All Rights Reserved.
  6.  *
  7.  * http://www.ec-cube.co.jp/
  8.  *
  9.  * For the full copyright and license information, please view the LICENSE
  10.  * file that was distributed with this source code.
  11.  */
  12. namespace Customize\Controller\Admin\Product;
  13. use Doctrine\DBAL\Exception\ForeignKeyConstraintViolationException;
  14. use Eccube\Common\Constant;
  15. use Eccube\Controller\AbstractController;
  16. use Eccube\Entity\BaseInfo;
  17. use Eccube\Entity\ExportCsvRow;
  18. use Eccube\Entity\Master\CsvType;
  19. use Eccube\Entity\Master\ProductStatus;
  20. use Eccube\Entity\Product;
  21. use Eccube\Entity\ProductCategory;
  22. use Eccube\Entity\ProductClass;
  23. use Eccube\Entity\ProductImage;
  24. use Eccube\Entity\ProductStock;
  25. use Eccube\Entity\ProductTag;
  26. use Eccube\Event\EccubeEvents;
  27. use Eccube\Event\EventArgs;
  28. use Customize\Form\Type\Admin\ProductType;
  29. use Eccube\Form\Type\Admin\SearchProductType;
  30. use Eccube\Repository\BaseInfoRepository;
  31. use Eccube\Repository\CategoryRepository;
  32. use Eccube\Repository\Master\PageMaxRepository;
  33. use Eccube\Repository\Master\ProductStatusRepository;
  34. use Eccube\Repository\ProductClassRepository;
  35. use Eccube\Repository\ProductImageRepository;
  36. use Customize\Repository\ProductRepository;
  37. use Eccube\Repository\TagRepository;
  38. use Eccube\Repository\TaxRuleRepository;
  39. use Eccube\Service\CsvExportService;
  40. use Eccube\Util\CacheUtil;
  41. use Eccube\Util\FormUtil;
  42. use Knp\Component\Pager\PaginatorInterface;
  43. use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
  44. use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
  45. use Symfony\Component\Filesystem\Filesystem;
  46. use Symfony\Component\HttpFoundation\File\File;
  47. use Symfony\Component\HttpFoundation\RedirectResponse;
  48. use Symfony\Component\HttpFoundation\Request;
  49. use Symfony\Component\HttpFoundation\Response;
  50. use Symfony\Component\HttpFoundation\StreamedResponse;
  51. use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
  52. use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
  53. use Symfony\Component\HttpKernel\Exception\UnsupportedMediaTypeHttpException;
  54. use Symfony\Component\Routing\Annotation\Route;
  55. use Symfony\Component\Routing\RouterInterface;
  56. use Customize\Util\SpreadSheetUtil;
  57. use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
  58. use Plugin\Maker42\Repository\MakerRepository;
  59. use Plugin\Maker42\Entity\Maker;
  60. class ProductController extends AbstractController
  61. {
  62.     /**
  63.      * @var CsvExportService
  64.      */
  65.     protected $csvExportService;
  66.     /**
  67.      * @var ProductClassRepository
  68.      */
  69.     protected $productClassRepository;
  70.     /**
  71.      * @var ProductImageRepository
  72.      */
  73.     protected $productImageRepository;
  74.     /**
  75.      * @var TaxRuleRepository
  76.      */
  77.     protected $taxRuleRepository;
  78.     /**
  79.      * @var CategoryRepository
  80.      */
  81.     protected $categoryRepository;
  82.     /**
  83.      * @var ProductRepository
  84.      */
  85.     protected $productRepository;
  86.     /**
  87.      * @var MakerRepository
  88.      */
  89.     protected $makerRepository;
  90.     /**
  91.      * @var BaseInfo
  92.      */
  93.     protected $BaseInfo;
  94.     /**
  95.      * @var PageMaxRepository
  96.      */
  97.     protected $pageMaxRepository;
  98.     /**
  99.      * @var ProductStatusRepository
  100.      */
  101.     protected $productStatusRepository;
  102.     /**
  103.      * @var TagRepository
  104.      */
  105.     protected $tagRepository;
  106.     /**
  107.      * ProductController constructor.
  108.      *
  109.      * @param CsvExportService $csvExportService
  110.      * @param ProductClassRepository $productClassRepository
  111.      * @param ProductImageRepository $productImageRepository
  112.      * @param TaxRuleRepository $taxRuleRepository
  113.      * @param CategoryRepository $categoryRepository
  114.      * @param ProductRepository $productRepository
  115.      * @param BaseInfoRepository $baseInfoRepository
  116.      * @param PageMaxRepository $pageMaxRepository
  117.      * @param ProductStatusRepository $productStatusRepository
  118.      * @param TagRepository $tagRepository
  119.      */
  120.     public function __construct(
  121.         CsvExportService $csvExportService,
  122.         ProductClassRepository $productClassRepository,
  123.         ProductImageRepository $productImageRepository,
  124.         TaxRuleRepository $taxRuleRepository,
  125.         CategoryRepository $categoryRepository,
  126.         ProductRepository $productRepository,
  127.         BaseInfoRepository $baseInfoRepository,
  128.         PageMaxRepository $pageMaxRepository,
  129.         ProductStatusRepository $productStatusRepository,
  130.         TagRepository $tagRepository,
  131.         MakerRepository $makerRepository
  132.     ) {
  133.         $this->csvExportService $csvExportService;
  134.         $this->productClassRepository $productClassRepository;
  135.         $this->productImageRepository $productImageRepository;
  136.         $this->taxRuleRepository $taxRuleRepository;
  137.         $this->categoryRepository $categoryRepository;
  138.         $this->productRepository $productRepository;
  139.         $this->BaseInfo $baseInfoRepository->get();
  140.         $this->pageMaxRepository $pageMaxRepository;
  141.         $this->productStatusRepository $productStatusRepository;
  142.         $this->tagRepository $tagRepository;
  143.         $this->makerRepository $makerRepository;
  144.         ini_set('max_execution_time''600');
  145.     }
  146.     /**
  147.      * @Route("/%eccube_admin_route%/product", name="admin_product", methods={"GET", "POST"})
  148.      * @Route("/%eccube_admin_route%/product/page/{page_no}", requirements={"page_no" = "\d+"}, name="admin_product_page", methods={"GET", "POST"})
  149.      * @Template("@admin/Product/index.twig")
  150.      */
  151.     public function index(Request $requestPaginatorInterface $paginator$page_no null)
  152.     {
  153.         $builder $this->formFactory
  154.             ->createBuilder(SearchProductType::class);
  155.         $event = new EventArgs(
  156.             [
  157.                 'builder' => $builder,
  158.             ],
  159.             $request
  160.         );
  161.         $this->eventDispatcher->dispatch($eventEccubeEvents::ADMIN_PRODUCT_INDEX_INITIALIZE);
  162.         $searchForm $builder->getForm();
  163.         /**
  164.          * ページの表示件数は, 以下の順に優先される.
  165.          * - リクエストパラメータ
  166.          * - セッション
  167.          * - デフォルト値
  168.          * また, セッションに保存する際は mtb_page_maxと照合し, 一致した場合のみ保存する.
  169.          **/
  170.         $page_count $this->session->get('eccube.admin.product.search.page_count',
  171.             $this->eccubeConfig->get('eccube_default_page_count'));
  172.         $page_count_param = (int) $request->get('page_count');
  173.         $pageMaxis $this->pageMaxRepository->findAll();
  174.         if ($page_count_param) {
  175.             foreach ($pageMaxis as $pageMax) {
  176.                 if ($page_count_param == $pageMax->getName()) {
  177.                     $page_count $pageMax->getName();
  178.                     $this->session->set('eccube.admin.product.search.page_count'$page_count);
  179.                     break;
  180.                 }
  181.             }
  182.         }
  183.         if ('POST' === $request->getMethod()) {
  184.             $searchForm->handleRequest($request);
  185.             if ($searchForm->isValid()) {
  186.                 /**
  187.                  * 検索が実行された場合は, セッションに検索条件を保存する.
  188.                  * ページ番号は最初のページ番号に初期化する.
  189.                  */
  190.                 $page_no 1;
  191.                 $searchData $searchForm->getData();
  192.                 // 検索条件, ページ番号をセッションに保持.
  193.                 $this->session->set('eccube.admin.product.search'FormUtil::getViewData($searchForm));
  194.                 $this->session->set('eccube.admin.product.search.page_no'$page_no);
  195.             } else {
  196.                 // 検索エラーの際は, 詳細検索枠を開いてエラー表示する.
  197.                 return [
  198.                     'searchForm' => $searchForm->createView(),
  199.                     'pagination' => [],
  200.                     'pageMaxis' => $pageMaxis,
  201.                     'page_no' => $page_no,
  202.                     'page_count' => $page_count,
  203.                     'has_errors' => true,
  204.                 ];
  205.             }
  206.         } else {
  207.             if (null !== $page_no || $request->get('resume')) {
  208.                 /*
  209.                  * ページ送りの場合または、他画面から戻ってきた場合は, セッションから検索条件を復旧する.
  210.                  */
  211.                 if ($page_no) {
  212.                     // ページ送りで遷移した場合.
  213.                     $this->session->set('eccube.admin.product.search.page_no', (int) $page_no);
  214.                 } else {
  215.                     // 他画面から遷移した場合.
  216.                     $page_no $this->session->get('eccube.admin.product.search.page_no'1);
  217.                 }
  218.                 $viewData $this->session->get('eccube.admin.product.search', []);
  219.                 $searchData FormUtil::submitAndGetData($searchForm$viewData);
  220.             } else {
  221.                 /**
  222.                  * 初期表示の場合.
  223.                  */
  224.                 $page_no 1;
  225.                 // submit default value
  226.                 $viewData FormUtil::getViewData($searchForm);
  227.                 $searchData FormUtil::submitAndGetData($searchForm$viewData);
  228.                 // セッション中の検索条件, ページ番号を初期化.
  229.                 $this->session->set('eccube.admin.product.search'$viewData);
  230.                 $this->session->set('eccube.admin.product.search.page_no'$page_no);
  231.             }
  232.         }
  233.         $qb $this->productRepository->getQueryBuilderBySearchDataForAdmin($searchData);
  234.         $event = new EventArgs(
  235.             [
  236.                 'qb' => $qb,
  237.                 'searchData' => $searchData,
  238.             ],
  239.             $request
  240.         );
  241.         $this->eventDispatcher->dispatch($eventEccubeEvents::ADMIN_PRODUCT_INDEX_SEARCH);
  242.         $sortKey $searchData['sortkey'];
  243.         if (empty($this->productRepository::COLUMNS[$sortKey]) || $sortKey == 'code' || $sortKey == 'status') {
  244.             $pagination $paginator->paginate(
  245.                 $qb,
  246.                 $page_no,
  247.                 $page_count
  248.             );
  249.         } else {
  250.             $pagination $paginator->paginate(
  251.                 $qb,
  252.                 $page_no,
  253.                 $page_count,
  254.                 ['wrap-queries' => true]
  255.             );
  256.         }
  257.         return [
  258.             'searchForm' => $searchForm->createView(),
  259.             'pagination' => $pagination,
  260.             'pageMaxis' => $pageMaxis,
  261.             'page_no' => $page_no,
  262.             'page_count' => $page_count,
  263.             'has_errors' => false,
  264.         ];
  265.     }
  266.     /**
  267.      * @Route("/%eccube_admin_route%/product/spreadsheet_import", name="admin_product_spreadsheet_import", methods={"GET", "POST"})
  268.      * @Template("@admin/Product/spreadsheet_import.twig")
  269.      */
  270.     public function spreadsheetImport(Request $request)
  271.     {
  272.         $credentials_path $this->getParameter('kernel.project_dir').'/app/config/google.json';
  273.         $client = new \Google_Client();
  274.         $client->setScopes([
  275.             \Google_Service_Sheets::SPREADSHEETS// スプレッドシート
  276.             \Google_Service_Sheets::DRIVE// ドライブ
  277.         ]);
  278.         $client->setAuthConfig($credentials_path);
  279.         $spreadsheet_service = new \Google_Service_Sheets($client);
  280.         $spreadsheetId "1sXy_KbSqweE3QFwIgRRTQyowasYIAan3bK7bQM9hLGQ";
  281.         $response $spreadsheet_service->spreadsheets->get($spreadsheetId);
  282.         $sheets $response->getSheets();
  283.         foreach ($sheets as $sheet) {
  284.             $properties $sheet->getProperties();
  285.             if($properties->getTitle() == "ProductALL"){
  286.                 $sheet_id $properties->getSheetId(); 
  287.             }
  288.         }
  289.         $response $spreadsheet_service->spreadsheets_values->get($spreadsheetId'ProductALL!A:AZ');
  290.         $SpreadsheetsValues $response->getValues();
  291.         array_shift($SpreadsheetsValues);
  292.         $category_ids = array();
  293.         $category_ids['cp'] = 7;
  294.         $category_ids['cy'] = 8;
  295.         $category_ids['cg'] = 9;
  296.         $category_ids['tr'] = 10;
  297.         $category_ids['wd'] = 11;
  298.         $category_ids['tg'] = 12;
  299.         $category_ids['mp'] = 25;
  300.         $category_ids['fu'] = 13;
  301.         $category_ids['fe'] = 14;
  302.         $category_ids['mo'] = 15;
  303.         $category_ids['wh'] = 16;
  304.         $category_ids['um'] = 17;
  305.         $category_ids['br'] = 18;
  306.         $category_ids['bl'] = 19;
  307.         $category_ids['gf'] = 20;
  308.         $category_ids['dy'] = 21;
  309.         $category_ids['tf'] = 22;
  310.         $TaxRule $this->taxRuleRepository->findOneBy(["id"=>1]);
  311.         foreach($SpreadsheetsValues as $rowIndex => $cell){
  312.             
  313.             $id $cell[0];
  314.             $Products $this->productRepository->findBy(["note"=>$cell[0]]);
  315.             $Product null;
  316.             $ProductClass null;
  317.             $ProductStock null;
  318.             if (!$Products) {
  319.                 $Product = new Product();
  320.                 $ProductClass = new ProductClass();
  321.                 $ProductStatus $this->productStatusRepository->find(ProductStatus::DISPLAY_HIDE);
  322.                 $Product
  323.                     ->addProductClass($ProductClass)
  324.                     ->setStatus($ProductStatus);
  325.                 $ProductClass
  326.                     ->setVisible(true)
  327.                     ->setStockUnlimited(true)
  328.                     ->setProduct($Product);
  329.                 $ProductStock = new ProductStock();
  330.                 $ProductClass->setTaxRule($TaxRule);
  331.                 $ProductClass->setProductStock($ProductStock);
  332.                 $ProductStock->setProductClass($ProductClass);
  333.             }else{
  334.                 $Product $this->productRepository->findWithSortedClassCategories($id);
  335.                 $ProductClasses $Product->getProductClasses();
  336.                 $ProductClass $ProductClasses[0];
  337.                 $ProductClass->setTaxRule($TaxRule);
  338.                 $ProductStock $ProductClass->getProductStock();
  339.             }
  340.             $maker_name $cell[1];
  341.             $maker_name str_replace("LIXIL(リクシル)","LIXIL",$maker_name);
  342.             $maker_name str_replace("YKKAP","YKK AP",$maker_name);
  343.             $Maker $this->makerRepository->findOneBy(["name"=>$maker_name]);
  344.             if(!$Maker){
  345.                 $Maker = new Maker();
  346.             }
  347.             $Maker->setName($maker_name);
  348.             $Product->setMaker($Maker);                    //メーカ名
  349.             $Product->setName($cell[2]);                //商品名
  350.             $Product->setNote($cell[0]);                //URL
  351.             $Product->setListSpec($cell[4]);            //一覧スペック
  352.             $Product->setDescriptionList($cell[5]);        //一覧コメント
  353.             //カテゴリコード
  354.             //カテゴリ名
  355.             $Category $this->categoryRepository->find($category_ids[$cell[6]]);
  356.             //価格
  357.             //値引率
  358.             //メーカー価格
  359.             $price preg_replace("/[^0-9]/","",$cell[11]);
  360.             $ProductClass->setPrice02($price);        //プラスとしての販売価格
  361.             $Product->setSalesInfomation($cell[12]);        //販売情報
  362.             $Product->setRecommendPoint($cell[13]);        //おすすめポイント
  363.             $Product->setMakerUrl($cell[14]);            //カタログURL
  364.             //商品について 15,16,17,18,19
  365.             //商品規格 20,21,22,23,24
  366.             //商品画像        
  367.             for($i 1;$i 10;$i++){
  368.                 $fileName "p".$cell[0]."_".$cell[6]."_".$i;
  369.                 if(!empty($cell[$i 24])){
  370.                     $image_url $cell[$i 24];
  371.                     $file_info pathinfo($image_url);
  372.                     $image_extension strtolower($file_info['extension']);
  373.                     $image_name $fileName.".".$image_extension;
  374.                     $save_path $this->getSaveImageDir().$image_name;
  375.                     $image_data file_get_contents($cell[$i 24]);
  376.                     file_put_contents($save_path ,$image_data);
  377.                     $ProductImage = new ProductImage();
  378.                     $ProductImage->setFileName($image_name);
  379.                     $ProductImage->setProduct($Product);
  380.                     $ProductImage->setSortNo($i);
  381.                     $Product->addProductImage($ProductImage);
  382.                     $this->entityManager->persist($ProductImage);
  383.                 }
  384.             }
  385.             $this->entityManager->persist($Maker);
  386.             $this->entityManager->persist($ProductClass);
  387.             $this->entityManager->persist($ProductStock);
  388.             $this->entityManager->persist($Product);
  389.             $this->entityManager->flush();
  390.             $ProductCategory = new ProductCategory();
  391.             $ProductCategory->setProduct($Product);
  392.             $ProductCategory->setProductId($Product->getId());
  393.             $ProductCategory->setCategory($Category);
  394.             $ProductCategory->setCategoryId($Category->getId());
  395.             $this->entityManager->persist($ProductCategory);
  396.             $this->entityManager->flush();
  397.         }
  398.             
  399.         return [
  400.             'has_errors' => false,
  401.         ];
  402.         
  403.     }
  404.     private function getSaveImageDir()
  405.     {
  406.         return rtrim($this->getParameter('kernel.project_dir').'/html/upload/save_image/');
  407.     }
  408.     /**
  409.      * @Route("/%eccube_admin_route%/product/classes/{id}/load", name="admin_product_classes_load", methods={"GET"}, requirements={"id" = "\d+"}, methods={"GET"})
  410.      * @Template("@admin/Product/product_class_popup.twig")
  411.      * @ParamConverter("Product", options={"repository_method":"findWithSortedClassCategories"})
  412.      */
  413.     public function loadProductClasses(Request $requestProduct $Product)
  414.     {
  415.         if (!$request->isXmlHttpRequest() && $this->isTokenValid()) {
  416.             throw new BadRequestHttpException();
  417.         }
  418.         $data = [];
  419.         /** @var $Product ProductRepository */
  420.         if (!$Product) {
  421.             throw new NotFoundHttpException();
  422.         }
  423.         if ($Product->hasProductClass()) {
  424.             $class $Product->getProductClasses();
  425.             foreach ($class as $item) {
  426.                 $data[] = $item;
  427.             }
  428.         }
  429.         return [
  430.             'data' => $data,
  431.         ];
  432.     }
  433.     /**
  434.      * 画像アップロード時にリクエストされるメソッド.
  435.      *
  436.      * @see https://pqina.nl/filepond/docs/api/server/#process
  437.      * @Route("/%eccube_admin_route%/product/product/image/process", name="admin_product_image_process", methods={"POST"})
  438.      */
  439.     public function imageProcess(Request $request)
  440.     {
  441.         if (!$request->isXmlHttpRequest() && $this->isTokenValid()) {
  442.             throw new BadRequestHttpException();
  443.         }
  444.         $images $request->files->get('admin_product');
  445.         $allowExtensions = ['gif''jpg''jpeg''png'];
  446.         $files = [];
  447.         if (count($images) > 0) {
  448.             foreach ($images as $img) {
  449.                 foreach ($img as $image) {
  450.                     // ファイルフォーマット検証
  451.                     $mimeType $image->getMimeType();
  452.                     if (!== strpos($mimeType'image')) {
  453.                         throw new UnsupportedMediaTypeHttpException();
  454.                     }
  455.                     // 拡張子
  456.                     $extension $image->getClientOriginalExtension();
  457.                     if (!in_array(strtolower($extension), $allowExtensions)) {
  458.                         throw new UnsupportedMediaTypeHttpException();
  459.                     }
  460.                     $filename date('mdHis').uniqid('_').'.'.$extension;
  461.                     $image->move($this->eccubeConfig['eccube_temp_image_dir'], $filename);
  462.                     $files[] = $filename;
  463.                 }
  464.             }
  465.         }
  466.         $event = new EventArgs(
  467.             [
  468.                 'images' => $images,
  469.                 'files' => $files,
  470.             ],
  471.             $request
  472.         );
  473.         $this->eventDispatcher->dispatch($eventEccubeEvents::ADMIN_PRODUCT_ADD_IMAGE_COMPLETE);
  474.         $files $event->getArgument('files');
  475.         return new Response(array_shift($files));
  476.     }
  477.     /**
  478.      * アップロード画像を取得する際にコールされるメソッド.
  479.      *
  480.      * @see https://pqina.nl/filepond/docs/api/server/#load
  481.      * @Route("/%eccube_admin_route%/product/product/image/load", name="admin_product_image_load", methods={"GET"})
  482.      */
  483.     public function imageLoad(Request $request)
  484.     {
  485.         if (!$request->isXmlHttpRequest()) {
  486.             throw new BadRequestHttpException();
  487.         }
  488.         $dirs = [
  489.             $this->eccubeConfig['eccube_save_image_dir'],
  490.             $this->eccubeConfig['eccube_temp_image_dir'],
  491.         ];
  492.         foreach ($dirs as $dir) {
  493.             if (strpos($request->query->get('source'), '..') !== false) {
  494.                 throw new NotFoundHttpException();
  495.             }
  496.             $image = \realpath($dir.'/'.$request->query->get('source'));
  497.             $dir = \realpath($dir);
  498.             if (\is_file($image) && \str_starts_with($image$dir)) {
  499.                 $file = new \SplFileObject($image);
  500.                 return $this->file($file$file->getBasename());
  501.             }
  502.         }
  503.         throw new NotFoundHttpException();
  504.     }
  505.     /**
  506.      * アップロード画像をすぐ削除する際にコールされるメソッド.
  507.      *
  508.      * @see https://pqina.nl/filepond/docs/api/server/#revert
  509.      * @Route("/%eccube_admin_route%/product/product/image/revert", name="admin_product_image_revert", methods={"DELETE"})
  510.      */
  511.     public function imageRevert(Request $request)
  512.     {
  513.         if (!$request->isXmlHttpRequest() && $this->isTokenValid()) {
  514.             throw new BadRequestHttpException();
  515.         }
  516.         $tempFile $this->eccubeConfig['eccube_temp_image_dir'].'/'.$request->getContent();
  517.         if (is_file($tempFile) && stripos(realpath($tempFile), $this->eccubeConfig['eccube_temp_image_dir']) === 0) {
  518.             $fs = new Filesystem();
  519.             $fs->remove($tempFile);
  520.             return new Response(nullResponse::HTTP_NO_CONTENT);
  521.         }
  522.         throw new NotFoundHttpException();
  523.     }
  524.     /**
  525.      * @Route("/%eccube_admin_route%/product/product/new", name="admin_product_product_new", methods={"GET", "POST"})
  526.      * @Route("/%eccube_admin_route%/product/product/{id}/edit", requirements={"id" = "\d+"}, name="admin_product_product_edit", methods={"GET", "POST"})
  527.      * @Template("@admin/Product/product.twig")
  528.      */
  529.     public function edit(Request $requestRouterInterface $routerCacheUtil $cacheUtil$id null)
  530.     {
  531.         $has_class false;
  532.         if (is_null($id)) {
  533.             $Product = new Product();
  534.             $ProductClass = new ProductClass();
  535.             $ProductStatus $this->productStatusRepository->find(ProductStatus::DISPLAY_HIDE);
  536.             $Product
  537.                 ->addProductClass($ProductClass)
  538.                 ->setStatus($ProductStatus);
  539.             $ProductClass
  540.                 ->setVisible(true)
  541.                 ->setStockUnlimited(true)
  542.                 ->setProduct($Product);
  543.             $ProductStock = new ProductStock();
  544.             $ProductClass->setProductStock($ProductStock);
  545.             $ProductStock->setProductClass($ProductClass);
  546.         } else {
  547.             $Product $this->productRepository->findWithSortedClassCategories($id);
  548.             $ProductClass null;
  549.             $ProductStock null;
  550.             if (!$Product) {
  551.                 throw new NotFoundHttpException();
  552.             }
  553.             // 規格無しの商品の場合は、デフォルト規格を表示用に取得する
  554.             $has_class $Product->hasProductClass();
  555.             if (!$has_class) {
  556.                 $ProductClasses $Product->getProductClasses();
  557.                 foreach ($ProductClasses as $pc) {
  558.                     if (!is_null($pc->getClassCategory1())) {
  559.                         continue;
  560.                     }
  561.                     if ($pc->isVisible()) {
  562.                         $ProductClass $pc;
  563.                         break;
  564.                     }
  565.                 }
  566.                 if ($this->BaseInfo->isOptionProductTaxRule() && $ProductClass->getTaxRule()) {
  567.                     $ProductClass->setTaxRate($ProductClass->getTaxRule()->getTaxRate());
  568.                 }
  569.                 $ProductStock $ProductClass->getProductStock();
  570.             }
  571.         }
  572.         $builder $this->formFactory
  573.             ->createBuilder(ProductType::class, $Product);
  574.         // 規格あり商品の場合、規格関連情報をFormから除外
  575.         if ($has_class) {
  576.             $builder->remove('class');
  577.         }
  578.         $event = new EventArgs(
  579.             [
  580.                 'builder' => $builder,
  581.                 'Product' => $Product,
  582.             ],
  583.             $request
  584.         );
  585.         $this->eventDispatcher->dispatch($eventEccubeEvents::ADMIN_PRODUCT_EDIT_INITIALIZE);
  586.         $form $builder->getForm();
  587.         if (!$has_class) {
  588.             $ProductClass->setStockUnlimited($ProductClass->isStockUnlimited());
  589.             $form['class']->setData($ProductClass);
  590.         }
  591.         // ファイルの登録
  592.         $images = [];
  593.         $ProductImages $Product->getProductImage();
  594.         foreach ($ProductImages as $ProductImage) {
  595.             $images[] = $ProductImage->getFileName();
  596.         }
  597.         $form['images']->setData($images);
  598.         $categories = [];
  599.         $ProductCategories $Product->getProductCategories();
  600.         foreach ($ProductCategories as $ProductCategory) {
  601.             /* @var $ProductCategory \Eccube\Entity\ProductCategory */
  602.             $categories[] = $ProductCategory->getCategory();
  603.         }
  604.         $form['Category']->setData($categories);
  605.         $Tags $Product->getTags();
  606.         $form['Tag']->setData($Tags);
  607.         if ('POST' === $request->getMethod()) {
  608.             $form->handleRequest($request);
  609.             if ($form->isValid()) {
  610.                 log_info('商品登録開始', [$id]);
  611.                 $Product $form->getData();
  612.                 if(!empty($_POST['admin_product']['plg_product_field_related_keyword']) 
  613.                         && empty($_POST['admin_product']['plg_product_field_related_product'])){
  614.                     $search_keyword $_POST['admin_product']['plg_product_field_related_keyword'];
  615.                     $RelatedProducts $this->productRepository->findByLikeName($search_keyword$categories);
  616.                     $related_product_ids = array();
  617.                     foreach ($RelatedProducts as $rp) {
  618.                         $related_product_ids[] = $rp->getId();
  619.                     }
  620.                     $_POST['admin_product']['plg_product_field_related_product'] = $related_product_ids;
  621.                 }
  622.                 if (!$has_class) {
  623.                     $ProductClass $form['class']->getData();
  624.                     // 個別消費税
  625.                     if ($this->BaseInfo->isOptionProductTaxRule()) {
  626.                         if ($ProductClass->getTaxRate() !== null) {
  627.                             if ($ProductClass->getTaxRule()) {
  628.                                 $ProductClass->getTaxRule()->setTaxRate($ProductClass->getTaxRate());
  629.                             } else {
  630.                                 $taxrule $this->taxRuleRepository->newTaxRule();
  631.                                 $taxrule->setTaxRate($ProductClass->getTaxRate());
  632.                                 $taxrule->setApplyDate(new \DateTime());
  633.                                 $taxrule->setProduct($Product);
  634.                                 $taxrule->setProductClass($ProductClass);
  635.                                 $ProductClass->setTaxRule($taxrule);
  636.                             }
  637.                             $ProductClass->getTaxRule()->setTaxRate($ProductClass->getTaxRate());
  638.                         } else {
  639.                             if ($ProductClass->getTaxRule()) {
  640.                                 $this->taxRuleRepository->delete($ProductClass->getTaxRule());
  641.                                 $ProductClass->setTaxRule(null);
  642.                             }
  643.                         }
  644.                     }
  645.                     $this->entityManager->persist($ProductClass);
  646.                     // 在庫情報を作成
  647.                     if (!$ProductClass->isStockUnlimited()) {
  648.                         $ProductStock->setStock($ProductClass->getStock());
  649.                     } else {
  650.                         // 在庫無制限時はnullを設定
  651.                         $ProductStock->setStock(null);
  652.                     }
  653.                     $this->entityManager->persist($ProductStock);
  654.                 }
  655.                 $opt_color = @$_POST['color'];
  656.                 if(!empty($opt_color)){
  657.                     $color = array();
  658.                     foreach($opt_color as $add_color){
  659.                         $cc_empty true;
  660.                         foreach($add_color as $p){
  661.                             if(!empty($p)) $cc_empty false;
  662.                         }
  663.                         if(!$cc_empty){
  664.                             $color[] = $add_color;
  665.                         }
  666.                     }
  667.                     $Product->setSearchWord(serialize($color));
  668.                 }
  669.                 $opt_pp = @$_POST['pp'];
  670.                 if(!empty($opt_pp)){
  671.                     $pp = array();
  672.                     foreach($opt_pp as $add_pp){
  673.                         $pp_empty true;
  674.                         foreach($add_pp as $p){
  675.                             if(!empty($p)) $pp_empty false;
  676.                         }
  677.                         if(!$pp_empty){
  678.                             $pp[] = $add_pp;
  679.                         }
  680.                     }
  681.                     $Product->setFreeArea(serialize($pp));
  682.                 }
  683.                 $opt_op = @$_POST['op'];
  684.                 if(!empty($opt_op)){
  685.                     $op = array();
  686.                     foreach($opt_op as $add_op){
  687.                         $op_empty true;
  688.                         foreach($add_op as $p){
  689.                             if(!empty($p)) $op_empty false;
  690.                         }
  691.                         if(!$op_empty){
  692.                             $op[] = $add_op;
  693.                         }
  694.                     }
  695.                     $Product->setOptionArea(serialize($op));
  696.                 }
  697.                 $opt_oi = @$_POST['oi'];
  698.                 if(!empty($opt_oi)){
  699.                     $oi = array();
  700.                     foreach($opt_oi as $add_oi){
  701.                         $oi_empty true;
  702.                         foreach($add_oi as $p){
  703.                             if(!empty($p)) $oi_empty false;
  704.                         }
  705.                         if(!$oi_empty){
  706.                             $oi[] = $add_oi;
  707.                         }
  708.                         
  709.                     }
  710.                     $Product->setOptionItemArea(serialize($oi));
  711.                 }
  712.                 // カテゴリの登録
  713.                 // 一度クリア
  714.                 /* @var $Product \Eccube\Entity\Product */
  715.                 foreach ($Product->getProductCategories() as $ProductCategory) {
  716.                     $Product->removeProductCategory($ProductCategory);
  717.                     $this->entityManager->remove($ProductCategory);
  718.                 }
  719.                 $this->entityManager->persist($Product);
  720.                 $this->entityManager->flush();
  721.                 $count 1;
  722.                 $Categories $form->get('Category')->getData();
  723.                 $categoriesIdList = [];
  724.                 foreach ($Categories as $Category) {
  725.                     foreach ($Category->getPath() as $ParentCategory) {
  726.                         if (!isset($categoriesIdList[$ParentCategory->getId()])) {
  727.                             $ProductCategory $this->createProductCategory($Product$ParentCategory$count);
  728.                             $this->entityManager->persist($ProductCategory);
  729.                             $count++;
  730.                             /* @var $Product \Eccube\Entity\Product */
  731.                             $Product->addProductCategory($ProductCategory);
  732.                             $categoriesIdList[$ParentCategory->getId()] = true;
  733.                         }
  734.                     }
  735.                     if (!isset($categoriesIdList[$Category->getId()])) {
  736.                         $ProductCategory $this->createProductCategory($Product$Category$count);
  737.                         $this->entityManager->persist($ProductCategory);
  738.                         $count++;
  739.                         /* @var $Product \Eccube\Entity\Product */
  740.                         $Product->addProductCategory($ProductCategory);
  741.                         $categoriesIdList[$Category->getId()] = true;
  742.                     }
  743.                 }
  744.                 // 画像の登録
  745.                 $add_images $form->get('add_images')->getData();
  746.                 foreach ($add_images as $add_image) {
  747.                     $ProductImage = new \Eccube\Entity\ProductImage();
  748.                     $ProductImage
  749.                         ->setFileName($add_image)
  750.                         ->setProduct($Product)
  751.                         ->setSortNo(1);
  752.                     $Product->addProductImage($ProductImage);
  753.                     $this->entityManager->persist($ProductImage);
  754.                     // 移動
  755.                     $file = new File($this->eccubeConfig['eccube_temp_image_dir'].'/'.$add_image);
  756.                     $file->move($this->eccubeConfig['eccube_save_image_dir']);
  757.                 }
  758.                 // 画像の削除
  759.                 $delete_images $form->get('delete_images')->getData();
  760.                 $fs = new Filesystem();
  761.                 foreach ($delete_images as $delete_image) {
  762.                     $ProductImage $this->productImageRepository->findOneBy([
  763.                         'Product' => $Product,
  764.                         'file_name' => $delete_image,
  765.                     ]);
  766.                     if ($ProductImage instanceof ProductImage) {
  767.                         $Product->removeProductImage($ProductImage);
  768.                         $this->entityManager->remove($ProductImage);
  769.                         $this->entityManager->flush();
  770.                         // 他に同じ画像を参照する商品がなければ画像ファイルを削除
  771.                         if (!$this->productImageRepository->findOneBy(['file_name' => $delete_image])) {
  772.                             $fs->remove($this->eccubeConfig['eccube_save_image_dir'].'/'.$delete_image);
  773.                         }
  774.                     } else {
  775.                         // 追加してすぐに削除した画像は、Entityに追加されない
  776.                         $fs->remove($this->eccubeConfig['eccube_temp_image_dir'].'/'.$delete_image);
  777.                     }
  778.                 }
  779.                 $this->entityManager->flush();
  780.                 if (array_key_exists('product_image'$request->request->get('admin_product'))) {
  781.                     $product_image $request->request->get('admin_product')['product_image'];
  782.                     foreach ($product_image as $sortNo => $filename) {
  783.                         $ProductImage $this->productImageRepository
  784.                             ->findOneBy([
  785.                                 'file_name' => pathinfo($filenamePATHINFO_BASENAME),
  786.                                 'Product' => $Product,
  787.                             ]);
  788.                         if ($ProductImage !== null) {
  789.                             $ProductImage->setSortNo($sortNo);
  790.                             $this->entityManager->persist($ProductImage);
  791.                         }
  792.                     }
  793.                     $this->entityManager->flush();
  794.                 }
  795.                 // 商品タグの登録
  796.                 // 商品タグを一度クリア
  797.                 $ProductTags $Product->getProductTag();
  798.                 foreach ($ProductTags as $ProductTag) {
  799.                     $Product->removeProductTag($ProductTag);
  800.                     $this->entityManager->remove($ProductTag);
  801.                 }
  802.                 // 商品タグの登録
  803.                 $Tags $form->get('Tag')->getData();
  804.                 foreach ($Tags as $Tag) {
  805.                     $ProductTag = new ProductTag();
  806.                     $ProductTag
  807.                         ->setProduct($Product)
  808.                         ->setTag($Tag);
  809.                     $Product->addProductTag($ProductTag);
  810.                     $this->entityManager->persist($ProductTag);
  811.                 }
  812.                 $Product->setUpdateDate(new \DateTime());
  813.                 $this->entityManager->flush();
  814.                 log_info('商品登録完了', [$id]);
  815.                 $event = new EventArgs(
  816.                     [
  817.                         'form' => $form,
  818.                         'Product' => $Product,
  819.                     ],
  820.                     $request
  821.                 );
  822.                 $this->eventDispatcher->dispatch($eventEccubeEvents::ADMIN_PRODUCT_EDIT_COMPLETE);
  823.                 $this->addSuccess('admin.common.save_complete''admin');
  824.                 if ($returnLink $form->get('return_link')->getData()) {
  825.                     try {
  826.                         // $returnLinkはpathの形式で渡される. pathが存在するかをルータでチェックする.
  827.                         $pattern '/^'.preg_quote($request->getBasePath(), '/').'/';
  828.                         $returnLink preg_replace($pattern''$returnLink);
  829.                         $result $router->match($returnLink);
  830.                         // パラメータのみ抽出
  831.                         $params array_filter($result, function ($key) {
  832.                             return !== \strpos($key'_');
  833.                         }, ARRAY_FILTER_USE_KEY);
  834.                         // pathからurlを再構築してリダイレクト.
  835.                         return $this->redirectToRoute($result['_route'], $params);
  836.                     } catch (\Exception $e) {
  837.                         // マッチしない場合はログ出力してスキップ.
  838.                         log_warning('URLの形式が不正です。');
  839.                     }
  840.                 }
  841.                 $cacheUtil->clearDoctrineCache();
  842.                 return $this->redirectToRoute('admin_product_product_edit', ['id' => $Product->getId()]);
  843.             }
  844.         }
  845.         // 検索結果の保持
  846.         $builder $this->formFactory
  847.             ->createBuilder(SearchProductType::class);
  848.         $event = new EventArgs(
  849.             [
  850.                 'builder' => $builder,
  851.                 'Product' => $Product,
  852.             ],
  853.             $request
  854.         );
  855.         $this->eventDispatcher->dispatch($eventEccubeEvents::ADMIN_PRODUCT_EDIT_SEARCH);
  856.         $searchForm $builder->getForm();
  857.         if ('POST' === $request->getMethod()) {
  858.             $searchForm->handleRequest($request);
  859.         }
  860.         // Get Tags
  861.         $TagsList $this->tagRepository->getList();
  862.         // ツリー表示のため、ルートからのカテゴリを取得
  863.         $TopCategories $this->categoryRepository->getList(null);
  864.         $ChoicedCategoryIds array_map(function ($Category) {
  865.             return $Category->getId();
  866.         }, $form->get('Category')->getData());
  867.         $color = array();
  868.         if(!empty($Product->getSearchWord())){
  869.             $color_tmp unserialize($Product->getSearchWord());
  870.             foreach($color_tmp as $key => $cc){
  871.                 $cc_empty true;
  872.                 foreach($cc as $c){
  873.                     if(!empty($c)) $cc_empty false;
  874.                 }
  875.                 if(!$cc_empty){
  876.                     $color[$key] = $cc;
  877.                 }
  878.             }
  879.         }
  880.         $pp = array();
  881.         if(!empty($Product->getFreeArea())){
  882.             $pp_tmp unserialize($Product->getFreeArea());
  883.             foreach($pp_tmp as $key => $item){
  884.                 if(empty($item['c'])){ $item['c'] = ""; }
  885.                 if(empty($item['ct'])){ $item['ct'] = 0; }
  886.                 $pp[$key] = $item;
  887.             }
  888.         }
  889.         $op = array();
  890.         if(!empty($Product->getOptionArea())){
  891.             $op unserialize($Product->getOptionArea());
  892.         }
  893.         $oi = array();
  894.         if(!empty($Product->getOptionItemArea())){
  895.             $oi_tmp unserialize($Product->getOptionItemArea());
  896.             foreach($oi_tmp as $key => $item){
  897.                 $oi[] = $item;
  898.             }
  899.         }
  900.         return [
  901.             'Product' => $Product,
  902.             'color' => $color,
  903.             'pp' => $pp,
  904.             'op' => $op,
  905.             'oi' => $oi,
  906.             'Tags' => $Tags,
  907.             'TagsList' => $TagsList,
  908.             'form' => $form->createView(),
  909.             'searchForm' => $searchForm->createView(),
  910.             'has_class' => $has_class,
  911.             'id' => $id,
  912.             'TopCategories' => $TopCategories,
  913.             'ChoicedCategoryIds' => $ChoicedCategoryIds,
  914.         ];
  915.     }
  916.     /**
  917.      * @Route("/%eccube_admin_route%/product/product/{id}/delete", requirements={"id" = "\d+"}, name="admin_product_product_delete", methods={"DELETE"})
  918.      */
  919.     public function delete(Request $requestCacheUtil $cacheUtil$id null)
  920.     {
  921.         $this->isTokenValid();
  922.         $session $request->getSession();
  923.         $page_no intval($session->get('eccube.admin.product.search.page_no'));
  924.         $page_no $page_no $page_no Constant::ENABLED;
  925.         $success false;
  926.         if (!is_null($id)) {
  927.             /* @var $Product \Eccube\Entity\Product */
  928.             $Product $this->productRepository->find($id);
  929.             if (!$Product) {
  930.                 if ($request->isXmlHttpRequest()) {
  931.                     $message trans('admin.common.delete_error_already_deleted');
  932.                     return $this->json(['success' => $success'message' => $message]);
  933.                 } else {
  934.                     $this->deleteMessage();
  935.                     $rUrl $this->generateUrl('admin_product_page', ['page_no' => $page_no]).'?resume='.Constant::ENABLED;
  936.                     return $this->redirect($rUrl);
  937.                 }
  938.             }
  939.             if ($Product instanceof Product) {
  940.                 log_info('商品削除開始', [$id]);
  941.                 $deleteImages $Product->getProductImage();
  942.                 $ProductClasses $Product->getProductClasses();
  943.                 try {
  944.                     $this->productRepository->delete($Product);
  945.                     $this->entityManager->flush();
  946.                     $event = new EventArgs(
  947.                         [
  948.                             'Product' => $Product,
  949.                             'ProductClass' => $ProductClasses,
  950.                             'deleteImages' => $deleteImages,
  951.                         ],
  952.                         $request
  953.                     );
  954.                     $this->eventDispatcher->dispatch($eventEccubeEvents::ADMIN_PRODUCT_DELETE_COMPLETE);
  955.                     $deleteImages $event->getArgument('deleteImages');
  956.                     // 画像ファイルの削除(commit後に削除させる)
  957.                     /** @var ProductImage $deleteImage */
  958.                     foreach ($deleteImages as $deleteImage) {
  959.                         if ($this->productImageRepository->findOneBy(['file_name' => $deleteImage->getFileName()])) {
  960.                             continue;
  961.                         }
  962.                         try {
  963.                             $fs = new Filesystem();
  964.                             $fs->remove($this->eccubeConfig['eccube_save_image_dir'].'/'.$deleteImage);
  965.                         } catch (\Exception $e) {
  966.                             // エラーが発生しても無視する
  967.                         }
  968.                     }
  969.                     log_info('商品削除完了', [$id]);
  970.                     $success true;
  971.                     $message trans('admin.common.delete_complete');
  972.                     $cacheUtil->clearDoctrineCache();
  973.                 } catch (ForeignKeyConstraintViolationException $e) {
  974.                     log_info('商品削除エラー', [$id]);
  975.                     $message trans('admin.common.delete_error_foreign_key', ['%name%' => $Product->getName()]);
  976.                 }
  977.             } else {
  978.                 log_info('商品削除エラー', [$id]);
  979.                 $message trans('admin.common.delete_error');
  980.             }
  981.         } else {
  982.             log_info('商品削除エラー', [$id]);
  983.             $message trans('admin.common.delete_error');
  984.         }
  985.         if ($request->isXmlHttpRequest()) {
  986.             return $this->json(['success' => $success'message' => $message]);
  987.         } else {
  988.             if ($success) {
  989.                 $this->addSuccess($message'admin');
  990.             } else {
  991.                 $this->addError($message'admin');
  992.             }
  993.             $rUrl $this->generateUrl('admin_product_page', ['page_no' => $page_no]).'?resume='.Constant::ENABLED;
  994.             return $this->redirect($rUrl);
  995.         }
  996.     }
  997.     /**
  998.      * @Route("/%eccube_admin_route%/product/product/{id}/copy", requirements={"id" = "\d+"}, name="admin_product_product_copy", methods={"POST"})
  999.      */
  1000.     public function copy(Request $request$id null)
  1001.     {
  1002.         $this->isTokenValid();
  1003.         if (!is_null($id)) {
  1004.             $Product $this->productRepository->find($id);
  1005.             if ($Product instanceof Product) {
  1006.                 $CopyProduct = clone $Product;
  1007.                 $CopyProduct->copy();
  1008.                 $ProductStatus $this->productStatusRepository->find(ProductStatus::DISPLAY_HIDE);
  1009.                 $CopyProduct->setStatus($ProductStatus);
  1010.                 $CopyProductCategories $CopyProduct->getProductCategories();
  1011.                 foreach ($CopyProductCategories as $Category) {
  1012.                     $this->entityManager->persist($Category);
  1013.                 }
  1014.                 // 規格あり商品の場合は, デフォルトの商品規格を取得し登録する.
  1015.                 if ($CopyProduct->hasProductClass()) {
  1016.                     $dummyClass $this->productClassRepository->findOneBy([
  1017.                         'visible' => false,
  1018.                         'ClassCategory1' => null,
  1019.                         'ClassCategory2' => null,
  1020.                         'Product' => $Product,
  1021.                     ]);
  1022.                     $dummyClass = clone $dummyClass;
  1023.                     $dummyClass->setProduct($CopyProduct);
  1024.                     $CopyProduct->addProductClass($dummyClass);
  1025.                 }
  1026.                 $CopyProductClasses $CopyProduct->getProductClasses();
  1027.                 foreach ($CopyProductClasses as $Class) {
  1028.                     $Stock $Class->getProductStock();
  1029.                     $CopyStock = clone $Stock;
  1030.                     $CopyStock->setProductClass($Class);
  1031.                     $this->entityManager->persist($CopyStock);
  1032.                     $TaxRule $Class->getTaxRule();
  1033.                     if ($TaxRule) {
  1034.                         $CopyTaxRule = clone $TaxRule;
  1035.                         $CopyTaxRule->setProductClass($Class);
  1036.                         $CopyTaxRule->setProduct($CopyProduct);
  1037.                         $this->entityManager->persist($CopyTaxRule);
  1038.                     }
  1039.                     $this->entityManager->persist($Class);
  1040.                 }
  1041.                 $Images $CopyProduct->getProductImage();
  1042.                 foreach ($Images as $Image) {
  1043.                     // 画像ファイルを新規作成
  1044.                     $extension pathinfo($Image->getFileName(), PATHINFO_EXTENSION);
  1045.                     $filename date('mdHis').uniqid('_').'.'.$extension;
  1046.                     try {
  1047.                         $fs = new Filesystem();
  1048.                         $fs->copy($this->eccubeConfig['eccube_save_image_dir'].'/'.$Image->getFileName(), $this->eccubeConfig['eccube_save_image_dir'].'/'.$filename);
  1049.                     } catch (\Exception $e) {
  1050.                         // エラーが発生しても無視する
  1051.                     }
  1052.                     $Image->setFileName($filename);
  1053.                     $this->entityManager->persist($Image);
  1054.                 }
  1055.                 $Tags $CopyProduct->getProductTag();
  1056.                 foreach ($Tags as $Tag) {
  1057.                     $this->entityManager->persist($Tag);
  1058.                 }
  1059.                 $this->entityManager->persist($CopyProduct);
  1060.                 $this->entityManager->flush();
  1061.                 $event = new EventArgs(
  1062.                     [
  1063.                         'Product' => $Product,
  1064.                         'CopyProduct' => $CopyProduct,
  1065.                         'CopyProductCategories' => $CopyProductCategories,
  1066.                         'CopyProductClasses' => $CopyProductClasses,
  1067.                         'images' => $Images,
  1068.                         'Tags' => $Tags,
  1069.                     ],
  1070.                     $request
  1071.                 );
  1072.                 $this->eventDispatcher->dispatch($eventEccubeEvents::ADMIN_PRODUCT_COPY_COMPLETE);
  1073.                 $this->addSuccess('admin.product.copy_complete''admin');
  1074.                 return $this->redirectToRoute('admin_product_product_edit', ['id' => $CopyProduct->getId()]);
  1075.             } else {
  1076.                 $this->addError('admin.product.copy_error''admin');
  1077.             }
  1078.         } else {
  1079.             $msg trans('admin.product.copy_error');
  1080.             $this->addError($msg'admin');
  1081.         }
  1082.         return $this->redirectToRoute('admin_product');
  1083.     }
  1084.     /**
  1085.      * 商品CSVの出力.
  1086.      *
  1087.      * @Route("/%eccube_admin_route%/product/export", name="admin_product_export", methods={"GET"})
  1088.      *
  1089.      * @param Request $request
  1090.      *
  1091.      * @return StreamedResponse
  1092.      */
  1093.     public function export(Request $request)
  1094.     {
  1095.         // タイムアウトを無効にする.
  1096.         set_time_limit(0);
  1097.         // sql loggerを無効にする.
  1098.         $em $this->entityManager;
  1099.         $em->getConfiguration()->setSQLLogger(null);
  1100.         $response = new StreamedResponse();
  1101.         $response->setCallback(function () use ($request) {
  1102.             // CSV種別を元に初期化.
  1103.             $this->csvExportService->initCsvType(CsvType::CSV_TYPE_PRODUCT);
  1104.             // 商品データ検索用のクエリビルダを取得.
  1105.             $qb $this->csvExportService
  1106.                 ->getProductQueryBuilder($request);
  1107.             // ヘッダ行の出力.
  1108.             $this->csvExportService->exportHeader();
  1109.             // Get stock status
  1110.             $isOutOfStock 0;
  1111.             $session $request->getSession();
  1112.             if ($session->has('eccube.admin.product.search')) {
  1113.                 $searchData $session->get('eccube.admin.product.search', []);
  1114.                 if (isset($searchData['stock_status']) && $searchData['stock_status'] === 0) {
  1115.                     $isOutOfStock 1;
  1116.                 }
  1117.             }
  1118.             // joinする場合はiterateが使えないため, select句をdistinctする.
  1119.             // http://qiita.com/suin/items/2b1e98105fa3ef89beb7
  1120.             // distinctのmysqlとpgsqlの挙動をあわせる.
  1121.             // http://uedatakeshi.blogspot.jp/2010/04/distinct-oeder-by-postgresmysql.html
  1122.             $qb->resetDQLPart('select');
  1123.             if ($isOutOfStock) {
  1124.                 $qb->select('p, pc')
  1125.                     ->distinct();
  1126.             } else {
  1127.                 $qb->select('p')
  1128.                     ->distinct();
  1129.             }
  1130.             // データ行の出力.
  1131.             $this->csvExportService->setExportQueryBuilder($qb);
  1132.             $this->csvExportService->exportData(function ($entityCsvExportService $csvService) use ($request) {
  1133.                 $Csvs $csvService->getCsvs();
  1134.                 /** @var $Product \Eccube\Entity\Product */
  1135.                 $Product $entity;
  1136.                 /** @var $ProductClasses \Eccube\Entity\ProductClass[] */
  1137.                 $ProductClasses $Product->getProductClasses();
  1138.                 foreach ($ProductClasses as $ProductClass) {
  1139.                     $ExportCsvRow = new ExportCsvRow();
  1140.                     // CSV出力項目と合致するデータを取得.
  1141.                     foreach ($Csvs as $Csv) {
  1142.                         // 商品データを検索.
  1143.                         $ExportCsvRow->setData($csvService->getData($Csv$Product));
  1144.                         if ($ExportCsvRow->isDataNull()) {
  1145.                             // 商品規格情報を検索.
  1146.                             $ExportCsvRow->setData($csvService->getData($Csv$ProductClass));
  1147.                         }
  1148.                         $event = new EventArgs(
  1149.                             [
  1150.                                 'csvService' => $csvService,
  1151.                                 'Csv' => $Csv,
  1152.                                 'ProductClass' => $ProductClass,
  1153.                                 'ExportCsvRow' => $ExportCsvRow,
  1154.                             ],
  1155.                             $request
  1156.                         );
  1157.                         $this->eventDispatcher->dispatch($eventEccubeEvents::ADMIN_PRODUCT_CSV_EXPORT);
  1158.                         $ExportCsvRow->pushData();
  1159.                     }
  1160.                     // $row[] = number_format(memory_get_usage(true));
  1161.                     // 出力.
  1162.                     $csvService->fputcsv($ExportCsvRow->getRow());
  1163.                 }
  1164.             });
  1165.         });
  1166.         $now = new \DateTime();
  1167.         $filename 'product_'.$now->format('YmdHis').'.csv';
  1168.         $response->headers->set('Content-Type''application/octet-stream');
  1169.         $response->headers->set('Content-Disposition''attachment; filename='.$filename);
  1170.         log_info('商品CSV出力ファイル名', [$filename]);
  1171.         return $response;
  1172.     }
  1173.     /**
  1174.      * ProductCategory作成
  1175.      *
  1176.      * @param \Eccube\Entity\Product $Product
  1177.      * @param \Eccube\Entity\Category $Category
  1178.      * @param integer $count
  1179.      *
  1180.      * @return \Eccube\Entity\ProductCategory
  1181.      */
  1182.     private function createProductCategory($Product$Category$count)
  1183.     {
  1184.         $ProductCategory = new ProductCategory();
  1185.         $ProductCategory->setProduct($Product);
  1186.         $ProductCategory->setProductId($Product->getId());
  1187.         $ProductCategory->setCategory($Category);
  1188.         $ProductCategory->setCategoryId($Category->getId());
  1189.         return $ProductCategory;
  1190.     }
  1191.     /**
  1192.      * Bulk public action
  1193.      *
  1194.      * @Route("/%eccube_admin_route%/product/bulk/product-status/{id}", requirements={"id" = "\d+"}, name="admin_product_bulk_product_status", methods={"POST"})
  1195.      *
  1196.      * @param Request $request
  1197.      * @param ProductStatus $ProductStatus
  1198.      *
  1199.      * @return RedirectResponse
  1200.      */
  1201.     public function bulkProductStatus(Request $requestProductStatus $ProductStatusCacheUtil $cacheUtil)
  1202.     {
  1203.         $this->isTokenValid();
  1204.         /** @var Product[] $Products */
  1205.         $Products $this->productRepository->findBy(['id' => $request->get('ids')]);
  1206.         $count 0;
  1207.         foreach ($Products as $Product) {
  1208.             try {
  1209.                 $Product->setStatus($ProductStatus);
  1210.                 $this->productRepository->save($Product);
  1211.                 $count++;
  1212.             } catch (\Exception $e) {
  1213.                 $this->addError($e->getMessage(), 'admin');
  1214.             }
  1215.         }
  1216.         try {
  1217.             if ($count) {
  1218.                 $this->entityManager->flush();
  1219.                 $msg $this->translator->trans('admin.product.bulk_change_status_complete', [
  1220.                     '%count%' => $count,
  1221.                     '%status%' => $ProductStatus->getName(),
  1222.                 ]);
  1223.                 $this->addSuccess($msg'admin');
  1224.                 $cacheUtil->clearDoctrineCache();
  1225.             }
  1226.         } catch (\Exception $e) {
  1227.             $this->addError($e->getMessage(), 'admin');
  1228.         }
  1229.         return $this->redirectToRoute('admin_product', ['resume' => Constant::ENABLED]);
  1230.     }
  1231. }