Completed
Pull Request — 4.0 (#4898)
by
unknown
04:55
created

ProductController   F

Complexity

Total Complexity 105

Size/Duplication

Total Lines 1062
Duplicated Lines 14.69 %

Coupling/Cohesion

Components 1
Dependencies 46

Test Coverage

Coverage 65.06%

Importance

Changes 0
Metric Value
dl 156
loc 1062
ccs 313
cts 481
cp 0.6506
rs 1.352
c 0
b 0
f 0
wmc 105
lcom 1
cbo 46

11 Methods

Rating   Name   Duplication   Size   Complexity  
B loadProductClasses() 0 25 6
A __construct() 23 23 1
C index() 59 119 9
C addImage() 0 99 14
F edit() 28 318 37
C delete() 36 93 12
C copy() 0 96 10
A display() 0 14 2
C export() 0 101 8
A createProductCategory() 10 10 1
A bulkProductStatus() 0 32 5

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like ProductController often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use ProductController, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/*
4
 * This file is part of EC-CUBE
5
 *
6
 * Copyright(c) EC-CUBE CO.,LTD. All Rights Reserved.
7
 *
8
 * http://www.ec-cube.co.jp/
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13
14
namespace Eccube\Controller\Admin\Product;
15
16
use Doctrine\DBAL\Exception\ForeignKeyConstraintViolationException;
17
use Eccube\Common\Constant;
18
use Eccube\Controller\AbstractController;
19
use Eccube\Entity\BaseInfo;
20
use Eccube\Entity\ExportCsvRow;
21
use Eccube\Entity\Master\CsvType;
22
use Eccube\Entity\Master\ProductStatus;
23
use Eccube\Entity\Product;
24
use Eccube\Entity\ProductCategory;
25
use Eccube\Entity\ProductClass;
26
use Eccube\Entity\ProductImage;
27
use Eccube\Entity\ProductStock;
28
use Eccube\Entity\ProductTag;
29
use Eccube\Event\EccubeEvents;
30
use Eccube\Event\EventArgs;
31
use Eccube\Form\Type\Admin\ProductType;
32
use Eccube\Form\Type\Admin\SearchProductType;
33
use Eccube\Repository\BaseInfoRepository;
34
use Eccube\Repository\CategoryRepository;
35
use Eccube\Repository\Master\PageMaxRepository;
36
use Eccube\Repository\Master\ProductStatusRepository;
37
use Eccube\Repository\ProductClassRepository;
38
use Eccube\Repository\ProductImageRepository;
39
use Eccube\Repository\ProductRepository;
40
use Eccube\Repository\TagRepository;
41
use Eccube\Repository\TaxRuleRepository;
42
use Eccube\Service\CsvExportService;
43
use Eccube\Util\CacheUtil;
44
use Eccube\Util\FormUtil;
45
use Knp\Component\Pager\Paginator;
46
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
47
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
48
use Symfony\Component\Filesystem\Filesystem;
49
use Symfony\Component\HttpFoundation\File\File;
50
use Symfony\Component\HttpFoundation\RedirectResponse;
51
use Symfony\Component\HttpFoundation\Request;
52
use Symfony\Component\HttpFoundation\StreamedResponse;
53
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
54
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
55
use Symfony\Component\HttpKernel\Exception\UnsupportedMediaTypeHttpException;
56
use Symfony\Component\Routing\Annotation\Route;
57
use Symfony\Component\Routing\RouterInterface;
58
59
class ProductController extends AbstractController
60
{
61
    /**
62
     * @var CsvExportService
63
     */
64
    protected $csvExportService;
65
66
    /**
67
     * @var ProductClassRepository
68
     */
69
    protected $productClassRepository;
70
71
    /**
72
     * @var ProductImageRepository
73
     */
74
    protected $productImageRepository;
75
76
    /**
77
     * @var TaxRuleRepository
78
     */
79
    protected $taxRuleRepository;
80
81
    /**
82
     * @var CategoryRepository
83
     */
84
    protected $categoryRepository;
85
86
    /**
87
     * @var ProductRepository
88
     */
89
    protected $productRepository;
90
91
    /**
92
     * @var BaseInfo
93
     */
94
    protected $BaseInfo;
95
96
    /**
97
     * @var PageMaxRepository
98
     */
99
    protected $pageMaxRepository;
100
101
    /**
102
     * @var ProductStatusRepository
103
     */
104
    protected $productStatusRepository;
105
106
    /**
107
     * @var TagRepository
108
     */
109
    protected $tagRepository;
110
111
    /**
112
     * ProductController constructor.
113
     *
114
     * @param CsvExportService $csvExportService
115
     * @param ProductClassRepository $productClassRepository
116
     * @param ProductImageRepository $productImageRepository
117
     * @param TaxRuleRepository $taxRuleRepository
118
     * @param CategoryRepository $categoryRepository
119
     * @param ProductRepository $productRepository
120
     * @param BaseInfoRepository $baseInfoRepository
121
     * @param PageMaxRepository $pageMaxRepository
122
     * @param ProductStatusRepository $productStatusRepository
123
     * @param TagRepository $tagRepository
124 26
     */
125 View Code Duplication
    public function __construct(
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
126
        CsvExportService $csvExportService,
127
        ProductClassRepository $productClassRepository,
128
        ProductImageRepository $productImageRepository,
129
        TaxRuleRepository $taxRuleRepository,
130
        CategoryRepository $categoryRepository,
131
        ProductRepository $productRepository,
132
        BaseInfoRepository $baseInfoRepository,
133
        PageMaxRepository $pageMaxRepository,
134
        ProductStatusRepository $productStatusRepository,
135
        TagRepository $tagRepository
136 26
    ) {
137 26
        $this->csvExportService = $csvExportService;
138 26
        $this->productClassRepository = $productClassRepository;
139 26
        $this->productImageRepository = $productImageRepository;
140 26
        $this->taxRuleRepository = $taxRuleRepository;
141 26
        $this->categoryRepository = $categoryRepository;
142 26
        $this->productRepository = $productRepository;
143 26
        $this->BaseInfo = $baseInfoRepository->get();
144 26
        $this->pageMaxRepository = $pageMaxRepository;
145 26
        $this->productStatusRepository = $productStatusRepository;
146
        $this->tagRepository = $tagRepository;
147
    }
148
149
    /**
150
     * @Route("/%eccube_admin_route%/product", name="admin_product")
151
     * @Route("/%eccube_admin_route%/product/page/{page_no}", requirements={"page_no" = "\d+"}, name="admin_product_page")
152
     * @Template("@admin/Product/index.twig")
153 4
     */
154
    public function index(Request $request, $page_no = null, Paginator $paginator)
155 4
    {
156 4
        $builder = $this->formFactory
157
            ->createBuilder(SearchProductType::class);
158 4
159
        $event = new EventArgs(
160 4
            [
161
                'builder' => $builder,
162 4
            ],
163
            $request
164 4
        );
165
        $this->eventDispatcher->dispatch(EccubeEvents::ADMIN_PRODUCT_INDEX_INITIALIZE, $event);
166 4
167
        $searchForm = $builder->getForm();
168
169
        /**
170
         * ページの表示件数は, 以下の順に優先される.
171
         * - リクエストパラメータ
172
         * - セッション
173
         * - デフォルト値
174
         * また, セッションに保存する際は mtb_page_maxと照合し, 一致した場合のみ保存する.
175 4
         **/
176 4
        $page_count = $this->session->get('eccube.admin.product.search.page_count',
177
            $this->eccubeConfig->get('eccube_default_page_count'));
178 4
179 4
        $page_count_param = (int) $request->get('page_count');
180
        $pageMaxis = $this->pageMaxRepository->findAll();
181 4
182 View Code Duplication
        if ($page_count_param) {
183
            foreach ($pageMaxis as $pageMax) {
184
                if ($page_count_param == $pageMax->getName()) {
185
                    $page_count = $pageMax->getName();
186
                    $this->session->set('eccube.admin.product.search.page_count', $page_count);
187
                    break;
188
                }
189
            }
190
        }
191 4
192 2
        if ('POST' === $request->getMethod()) {
193
            $searchForm->handleRequest($request);
194 2
195 View Code Duplication
            if ($searchForm->isValid()) {
196
                /**
197
                 * 検索が実行された場合は, セッションに検索条件を保存する.
198
                 * ページ番号は最初のページ番号に初期化する.
199 2
                 */
200 2
                $page_no = 1;
201
                $searchData = $searchForm->getData();
202
203 2
                // 検索条件, ページ番号をセッションに保持.
204 2
                $this->session->set('eccube.admin.product.search', FormUtil::getViewData($searchForm));
205
                $this->session->set('eccube.admin.product.search.page_no', $page_no);
206
            } else {
207
                // 検索エラーの際は, 詳細検索枠を開いてエラー表示する.
208 2
                return [
209
                    'searchForm' => $searchForm->createView(),
210
                    'pagination' => [],
211
                    'pageMaxis' => $pageMaxis,
212
                    'page_no' => $page_no,
213
                    'page_count' => $page_count,
214
                    'has_errors' => true,
215
                ];
216
            }
217 2 View Code Duplication
        } else {
218
            if (null !== $page_no || $request->get('resume')) {
219
                /*
220
                 * ページ送りの場合または、他画面から戻ってきた場合は, セッションから検索条件を復旧する.
221 1
                 */
222
                if ($page_no) {
223 1
                    // ページ送りで遷移した場合.
224
                    $this->session->set('eccube.admin.product.search.page_no', (int) $page_no);
225
                } else {
226
                    // 他画面から遷移した場合.
227
                    $page_no = $this->session->get('eccube.admin.product.search.page_no', 1);
228 1
                }
229 1
                $viewData = $this->session->get('eccube.admin.product.search', []);
230
                $searchData = FormUtil::submitAndGetData($searchForm, $viewData);
231
            } else {
232
                /**
233
                 * 初期表示の場合.
234 1
                 */
235
                $page_no = 1;
236 1
                // submit default value
237 1
                $viewData = FormUtil::getViewData($searchForm);
238
                $searchData = FormUtil::submitAndGetData($searchForm, $viewData);
239
240 1
                // セッション中の検索条件, ページ番号を初期化.
241 1
                $this->session->set('eccube.admin.product.search', $viewData);
242
                $this->session->set('eccube.admin.product.search.page_no', $page_no);
243
            }
244
        }
245 4
246
        $qb = $this->productRepository->getQueryBuilderBySearchDataForAdmin($searchData);
247 4
248
        $event = new EventArgs(
249 4
            [
250 4
                'qb' => $qb,
251
                'searchData' => $searchData,
252 4
            ],
253
            $request
254
        );
255 4
256
        $this->eventDispatcher->dispatch(EccubeEvents::ADMIN_PRODUCT_INDEX_SEARCH, $event);
257 4
258 4
        $pagination = $paginator->paginate(
259 4
            $qb,
260 4
            $page_no,
261
            $page_count
262
        );
263
264 4
        return [
265 4
            'searchForm' => $searchForm->createView(),
266 4
            'pagination' => $pagination,
267 4
            'pageMaxis' => $pageMaxis,
268 4
            'page_no' => $page_no,
269
            'page_count' => $page_count,
270
            'has_errors' => false,
271
        ];
272
    }
273
274
    /**
275
     * @Route("/%eccube_admin_route%/product/classes/{id}/load", name="admin_product_classes_load", methods={"GET"}, requirements={"id" = "\d+"})
276
     * @Template("@admin/Product/product_class_popup.twig")
277
     * @ParamConverter("Product")
278
     */
279 1
    public function loadProductClasses(Request $request, Product $Product)
280
    {
281 1
        if (!$request->isXmlHttpRequest()) {
282
            throw new BadRequestHttpException();
283
        }
284
285 1
        $data = [];
286
        /** @var $Product ProductRepository */
287 1
        if (!$Product) {
288
            throw new NotFoundHttpException();
289
        }
290
291 1
        if ($Product->hasProductClass()) {
0 ignored issues
show
Documentation Bug introduced by
The method hasProductClass does not exist on object<Eccube\Repository\ProductRepository>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
292 1
            $class = $Product->getProductClasses();
0 ignored issues
show
Documentation Bug introduced by
The method getProductClasses does not exist on object<Eccube\Repository\ProductRepository>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
293 1
            foreach ($class as $item) {
294 1
                if ($item['visible']) {
295 1
                    $data[] = $item;
296
                }
297
            }
298
        }
299
300
        return [
301 1
            'data' => $data,
302
        ];
303
    }
304
305
    /**
306
     * @Route("/%eccube_admin_route%/product/product/image/add", name="admin_product_image_add", methods={"POST"})
307
     */
308
    public function addImage(Request $request)
309
    {
310
        if (!$request->isXmlHttpRequest()) {
311
            throw new BadRequestHttpException();
312
        }
313
314
        $images = $request->files->get('admin_product');
315
316
        $allowExtensions = ['gif', 'jpg', 'jpeg', 'png'];
317
        $files = [];
318
        if (count($images) > 0) {
319
            foreach ($images as $img) {
320
                foreach ($img as $image) {
321
                    //ファイルフォーマット検証
322
                    $mimeType = $image->getMimeType();
323
                    if (0 !== strpos($mimeType, 'image')) {
324
                        throw new UnsupportedMediaTypeHttpException();
325
                    }
326
327
                    // 拡張子
328
                    $extension = $image->getClientOriginalExtension();
329
                    if (!in_array(strtolower($extension), $allowExtensions)) {
330
                        throw new UnsupportedMediaTypeHttpException();
331
                    }
332
333
                    // リサイズ処理の可否('eccube_product_image_resize')
334
                    if ($this->eccubeConfig->get('eccube_product_image_resize')) {
335
336
                        // 加工前の画像の情報を取得
337
                        list($origin_w, $origin_h, $type) = getimagesize($image);
338
339
                        // 加工前のファイルをフォーマット別に読み出す
340
                        switch ($type) {
341
                            case IMAGETYPE_JPEG:
342
                                $origin_image = imagecreatefromjpeg($image);
343
                                break;
344
345
                            case IMAGETYPE_PNG:
346
                                $origin_image = imagecreatefrompng($image);
347
                                break;
348
349
                            case IMAGETYPE_GIF:
350
                                $origin_image = imagecreatefromgif($image);
351
                                break;
352
353 18
                            default:
354
                                throw new RuntimeException('対応していないファイル形式です。: ', $type);
355 18
                        }
356 18
357 5
                        // 新しく描画するキャンバスを作成
358 5
                        $canvas = imagecreatetruecolor($origin_w, $origin_h);
359 5
                        imagecopyresampled($canvas, $origin_image, 0,0,0,0, $origin_w, $origin_h, $origin_w, $origin_h);
360
361 5
                        $filename = date('mdHis').uniqid('_').'.'.$extension;
362 5
                        $files[] = $filename;
363
364 5
                        // 保存先を指定
365 5
                        $resize_path = $this->eccubeConfig['eccube_temp_image_dir'] . '/' . $filename;
366 5
367 5
                        // 画像形式ごとの処理
368 5
                        switch ($type) {
369 5
                            case IMAGETYPE_JPEG:
370
                                imagejpeg($canvas, $resize_path, $this->eccubeConfig->get('eccube_product_image_jpg_quality'));
371 13
                                break;
372 13
373
                            case IMAGETYPE_PNG:
374
                                imagepng($canvas, $resize_path, $this->eccubeConfig->get('eccube_product_image_png_quality'));
375
                                break;
376 13
377 13
                            case IMAGETYPE_GIF:
378 11
                                imagegif($canvas, $resize_path);
379 11
                                break;
380 11
                        }
381
382
                        imagedestroy($origin_image);
383 11
                        imagedestroy($canvas);
384 11
385 11
                    } else {
386
387
                        $filename = date('mdHis').uniqid('_').'.'.$extension;
388 11
                        $image->move($this->eccubeConfig['eccube_temp_image_dir'], $filename);
389 6
                        $files[] = $filename;
390
                    }
391 11
                }
392
            }
393
        }
394
395 18
        $event = new EventArgs(
396 18
            [
397
                'images' => $images,
398
                'files' => $files,
399 18
            ],
400 2
            $request
401
        );
402
        $this->eventDispatcher->dispatch(EccubeEvents::ADMIN_PRODUCT_ADD_IMAGE_COMPLETE, $event);
403 18
        $files = $event->getArgument('files');
404
405 18
        return $this->json(['files' => $files], 200);
406 18
    }
407
408 18
    /**
409
     * @Route("/%eccube_admin_route%/product/product/new", name="admin_product_product_new")
410 18
     * @Route("/%eccube_admin_route%/product/product/{id}/edit", requirements={"id" = "\d+"}, name="admin_product_product_edit")
411
     * @Template("@admin/Product/product.twig")
412 18
     */
413
    public function edit(Request $request, $id = null, RouterInterface $router, CacheUtil $cacheUtil)
414 18
    {
415 16
        $has_class = false;
416 16
        if (is_null($id)) {
417
            $Product = new Product();
418
            $ProductClass = new ProductClass();
419
            $ProductStatus = $this->productStatusRepository->find(ProductStatus::DISPLAY_HIDE);
420 18
            $Product
421 18
                ->addProductClass($ProductClass)
422 18
                ->setStatus($ProductStatus);
0 ignored issues
show
Bug introduced by
It seems like $ProductStatus defined by $this->productStatusRepo...ctStatus::DISPLAY_HIDE) on line 419 can also be of type object; however, Eccube\Entity\Product::setStatus() does only seem to accept null|object<Eccube\Entity\Master\ProductStatus>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
423 13
            $ProductClass
424
                ->setVisible(true)
425 18
                ->setStockUnlimited(true)
426
                ->setProduct($Product);
427 18
            $ProductStock = new ProductStock();
428 18
            $ProductClass->setProductStock($ProductStock);
429 18
            $ProductStock->setProductClass($ProductClass);
430
        } else {
431 13
            $Product = $this->productRepository->find($id);
432
            $ProductClass = null;
433 18
            $ProductStock = null;
434
            if (!$Product) {
435 18
                throw new NotFoundHttpException();
436 18
            }
437
            // 規格無しの商品の場合は、デフォルト規格を表示用に取得する
438 18
            $has_class = $Product->hasProductClass();
439 13
            if (!$has_class) {
440 13
                $ProductClasses = $Product->getProductClasses();
441 13
                foreach ($ProductClasses as $pc) {
442 13
                    if (!is_null($pc->getClassCategory1())) {
443
                        continue;
444 13
                    }
445 13
                    if ($pc->isVisible()) {
446
                        $ProductClass = $pc;
447
                        break;
448 13
                    }
449 12
                }
450 8
                if ($this->BaseInfo->isOptionProductTaxRule() && $ProductClass->getTaxRule()) {
451 4
                    $ProductClass->setTaxRate($ProductClass->getTaxRule()->getTaxRate());
452
                }
453 4
                $ProductStock = $ProductClass->getProductStock();
454 4
            }
455 4
        }
456 4
457 4
        $builder = $this->formFactory
458 4
            ->createBuilder(ProductType::class, $Product);
459
460
        // 規格あり商品の場合、規格関連情報をFormから除外
461 8
        if ($has_class) {
462
            $builder->remove('class');
463 4
        }
464 2
465 2
        $event = new EventArgs(
466
            [
467
                'builder' => $builder,
468
                'Product' => $Product,
469 13
            ],
470
            $request
471
        );
472 13
        $this->eventDispatcher->dispatch(EccubeEvents::ADMIN_PRODUCT_EDIT_INITIALIZE, $event);
473
474
        $form = $builder->getForm();
475
476 13
        if (!$has_class) {
477
            $ProductClass->setStockUnlimited($ProductClass->isStockUnlimited());
478 13
            $form['class']->setData($ProductClass);
479
        }
480
481
        // ファイルの登録
482
        $images = [];
483
        $ProductImages = $Product->getProductImage();
484 13
        foreach ($ProductImages as $ProductImage) {
485 10
            $images[] = $ProductImage->getFileName();
486 10
        }
487
        $form['images']->setData($images);
488 13
489 13
        $categories = [];
490
        $ProductCategories = $Product->getProductCategories();
491 13
        foreach ($ProductCategories as $ProductCategory) {
492 13
            /* @var $ProductCategory \Eccube\Entity\ProductCategory */
493 13
            $categories[] = $ProductCategory->getCategory();
494 13
        }
495
        $form['Category']->setData($categories);
496
497
        $Tags = $Product->getTags();
498
        $form['Tag']->setData($Tags);
499
500
        if ('POST' === $request->getMethod()) {
501
            $form->handleRequest($request);
502
            if ($form->isValid()) {
503
                log_info('商品登録開始', [$id]);
504
                $Product = $form->getData();
505
506
                if (!$has_class) {
507
                    $ProductClass = $form['class']->getData();
508
509
                    // 個別消費税
510
                    if ($this->BaseInfo->isOptionProductTaxRule()) {
511
                        if ($ProductClass->getTaxRate() !== null) {
512
                            if ($ProductClass->getTaxRule()) {
513
                                $ProductClass->getTaxRule()->setTaxRate($ProductClass->getTaxRate());
514
                            } else {
515
                                $taxrule = $this->taxRuleRepository->newTaxRule();
516 13
                                $taxrule->setTaxRate($ProductClass->getTaxRate());
517 13
                                $taxrule->setApplyDate(new \DateTime());
518
                                $taxrule->setProduct($Product);
519
                                $taxrule->setProductClass($ProductClass);
520
                                $ProductClass->setTaxRule($taxrule);
521
                            }
522
523
                            $ProductClass->getTaxRule()->setTaxRate($ProductClass->getTaxRate());
524
                        } else {
525
                            if ($ProductClass->getTaxRule()) {
526
                                $this->taxRuleRepository->delete($ProductClass->getTaxRule());
527
                                $ProductClass->setTaxRule(null);
528
                            }
529
                        }
530
                    }
531
                    $this->entityManager->persist($ProductClass);
532 13
533 13
                    // 在庫情報を作成
534
                    if (!$ProductClass->isStockUnlimited()) {
535
                        $ProductStock->setStock($ProductClass->getStock());
536
                    } else {
537
                        // 在庫無制限時はnullを設定
538
                        $ProductStock->setStock(null);
539
                    }
540
                    $this->entityManager->persist($ProductStock);
541
                }
542
543
                // カテゴリの登録
544
                // 一度クリア
545
                /* @var $Product \Eccube\Entity\Product */
546
                foreach ($Product->getProductCategories() as $ProductCategory) {
547
                    $Product->removeProductCategory($ProductCategory);
548 13
                    $this->entityManager->remove($ProductCategory);
549 13
                }
550
                $this->entityManager->persist($Product);
551 13
                $this->entityManager->flush();
552 13
553
                $count = 1;
554
                $Categories = $form->get('Category')->getData();
555
                $categoriesIdList = [];
556
                foreach ($Categories as $Category) {
557 View Code Duplication
                    foreach ($Category->getPath() as $ParentCategory) {
558
                        if (!isset($categoriesIdList[$ParentCategory->getId()])) {
559
                            $ProductCategory = $this->createProductCategory($Product, $ParentCategory, $count);
560
                            $this->entityManager->persist($ProductCategory);
561
                            $count++;
562
                            /* @var $Product \Eccube\Entity\Product */
563
                            $Product->addProductCategory($ProductCategory);
564 13
                            $categoriesIdList[$ParentCategory->getId()] = true;
565
                        }
566
                    }
567
                    if (!isset($categoriesIdList[$Category->getId()])) {
568 13
                        $ProductCategory = $this->createProductCategory($Product, $Category, $count);
569 13
                        $this->entityManager->persist($ProductCategory);
570 1
                        $count++;
571 1
                        /* @var $Product \Eccube\Entity\Product */
572
                        $Product->addProductCategory($ProductCategory);
573
                        $categoriesIdList[$Category->getId()] = true;
574
                    }
575 13
                }
576 13
577 13
                // 画像の登録
578
                $add_images = $form->get('add_images')->getData();
579 13
                foreach ($add_images as $add_image) {
580 13
                    $ProductImage = new \Eccube\Entity\ProductImage();
581 13
                    $ProductImage
582 13
                        ->setFileName($add_image)
583
                        ->setProduct($Product)
584
                        ->setSortNo(1);
585 13
                    $Product->addProductImage($ProductImage);
586 13
                    $this->entityManager->persist($ProductImage);
587
588 13
                    // 移動
589
                    $file = new File($this->eccubeConfig['eccube_temp_image_dir'].'/'.$add_image);
590 13
                    $file->move($this->eccubeConfig['eccube_save_image_dir']);
591
                }
592 13
593 13
                // 画像の削除
594
                $delete_images = $form->get('delete_images')->getData();
595 13
                foreach ($delete_images as $delete_image) {
596
                    $ProductImage = $this->productImageRepository
597 13
                        ->findOneBy(['file_name' => $delete_image]);
598
599 13
                    // 追加してすぐに削除した画像は、Entityに追加されない
600
                    if ($ProductImage instanceof ProductImage) {
601 13
                        $Product->removeProductImage($ProductImage);
602 1
                        $this->entityManager->remove($ProductImage);
603
                    }
604
                    $this->entityManager->persist($Product);
605 13
                    $this->entityManager->flush();
606
607
                    if (!$this->productImageRepository->findOneBy(['file_name' => $delete_image])) {
608
                        // 削除
609
                        $fs = new Filesystem();
610 5
                        $fs->remove($this->eccubeConfig['eccube_save_image_dir'] . '/' . $delete_image);
611 5
                    }
612
                }
613 5
                $this->entityManager->persist($Product);
614
                $this->entityManager->flush();
615 5
616 5
                $sortNos = $request->get('sort_no_images');
617
                if ($sortNos) {
618 5
                    foreach ($sortNos as $sortNo) {
619
                        list($filename, $sortNo_val) = explode('//', $sortNo);
620 5
                        $ProductImage = $this->productImageRepository
621
                            ->findOneBy([
622 5
                                'file_name' => $filename,
623
                                'Product' => $Product,
624 5
                            ]);
625
                        $ProductImage->setSortNo($sortNo_val);
626
                        $this->entityManager->persist($ProductImage);
627
                    }
628
                }
629 5
                $this->entityManager->flush();
630
631
                // 商品タグの登録
632 5
                // 商品タグを一度クリア
633 5
                $ProductTags = $Product->getProductTag();
634 3
                foreach ($ProductTags as $ProductTag) {
635 5
                    $Product->removeProductTag($ProductTag);
636
                    $this->entityManager->remove($ProductTag);
637
                }
638 5
639 5
                // 商品タグの登録
640 5
                $Tags = $form->get('Tag')->getData();
641 5
                foreach ($Tags as $Tag) {
642 5
                    $ProductTag = new ProductTag();
643 5
                    $ProductTag
644 5
                        ->setProduct($Product)
645 5
                        ->setTag($Tag);
646 5
                    $Product->addProductTag($ProductTag);
647
                    $this->entityManager->persist($ProductTag);
648
                }
649
650
                $Product->setUpdateDate(new \DateTime());
651
                $this->entityManager->flush();
652
653
                log_info('商品登録完了', [$id]);
654 1
655
                $event = new EventArgs(
656 1
                    [
657 1
                        'form' => $form,
658 1
                        'Product' => $Product,
659 1
                    ],
660 1
                    $request
661 1
                );
662
                $this->eventDispatcher->dispatch(EccubeEvents::ADMIN_PRODUCT_EDIT_COMPLETE, $event);
663 1
664
                $this->addSuccess('admin.common.save_complete', 'admin');
665 1
666 1 View Code Duplication
                if ($returnLink = $form->get('return_link')->getData()) {
667
                    try {
668
                        // $returnLinkはpathの形式で渡される. pathが存在するかをルータでチェックする.
669
                        $pattern = '/^'.preg_quote($request->getBasePath(), '/').'/';
670
                        $returnLink = preg_replace($pattern, '', $returnLink);
671
                        $result = $router->match($returnLink);
0 ignored issues
show
Bug introduced by
It seems like $returnLink defined by preg_replace($pattern, '', $returnLink) on line 670 can also be of type array<integer,string>; however, Symfony\Component\Routin...tcherInterface::match() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
672
                        // パラメータのみ抽出
673
                        $params = array_filter($result, function ($key) {
674
                            return 0 !== \strpos($key, '_');
675
                        }, ARRAY_FILTER_USE_KEY);
676
677
                        // pathからurlを再構築してリダイレクト.
678
                        return $this->redirectToRoute($result['_route'], $params);
679 1
                    } catch (\Exception $e) {
680 1
                        // マッチしない場合はログ出力してスキップ.
681
                        log_warning('URLの形式が不正です。');
682 1
                    }
683 1
                }
684
685
                $cacheUtil->clearDoctrineCache();
686 1
687 1
                return $this->redirectToRoute('admin_product_product_edit', ['id' => $Product->getId()]);
688
            }
689 1
        }
690
691 1
        // 検索結果の保持
692 1
        $builder = $this->formFactory
693 1
            ->createBuilder(SearchProductType::class);
694
695 1
        $event = new EventArgs(
696
            [
697 1
                'builder' => $builder,
698 1
                'Product' => $Product,
699
            ],
700
            $request
701 1
        );
702
        $this->eventDispatcher->dispatch(EccubeEvents::ADMIN_PRODUCT_EDIT_SEARCH, $event);
703 1
704 1
        $searchForm = $builder->getForm();
705 1
706
        if ('POST' === $request->getMethod()) {
707
            $searchForm->handleRequest($request);
708
        }
709
710 1
        // Get Tags
711
        $TagsList = $this->tagRepository->getList();
712 1
713 1
        // ツリー表示のため、ルートからのカテゴリを取得
714
        $TopCategories = $this->categoryRepository->getList(null);
715
        $ChoicedCategoryIds = array_map(function ($Category) {
716 1
            return $Category->getId();
717
        }, $form->get('Category')->getData());
718
719
        return [
720 1
            'Product' => $Product,
721
            'Tags' => $Tags,
722
            'TagsList' => $TagsList,
723
            'form' => $form->createView(),
724
            'searchForm' => $searchForm->createView(),
725
            'has_class' => $has_class,
726
            'id' => $id,
727 1
            'TopCategories' => $TopCategories,
728
            'ChoicedCategoryIds' => $ChoicedCategoryIds,
729
        ];
730 1
    }
731 1
732
    /**
733
     * @Route("/%eccube_admin_route%/product/product/{id}/delete", requirements={"id" = "\d+"}, name="admin_product_product_delete", methods={"DELETE"})
734
     */
735
    public function delete(Request $request, $id = null, CacheUtil $cacheUtil)
736 1
    {
737
        $this->isTokenValid();
738 1
        $session = $request->getSession();
739
        $page_no = intval($session->get('eccube.admin.product.search.page_no'));
740
        $page_no = $page_no ? $page_no : Constant::ENABLED;
741
        $message = null;
742
        $success = false;
743
744
        if (!is_null($id)) {
745
            /* @var $Product \Eccube\Entity\Product */
746 1
            $Product = $this->productRepository->find($id);
747 View Code Duplication
            if (!$Product) {
748 1
                if ($request->isXmlHttpRequest()) {
749
                    $message = trans('admin.common.delete_error_already_deleted');
750 1
751 1
                    return $this->json(['success' => $success, 'message' => $message]);
752 1
                } else {
753 1
                    $this->deleteMessage();
754 1
                    $rUrl = $this->generateUrl('admin_product_page', ['page_no' => $page_no]).'?resume='.Constant::ENABLED;
755 1
756 1
                    return $this->redirect($rUrl);
757
                }
758 1
            }
759 1
760 1
            if ($Product instanceof Product) {
761
                log_info('商品削除開始', [$id]);
762
763
                $deleteImages = $Product->getProductImage();
764 1
                $ProductClasses = $Product->getProductClasses();
765 1
766 1
                try {
767
                    $this->productRepository->delete($Product);
768
                    $this->entityManager->flush();
769 1
770
                    $event = new EventArgs(
771 1
                        [
772 1
                            'Product' => $Product,
773 1
                            'ProductClass' => $ProductClasses,
774
                            'deleteImages' => $deleteImages,
775
                        ],
776 1
                        $request
777 1
                    );
778 1
                    $this->eventDispatcher->dispatch(EccubeEvents::ADMIN_PRODUCT_DELETE_COMPLETE, $event);
779 1
                    $deleteImages = $event->getArgument('deleteImages');
780 1
781 1
                    // 画像ファイルの削除(commit後に削除させる)
782
                    /** @var ProductImage $deleteImage */
783 1 View Code Duplication
                    foreach ($deleteImages as $deleteImage) {
784 1
                        if ($this->productImageRepository->findOneBy(['file_name' => $deleteImage->getFileName()])) {
785
                            continue;
786
                        }
787
                        try {
788
                            $fs = new Filesystem();
789
                            $fs->remove($this->eccubeConfig['eccube_save_image_dir'].'/'.$deleteImage);
790 1
                        } catch (\Exception $e) {
791
                            // エラーが発生しても無視する
792 1
                        }
793 1
                    }
794
795 1
                    log_info('商品削除完了', [$id]);
796 1
797
                    $success = true;
798 1
                    $message = trans('admin.common.delete_complete');
799 1
800 1
                    $cacheUtil->clearDoctrineCache();
801
                } catch (ForeignKeyConstraintViolationException $e) {
802
                    log_info('商品削除エラー', [$id]);
803 1
                    $message = trans('admin.common.delete_error_foreign_key', ['%name%' => $Product->getName()]);
804
                }
805 1
            } else {
806
                log_info('商品削除エラー', [$id]);
807 1
                $message = trans('admin.common.delete_error');
808 1
            }
809
        } else {
810
            log_info('商品削除エラー', [$id]);
811
            $message = trans('admin.common.delete_error');
812 1
        }
813
814 1 View Code Duplication
        if ($request->isXmlHttpRequest()) {
815
            return $this->json(['success' => $success, 'message' => $message]);
816 1
        } else {
817
            if ($success) {
818 1
                $this->addSuccess($message, 'admin');
819 1
            } else {
820 1
                $this->addError($message, 'admin');
821 1
            }
822 1
823 1
            $rUrl = $this->generateUrl('admin_product_page', ['page_no' => $page_no]).'?resume='.Constant::ENABLED;
824
825 1
            return $this->redirect($rUrl);
826
        }
827 1
    }
828
829 1
    /**
830
     * @Route("/%eccube_admin_route%/product/product/{id}/copy", requirements={"id" = "\d+"}, name="admin_product_product_copy", methods={"POST"})
831 1
     */
832
    public function copy(Request $request, $id = null)
833
    {
834
        $this->isTokenValid();
835
836
        if (!is_null($id)) {
837
            $Product = $this->productRepository->find($id);
838
            if ($Product instanceof Product) {
839
                $CopyProduct = clone $Product;
840
                $CopyProduct->copy();
841
                $ProductStatus = $this->productStatusRepository->find(ProductStatus::DISPLAY_HIDE);
842
                $CopyProduct->setStatus($ProductStatus);
0 ignored issues
show
Bug introduced by
It seems like $ProductStatus defined by $this->productStatusRepo...ctStatus::DISPLAY_HIDE) on line 841 can also be of type object; however, Eccube\Entity\Product::setStatus() does only seem to accept null|object<Eccube\Entity\Master\ProductStatus>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
843
844
                $CopyProductCategories = $CopyProduct->getProductCategories();
845
                foreach ($CopyProductCategories as $Category) {
846
                    $this->entityManager->persist($Category);
847
                }
848
849
                // 規格あり商品の場合は, デフォルトの商品規格を取得し登録する.
850
                if ($CopyProduct->hasProductClass()) {
851
                    $dummyClass = $this->productClassRepository->findOneBy([
852
                        'visible' => false,
853
                        'ClassCategory1' => null,
854
                        'ClassCategory2' => null,
855
                        'Product' => $Product,
856
                    ]);
857
                    $dummyClass = clone $dummyClass;
858
                    $dummyClass->setProduct($CopyProduct);
859
                    $CopyProduct->addProductClass($dummyClass);
860
                }
861
862
                $CopyProductClasses = $CopyProduct->getProductClasses();
863
                foreach ($CopyProductClasses as $Class) {
864
                    $Stock = $Class->getProductStock();
865
                    $CopyStock = clone $Stock;
866
                    $CopyStock->setProductClass($Class);
867
                    $this->entityManager->persist($CopyStock);
868
869
                    $TaxRule = $Class->getTaxRule();
870
                    if ($TaxRule) {
871
                        $CopyTaxRule = clone $TaxRule;
872
                        $CopyTaxRule->setProductClass($Class);
873
                        $CopyTaxRule->setProduct($CopyProduct);
874
                        $this->entityManager->persist($CopyTaxRule);
875
                    }
876
                    $this->entityManager->persist($Class);
877
                }
878
                $Images = $CopyProduct->getProductImage();
879
                foreach ($Images as $Image) {
880
                    // 画像ファイルを新規作成
881
                    $extension = pathinfo($Image->getFileName(), PATHINFO_EXTENSION);
882
                    $filename = date('mdHis').uniqid('_').'.'.$extension;
883
                    try {
884
                        $fs = new Filesystem();
885
                        $fs->copy($this->eccubeConfig['eccube_save_image_dir'].'/'.$Image->getFileName(), $this->eccubeConfig['eccube_save_image_dir'].'/'.$filename);
886
                    } catch (\Exception $e) {
887
                        // エラーが発生しても無視する
888
                    }
889
                    $Image->setFileName($filename);
890
891
                    $this->entityManager->persist($Image);
892
                }
893
                $Tags = $CopyProduct->getProductTag();
894
                foreach ($Tags as $Tag) {
895
                    $this->entityManager->persist($Tag);
896
                }
897
898
                $this->entityManager->persist($CopyProduct);
899
900
                $this->entityManager->flush();
901
902
                $event = new EventArgs(
903
                    [
904
                        'Product' => $Product,
905
                        'CopyProduct' => $CopyProduct,
906
                        'CopyProductCategories' => $CopyProductCategories,
907
                        'CopyProductClasses' => $CopyProductClasses,
908
                        'images' => $Images,
909
                        'Tags' => $Tags,
910
                    ],
911
                    $request
912
                );
913
                $this->eventDispatcher->dispatch(EccubeEvents::ADMIN_PRODUCT_COPY_COMPLETE, $event);
914
915
                $this->addSuccess('admin.product.copy_complete', 'admin');
916
917
                return $this->redirectToRoute('admin_product_product_edit', ['id' => $CopyProduct->getId()]);
918
            } else {
919
                $this->addError('admin.product.copy_error', 'admin');
920
            }
921
        } else {
922
            $msg = trans('admin.product.copy_error');
923
            $this->addError($msg, 'admin');
924
        }
925
926
        return $this->redirectToRoute('admin_product');
927
    }
928
929
    /**
930
     * @Route("/%eccube_admin_route%/product/product/{id}/display", requirements={"id" = "\d+"}, name="admin_product_product_display")
931
     */
932
    public function display(Request $request, $id = null)
933
    {
934
        $event = new EventArgs(
935
            [],
936
            $request
937
        );
938
        $this->eventDispatcher->dispatch(EccubeEvents::ADMIN_PRODUCT_DISPLAY_COMPLETE, $event);
939
940
        if (!is_null($id)) {
941
            return $this->redirectToRoute('product_detail', ['id' => $id, 'admin' => '1']);
942
        }
943
944
        return $this->redirectToRoute('admin_product');
945
    }
946
947
    /**
948
     * 商品CSVの出力.
949
     *
950
     * @Route("/%eccube_admin_route%/product/export", name="admin_product_export")
951
     *
952
     * @param Request $request
953
     *
954
     * @return StreamedResponse
955
     */
956
    public function export(Request $request)
957
    {
958
        // タイムアウトを無効にする.
959
        set_time_limit(0);
960
961
        // sql loggerを無効にする.
962
        $em = $this->entityManager;
963
        $em->getConfiguration()->setSQLLogger(null);
964
965
        $response = new StreamedResponse();
966
        $response->setCallback(function () use ($request) {
967
            // CSV種別を元に初期化.
968
            $this->csvExportService->initCsvType(CsvType::CSV_TYPE_PRODUCT);
969
970
            // ヘッダ行の出力.
971
            $this->csvExportService->exportHeader();
972
973
            // 商品データ検索用のクエリビルダを取得.
974
            $qb = $this->csvExportService
975
                ->getProductQueryBuilder($request);
976
977
            // Get stock status
978
            $isOutOfStock = 0;
979
            $session = $request->getSession();
980
            if ($session->has('eccube.admin.product.search')) {
981
                $searchData = $session->get('eccube.admin.product.search', []);
982
                if (isset($searchData['stock_status']) && $searchData['stock_status'] === 0) {
983
                    $isOutOfStock = 1;
984
                }
985
            }
986
987
            // joinする場合はiterateが使えないため, select句をdistinctする.
988
            // http://qiita.com/suin/items/2b1e98105fa3ef89beb7
989
            // distinctのmysqlとpgsqlの挙動をあわせる.
990
            // http://uedatakeshi.blogspot.jp/2010/04/distinct-oeder-by-postgresmysql.html
991
            $qb->resetDQLPart('select')
992
                ->resetDQLPart('orderBy')
993
                ->orderBy('p.update_date', 'DESC');
994
995
            if ($isOutOfStock) {
996
                $qb->select('p, pc')
997
                    ->distinct();
998
            } else {
999
                $qb->select('p')
1000
                    ->distinct();
1001
            }
1002
            // データ行の出力.
1003
            $this->csvExportService->setExportQueryBuilder($qb);
1004 1
1005
            $this->csvExportService->exportData(function ($entity, CsvExportService $csvService) use ($request) {
1006 1
                $Csvs = $csvService->getCsvs();
1007
1008
                /** @var $Product \Eccube\Entity\Product */
1009 1
                $Product = $entity;
1010 1
1011 1
                /** @var $ProductClasses \Eccube\Entity\ProductClass[] */
1012
                $ProductClasses = $Product->getProductClasses();
1013 1
1014 1
                foreach ($ProductClasses as $ProductClass) {
1015 1
                    $ExportCsvRow = new ExportCsvRow();
1016
1017 1
                    // CSV出力項目と合致するデータを取得.
1018
                    foreach ($Csvs as $Csv) {
1019
                        // 商品データを検索.
1020
                        $ExportCsvRow->setData($csvService->getData($Csv, $Product));
1021 1
                        if ($ExportCsvRow->isDataNull()) {
1022 1
                            // 商品規格情報を検索.
1023 1
                            $ExportCsvRow->setData($csvService->getData($Csv, $ProductClass));
1024 1
                        }
1025 1
1026
                        $event = new EventArgs(
1027 1
                            [
1028
                                'csvService' => $csvService,
1029
                                'Csv' => $Csv,
1030
                                'ProductClass' => $ProductClass,
1031
                                'ExportCsvRow' => $ExportCsvRow,
1032
                            ],
1033 1
                            $request
1034
                        );
1035
                        $this->eventDispatcher->dispatch(EccubeEvents::ADMIN_PRODUCT_CSV_EXPORT, $event);
1036
1037
                        $ExportCsvRow->pushData();
1038
                    }
1039
1040
                    // $row[] = number_format(memory_get_usage(true));
1041
                    // 出力.
1042
                    $csvService->fputcsv($ExportCsvRow->getRow());
1043
                }
1044
            });
1045
        });
1046
1047
        $now = new \DateTime();
1048
        $filename = 'product_'.$now->format('YmdHis').'.csv';
1049
        $response->headers->set('Content-Type', 'application/octet-stream');
1050
        $response->headers->set('Content-Disposition', 'attachment; filename='.$filename);
1051
        $response->send();
1052
1053
        log_info('商品CSV出力ファイル名', [$filename]);
1054
1055
        return $response;
1056
    }
1057
1058
    /**
1059
     * ProductCategory作成
1060
     *
1061
     * @param \Eccube\Entity\Product $Product
1062
     * @param \Eccube\Entity\Category $Category
1063
     * @param integer $count
1064
     *
1065
     * @return \Eccube\Entity\ProductCategory
1066
     */
1067 View Code Duplication
    private function createProductCategory($Product, $Category, $count)
0 ignored issues
show
Unused Code introduced by
The parameter $count is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1068
    {
1069
        $ProductCategory = new ProductCategory();
1070
        $ProductCategory->setProduct($Product);
1071
        $ProductCategory->setProductId($Product->getId());
1072
        $ProductCategory->setCategory($Category);
1073
        $ProductCategory->setCategoryId($Category->getId());
1074
1075
        return $ProductCategory;
1076
    }
1077
1078
    /**
1079
     * Bulk public action
1080
     *
1081
     * @Route("/%eccube_admin_route%/product/bulk/product-status/{id}", requirements={"id" = "\d+"}, name="admin_product_bulk_product_status", methods={"POST"})
1082
     *
1083
     * @param Request $request
1084
     * @param ProductStatus $ProductStatus
1085
     *
1086
     * @return RedirectResponse
1087
     */
1088
    public function bulkProductStatus(Request $request, ProductStatus $ProductStatus, CacheUtil $cacheUtil)
1089
    {
1090
        $this->isTokenValid();
1091
1092
        /** @var Product[] $Products */
1093
        $Products = $this->productRepository->findBy(['id' => $request->get('ids')]);
1094
        $count = 0;
1095
        foreach ($Products as $Product) {
1096
            try {
1097
                $Product->setStatus($ProductStatus);
1098
                $this->productRepository->save($Product);
1099
                $count++;
1100
            } catch (\Exception $e) {
1101
                $this->addError($e->getMessage(), 'admin');
1102
            }
1103
        }
1104
        try {
1105
            if ($count) {
1106
                $this->entityManager->flush();
1107
                $msg = $this->translator->trans('admin.product.bulk_change_status_complete', [
1108
                    '%count%' => $count,
1109
                    '%status%' => $ProductStatus->getName(),
1110
                ]);
1111
                $this->addSuccess($msg, 'admin');
1112
                $cacheUtil->clearDoctrineCache();
1113
            }
1114
        } catch (\Exception $e) {
1115
            $this->addError($e->getMessage(), 'admin');
1116
        }
1117
1118
        return $this->redirectToRoute('admin_product', ['resume' => Constant::ENABLED]);
1119
    }
1120
}
1121