Failed Conditions
Pull Request — 4.0 (#4915)
by
unknown
05:05
created

ProductController::addCart()   D

Complexity

Conditions 15
Paths 74

Size

Total Lines 130

Duplication

Lines 13
Ratio 10 %

Code Coverage

Tests 45
CRAP Score 17.5322

Importance

Changes 0
Metric Value
cc 15
nc 74
nop 2
dl 13
loc 130
ccs 45
cts 58
cp 0.7759
crap 17.5322
rs 4.7333
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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;
15
16
use Eccube\Entity\BaseInfo;
17
use Eccube\Entity\Master\ProductStatus;
18
use Eccube\Entity\Product;
19
use Eccube\Event\EccubeEvents;
20
use Eccube\Event\EventArgs;
21
use Eccube\Exception\CartException;
22
use Eccube\Form\Type\AddCartType;
23
use Eccube\Form\Type\Master\ProductListMaxType;
24
use Eccube\Form\Type\Master\ProductListOrderByType;
25
use Eccube\Form\Type\SearchProductType;
26
use Eccube\Repository\BaseInfoRepository;
27
use Eccube\Repository\CustomerFavoriteProductRepository;
28
use Eccube\Repository\Master\ProductListMaxRepository;
29
use Eccube\Repository\ProductRepository;
30
use Eccube\Service\CartService;
31
use Eccube\Service\PurchaseFlow\PurchaseContext;
32
use Eccube\Service\PurchaseFlow\PurchaseFlow;
33
use Knp\Bundle\PaginatorBundle\Pagination\SlidingPagination;
34
use Knp\Component\Pager\Paginator;
35
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
36
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
37
use Symfony\Component\HttpFoundation\Request;
38
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
39
use Symfony\Component\Routing\Annotation\Route;
40
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
41
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
42
43
class ProductController extends AbstractController
44
{
45
    /**
46
     * @var PurchaseFlow
47
     */
48
    protected $purchaseFlow;
49
50
    /**
51
     * @var CustomerFavoriteProductRepository
52
     */
53
    protected $customerFavoriteProductRepository;
54
55
    /**
56
     * @var CartService
57
     */
58
    protected $cartService;
59
60
    /**
61
     * @var ProductRepository
62
     */
63
    protected $productRepository;
64
65
    /**
66
     * @var BaseInfo
67
     */
68
    protected $BaseInfo;
69
70
    /**
71
     * @var AuthenticationUtils
72
     */
73
    protected $helper;
74
75
    /**
76
     * @var ProductListMaxRepository
77
     */
78
    protected $productListMaxRepository;
79
80
    private $title = '';
81
82
    /**
83
     * ProductController constructor.
84 46
     *
85
     * @param PurchaseFlow $cartPurchaseFlow
86
     * @param CustomerFavoriteProductRepository $customerFavoriteProductRepository
87
     * @param CartService $cartService
88
     * @param ProductRepository $productRepository
89
     * @param BaseInfoRepository $baseInfoRepository
90
     * @param AuthenticationUtils $helper
91
     * @param ProductListMaxRepository $productListMaxRepository
92 46
     */
93 46
    public function __construct(
94 46
        PurchaseFlow $cartPurchaseFlow,
95 46
        CustomerFavoriteProductRepository $customerFavoriteProductRepository,
96 46
        CartService $cartService,
97 46
        ProductRepository $productRepository,
98
        BaseInfoRepository $baseInfoRepository,
99
        AuthenticationUtils $helper,
100
        ProductListMaxRepository $productListMaxRepository
101
    ) {
102
        $this->purchaseFlow = $cartPurchaseFlow;
103
        $this->customerFavoriteProductRepository = $customerFavoriteProductRepository;
104
        $this->cartService = $cartService;
105
        $this->productRepository = $productRepository;
106 3
        $this->BaseInfo = $baseInfoRepository->get();
107
        $this->helper = $helper;
108
        $this->productListMaxRepository = $productListMaxRepository;
109
    }
110
111
    /**
112
     * 商品一覧画面.
113
     *
114 3
     * @Route("/products/list", name="product_list")
115 3
     * @Template("Product/list.twig")
116
     */
117
    public function index(Request $request, Paginator $paginator)
118
    {
119
        // Doctrine SQLFilter
120 3
        if ($this->BaseInfo->isOptionNostockHidden()) {
121 3
            $this->entityManager->getFilters()->enable('option_nostock_hidden');
122 3
        }
123 3
124 3
        // handleRequestは空のqueryの場合は無視するため
125
        if ($request->getMethod() === 'GET') {
126
            $request->query->set('pageno', $request->query->get('pageno', ''));
127 3
        }
128
129 3
        // searchForm
130
        /* @var $builder \Symfony\Component\Form\FormBuilderInterface */
131 3
        $builder = $this->formFactory->createNamedBuilder('', SearchProductType::class);
132
133 3
        if ($request->getMethod() === 'GET') {
134
            $builder->setMethod('GET');
135
        }
136 3
137
        $event = new EventArgs(
138 3
            [
139
                'builder' => $builder,
140
            ],
141 3
            $request
142 3
        );
143
        $this->eventDispatcher->dispatch(EccubeEvents::FRONT_PRODUCT_INDEX_INITIALIZE, $event);
144 3
145
        /* @var $searchForm \Symfony\Component\Form\FormInterface */
146 3
        $searchForm = $builder->getForm();
147 3
148
        $searchForm->handleRequest($request);
149 3
150
        // paginator
151 3
        $searchData = $searchForm->getData();
152 3
        $qb = $this->productRepository->getQueryBuilderBySearchData($searchData);
153
154 3
        $event = new EventArgs(
155 3
            [
156 3
                'searchData' => $searchData,
157 3
                'qb' => $qb,
158
            ],
159
            $request
160
        );
161 3
        $this->eventDispatcher->dispatch(EccubeEvents::FRONT_PRODUCT_INDEX_SEARCH, $event);
162 3
        $searchData = $event->getArgument('searchData');
163
164 3
        $query = $qb->getQuery()
0 ignored issues
show
Deprecated Code introduced by
The method Doctrine\ORM\AbstractQuery::useResultCache() has been deprecated with message: 2.7 Use {@see enableResultCache} and {@see disableResultCache} instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
165 3
            ->useResultCache(true, $this->eccubeConfig['eccube_result_cache_lifetime_short']);
166 3
167 3
        /** @var SlidingPagination $pagination */
168
        $pagination = $paginator->paginate(
169 3
            $query,
170
            !empty($searchData['pageno']) ? $searchData['pageno'] : 1,
171
            !empty($searchData['disp_number']) ? $searchData['disp_number']->getId() : $this->productListMaxRepository->findOneBy([], ['sort_no' => 'ASC'])->getId()
172
        );
173 3
174
        $ids = [];
175 3
        foreach ($pagination as $Product) {
176
            $ids[] = $Product->getId();
177
        }
178
        $ProductsAndClassCategories = $this->productRepository->findProductsWithSortedClassCategories($ids, 'p.id');
179 3
180 3
        // addCart form
181 3
        $forms = [];
182 3 View Code Duplication
        foreach ($pagination as $Product) {
183
            /* @var $builder \Symfony\Component\Form\FormBuilderInterface */
184 3
            $builder = $this->formFactory->createNamedBuilder(
185 3
                '',
186
                AddCartType::class,
187
                null,
188
                [
189 3
                    'product' => $ProductsAndClassCategories[$Product->getId()],
190 3
                    'allow_extra_fields' => true,
191
                ]
192
            );
193 3
            $addCartForm = $builder->getForm();
194
195 3
            $forms[$Product->getId()] = $addCartForm->createView();
196
        }
197 3
198
        // 表示件数
199 3
        $builder = $this->formFactory->createNamedBuilder(
200
            'disp_number',
201 3
            ProductListMaxType::class,
202
            null,
203 3
            [
204
                'required' => false,
205
                'allow_extra_fields' => true,
206 3
            ]
207 3
        );
208 3
        if ($request->getMethod() === 'GET') {
209 3
            $builder->setMethod('GET');
210
        }
211 3
212 3
        $event = new EventArgs(
213
            [
214
                'builder' => $builder,
215
            ],
216 3
            $request
217 3
        );
218
        $this->eventDispatcher->dispatch(EccubeEvents::FRONT_PRODUCT_INDEX_DISP, $event);
219
220 3
        $dispNumberForm = $builder->getForm();
221
222 3
        $dispNumberForm->handleRequest($request);
223
224 3
        // ソート順
225
        $builder = $this->formFactory->createNamedBuilder(
226 3
            'orderby',
227
            ProductListOrderByType::class,
228 3
            null,
229
            [
230 3
                'required' => false,
231
                'allow_extra_fields' => true,
232 3
            ]
233
        );
234
        if ($request->getMethod() === 'GET') {
235 3
            $builder->setMethod('GET');
236 3
        }
237 3
238 3
        $event = new EventArgs(
239 3
            [
240 3
                'builder' => $builder,
241 3
            ],
242
            $request
243
        );
244
        $this->eventDispatcher->dispatch(EccubeEvents::FRONT_PRODUCT_INDEX_ORDER, $event);
245
246
        $orderByForm = $builder->getForm();
247
248
        $orderByForm->handleRequest($request);
249
250
        $Category = $searchForm->get('category_id')->getData();
251
252
        return [
253
            'subtitle' => $this->getPageTitle($searchData),
254
            'pagination' => $pagination,
255
            'search_form' => $searchForm->createView(),
256
            'disp_number_form' => $dispNumberForm->createView(),
257
            'order_by_form' => $orderByForm->createView(),
258 11
            'forms' => $forms,
259
            'Category' => $Category,
260 11
        ];
261
    }
262
263
    /**
264 11
     * 商品詳細画面.
265 11
     *
266 11
     * @Route("/products/detail/{id}", name="product_detail", methods={"GET"}, requirements={"id" = "\d+"})
267 11
     * @Template("Product/detail.twig")
268
     * @ParamConverter("Product", options={"repository_method" = "findWithSortedClassCategories"})
269 11
     *
270
     * @param Request $request
271
     * @param Product $Product
272
     *
273
     * @return array
274 11
     */
275
    public function detail(Request $request, Product $Product)
276 11
    {
277 11
        if (!$this->checkVisibility($Product)) {
278
            throw new NotFoundHttpException();
279 11
        }
280
281 11
        $builder = $this->formFactory->createNamedBuilder(
282
            '',
283 11
            AddCartType::class,
284 11
            null,
285 2
            [
286 2
                'product' => $Product,
287
                'id_add_product_id' => false,
288
            ]
289
        );
290 11
291 11
        $event = new EventArgs(
292 11
            [
293 11
                'builder' => $builder,
294 11
                'Product' => $Product,
295
            ],
296
            $request
297
        );
298
        $this->eventDispatcher->dispatch(EccubeEvents::FRONT_PRODUCT_DETAIL_INITIALIZE, $event);
299
300
        $is_favorite = false;
301
        if ($this->isGranted('ROLE_USER')) {
302
            $Customer = $this->getUser();
303 3
            $is_favorite = $this->customerFavoriteProductRepository->isFavorite($Customer, $Product);
0 ignored issues
show
Documentation introduced by
$Customer is of type null|object, but the function expects a object<Eccube\Entity\Customer>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
304
        }
305 3
306
        return [
307 3
            'title' => $this->title,
308
            'subtitle' => $Product->getName(),
309 3
            'form' => $builder->getForm()->createView(),
310
            'Product' => $Product,
311 3
            'is_favorite' => $is_favorite,
312
        ];
313 3
    }
314
315 3
    /**
316 2
     * お気に入り追加.
317 2
     *
318 2
     * @Route("/products/add_favorite/{id}", name="product_add_favorite", requirements={"id" = "\d+"})
319
     */
320 2
    public function addFavorite(Request $request, Product $Product)
321
    {
322 2
        $this->checkVisibility($Product);
323
324 2
        $event = new EventArgs(
325
            [
326 2
                'Product' => $Product,
327
            ],
328 2
            $request
329
        );
330
        $this->eventDispatcher->dispatch(EccubeEvents::FRONT_PRODUCT_FAVORITE_ADD_INITIALIZE, $event);
331
332 1
        if ($this->isGranted('ROLE_USER')) {
333 1
            $Customer = $this->getUser();
334
            $this->customerFavoriteProductRepository->addFavorite($Customer, $Product);
0 ignored issues
show
Documentation introduced by
$Customer is of type null|object, but the function expects a object<Eccube\Entity\Customer>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
335 1
            $this->session->getFlashBag()->set('product_detail.just_added_favorite', $Product->getId());
336
337 1
            $event = new EventArgs(
338
                [
339 1
                    'Product' => $Product,
340
                ],
341 1
                $request
342
            );
343 1
            $this->eventDispatcher->dispatch(EccubeEvents::FRONT_PRODUCT_FAVORITE_ADD_COMPLETE, $event);
344
345
            return $this->redirectToRoute('product_detail', ['id' => $Product->getId()]);
346
        } else {
347
            // 非会員の場合、ログイン画面を表示
348
            //  ログイン後の画面遷移先を設定
349
            $this->setLoginTargetPath($this->generateUrl('product_add_favorite', ['id' => $Product->getId()], UrlGeneratorInterface::ABSOLUTE_URL));
350
            $this->session->getFlashBag()->set('eccube.add.favorite', true);
0 ignored issues
show
Documentation introduced by
true is of type boolean, but the function expects a string|array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
351
352
            $event = new EventArgs(
353 37
                [
354
                    'Product' => $Product,
355
                ],
356 37
                $request
357 37
            );
358 1
            $this->eventDispatcher->dispatch(EccubeEvents::FRONT_PRODUCT_FAVORITE_ADD_COMPLETE, $event);
359
360
            return $this->redirectToRoute('mypage_login');
361 36
        }
362 36
    }
363 36
364 36
    /**
365
     * カートに追加.
366 36
     *
367
     * @Route("/products/add_cart/{id}", name="product_add_cart", methods={"POST"}, requirements={"id" = "\d+"})
368
     */
369
    public function addCart(Request $request, Product $Product)
370
    {
371 36
        // エラーメッセージの配列
372
        $errorMessages = [];
373 36
        if (!$this->checkVisibility($Product)) {
374 36
            throw new NotFoundHttpException();
375
        }
376 36
377
        $builder = $this->formFactory->createNamedBuilder(
378 36
            '',
379
            AddCartType::class,
380
            null,
381 36
            [
382 36
                'product' => $Product,
383
                'id_add_product_id' => false,
384 36
            ]
385 1
        );
386
387
        $event = new EventArgs(
388 35
            [
389
                'builder' => $builder,
390 35
                'Product' => $Product,
391 35
            ],
392
            $request
393 35
        );
394 35
        $this->eventDispatcher->dispatch(EccubeEvents::FRONT_PRODUCT_CART_ADD_INITIALIZE, $event);
395 35
396
        /* @var $form \Symfony\Component\Form\FormInterface */
397
        $form = $builder->getForm();
398
        $form->handleRequest($request);
399
400 35
        if (!$form->isValid()) {
401
            throw new NotFoundHttpException();
402
        }
403 35
404 35
        $addCartData = $form->getData();
405 35
406
        log_info(
407 35
            'カート追加処理開始',
408
            [
409
                'product_id' => $Product->getId(),
410
                'product_class_id' => $addCartData['product_class_id'],
411
                'quantity' => $addCartData['quantity'],
412
            ]
413 35
        );
414 35
415
        // カートへ追加
416
        $this->cartService->addProduct($addCartData['product_class_id'], $addCartData['quantity']);
417
418 35
        // 明細の正規化
419
        $Carts = $this->cartService->getCarts();
420 35 View Code Duplication
        foreach ($Carts as $Cart) {
421 35
            $result = $this->purchaseFlow->validate($Cart, new PurchaseContext($Cart, $this->getUser()));
422
            // 復旧不可のエラーが発生した場合は追加した明細を削除.
423 35
            if ($result->hasError()) {
424 35
                $this->cartService->removeProduct($addCartData['product_class_id']);
425 35
                foreach ($result->getErrors() as $error) {
426
                    $errorMessages[] = $error->getMessage();
427
                }
428
            }
429 35
            foreach ($result->getWarning() as $warning) {
430
                $errorMessages[] = $warning->getMessage();
431 35
            }
432 35
        }
433
        // エラーが発生していた場合は再度正規化をする
434 35
        if (!empty($errorMessages)) {
435
            $Carts = $this->cartService->getCarts();
436 35
            foreach ($Carts as $Cart) {
437
                $retryResult = $this->purchaseFlow->validate($Cart, new PurchaseContext($Cart, $this->getUser()));
438 35
                if ($retryResult->hasError()) {
439
                    // 明細を削除してもエラーが出る場合はリクエストを失敗させる
440
                    foreach ($retryResult->getErrors() as $error) {
441
                        $errorMessages[] = $error->getMessage();
442 35
                    }
443
                    throw new CartException(implode(",", $errorMessages));
444
                }
445
            }
446
        }
447
448
        $this->cartService->save();
449
450
        log_info(
451
            'カート追加処理完了',
452
            [
453
                'product_id' => $Product->getId(),
454
                'product_class_id' => $addCartData['product_class_id'],
455
                'quantity' => $addCartData['quantity'],
456
            ]
457
        );
458
459
        $event = new EventArgs(
460
            [
461
                'form' => $form,
462 35
                'Product' => $Product,
463 5
            ],
464
            $request
465
        );
466 35
        $this->eventDispatcher->dispatch(EccubeEvents::FRONT_PRODUCT_CART_ADD_COMPLETE, $event);
467
468
        if ($event->getResponse() !== null) {
469
            return $event->getResponse();
470
        }
471
472
        if ($request->isXmlHttpRequest()) {
473
            // ajaxでのリクエストの場合は結果をjson形式で返す。
474
475
            // 初期化
476
            $done = null;
0 ignored issues
show
Unused Code introduced by
$done is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
477 3
            $messages = [];
478
479 3
            if (empty($errorMessages)) {
480
                // エラーが発生していない場合
481 3
                $done = true;
482 1
                array_push($messages, trans('front.product.add_cart_complete'));
483
            } else {
484 2
                // エラーが発生している場合
485
                $done = false;
486
                $messages = $errorMessages;
487
            }
488
489
            return $this->json(['done' => $done, 'messages' => $messages]);
490
        } else {
491
            // ajax以外でのリクエストの場合はカート画面へリダイレクト
492
            foreach ($errorMessages as $errorMessage) {
493
                $this->addRequestError($errorMessage);
494
            }
495 43
496
            return $this->redirectToRoute('cart');
497 43
        }
498
    }
499
500 43
    /**
501
     * ページタイトルの設定
502
     *
503
     * @param  null|array $searchData
504
     *
505
     * @return str
506
     */
507
    protected function getPageTitle($searchData)
508 43
    {
509 1
        if (isset($searchData['name']) && !empty($searchData['name'])) {
510
            return trans('front.product.search_result');
511
        } elseif (isset($searchData['category_id']) && $searchData['category_id']) {
512
            return $searchData['category_id']->getName();
513 43
        } else {
514
            return trans('front.product.all_products');
515
        }
516
    }
517
518
    /**
519
     * 閲覧可能な商品かどうかを判定
520
     *
521
     * @param Product $Product
522
     *
523
     * @return boolean 閲覧可能な場合はtrue
524
     */
525
    protected function checkVisibility(Product $Product)
526
    {
527
        $is_admin = $this->session->has('_security_admin');
528
529
        // 管理ユーザの場合はステータスやオプションにかかわらず閲覧可能.
530
        if (!$is_admin) {
531
            // 在庫なし商品の非表示オプションが有効な場合.
532
            // if ($this->BaseInfo->isOptionNostockHidden()) {
533
            //     if (!$Product->getStockFind()) {
534
            //         return false;
535
            //     }
536
            // }
537
            // 公開ステータスでない商品は表示しない.
538
            if ($Product->getStatus()->getId() !== ProductStatus::DISPLAY_SHOW) {
539
                return false;
540
            }
541
        }
542
543
        return true;
544
    }
545
}
546