Failed Conditions
Pull Request — experimental/3.1 (#2633)
by
unknown
25:04
created

ProductController::addCart()   D

Complexity

Conditions 10
Paths 27

Size

Total Lines 119
Code Lines 67

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 17
CRAP Score 29.0109

Importance

Changes 0
Metric Value
cc 10
eloc 67
nc 27
nop 3
dl 0
loc 119
ccs 17
cts 40
cp 0.425
crap 29.0109
rs 4.8196
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
 * This file is part of EC-CUBE
4
 *
5
 * Copyright(c) 2000-2015 LOCKON CO.,LTD. All Rights Reserved.
6
 *
7
 * http://www.lockon.co.jp/
8
 *
9
 * This program is free software; you can redistribute it and/or
10
 * modify it under the terms of the GNU General Public License
11
 * as published by the Free Software Foundation; either version 2
12
 * of the License, or (at your option) any later version.
13
 *
14
 * This program is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 * GNU General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU General Public License
20
 * along with this program; if not, write to the Free Software
21
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22
 */
23
24
25
namespace Eccube\Controller;
26
27
use Doctrine\ORM\EntityManager;
28
use Eccube\Annotation\Inject;
29
use Eccube\Application;
30
use Eccube\Entity\BaseInfo;
31
use Eccube\Entity\Master\ProductStatus;
32
use Eccube\Entity\Product;
33
use Eccube\Event\EccubeEvents;
34
use Eccube\Event\EventArgs;
35
use Eccube\Exception\CartException;
36
use Eccube\Form\Type\AddCartType;
37
use Eccube\Form\Type\Master\ProductListMaxType;
38
use Eccube\Form\Type\Master\ProductListOrderByType;
39
use Eccube\Form\Type\SearchProductType;
40
use Eccube\Repository\CustomerFavoriteProductRepository;
41
use Eccube\Repository\ProductRepository;
42
use Eccube\Service\CartService;
43
use Eccube\Service\PurchaseFlow\PurchaseFlow;
44
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
45
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
46
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
47
use Symfony\Component\EventDispatcher\EventDispatcher;
48
use Symfony\Component\Form\FormFactory;
49
use Symfony\Component\HttpFoundation\JsonResponse;
50
use Symfony\Component\HttpFoundation\Request;
51
use Symfony\Component\HttpFoundation\Session\Session;
52
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
53
54
/**
55
 * @Route(service=ProductController::class)
56
 */
57
class ProductController
58
{
59
    /**
60
     * @Inject("eccube.purchase.flow.cart")
61
     * @var PurchaseFlow
62
     */
63
    protected $purchaseFlow;
64
65
    /**
66
     * @Inject("session")
67
     * @var Session
68
     */
69
    protected $session;
70
71
    /**
72
     * @Inject(CustomerFavoriteProductRepository::class)
73
     * @var CustomerFavoriteProductRepository
74
     */
75
    protected $customerFavoriteProductRepository;
76
77
    /**
78
     * @Inject(CartService::class)
79
     * @var CartService
80
     */
81
    protected $cartService;
82
83
    /**
84
     * @Inject(ProductRepository::class)
85
     * @var ProductRepository
86
     */
87
    protected $productRepository;
88
89
    /**
90
     * @Inject("eccube.event.dispatcher")
91
     * @var EventDispatcher
92
     */
93
    protected $eventDispatcher;
94
95
    /**
96
     * @Inject("form.factory")
97
     * @var FormFactory
98
     */
99
    protected $formFactory;
100
101
    /**
102
     * @Inject("orm.em")
103
     * @var EntityManager
104
     */
105
    protected $entityManager;
106
107
    /**
108
     * @Inject(BaseInfo::class)
109
     * @var BaseInfo
110
     */
111
    protected $BaseInfo;
112
113
    private $title;
114
115
    public function __construct()
0 ignored issues
show
introduced by
Missing function doc comment
Loading history...
116
    {
117
        $this->title = '';
118
    }
119
120
    /**
0 ignored issues
show
introduced by
Doc comment for parameter "$app" missing
Loading history...
introduced by
Doc comment for parameter "$request" missing
Loading history...
121
     * 商品一覧画面.
122
     *
123
     * @Route("/products/list", name="product_list")
124
     * @Template("Product/list.twig")
125
     */
0 ignored issues
show
introduced by
Missing @return tag in function comment
Loading history...
126
    public function index(Application $app, Request $request)
127 3
    {
128
        // Doctrine SQLFilter
129
        if ($this->BaseInfo->isNostockHidden()) {
130 3
            $this->entityManager->getFilters()->enable('nostock_hidden');
131
        }
132
133
        // handleRequestは空のqueryの場合は無視するため
134
        if ($request->getMethod() === 'GET') {
135 3
            $request->query->set('pageno', $request->query->get('pageno', ''));
136 3
        }
137
138
        // searchForm
139
        /* @var $builder \Symfony\Component\Form\FormBuilderInterface */
140
        $builder = $this->formFactory->createNamedBuilder('', SearchProductType::class);
141 3
        $builder->setAttribute('freeze', true);
142 3
        $builder->setAttribute('freeze_display_text', false);
143 3
        if ($request->getMethod() === 'GET') {
144 3
            $builder->setMethod('GET');
145 3
        }
146
147
        $event = new EventArgs(
148 3
            array(
149
                'builder' => $builder,
150 3
            ),
151
            $request
152 3
        );
153
        $this->eventDispatcher->dispatch(EccubeEvents::FRONT_PRODUCT_INDEX_INITIALIZE, $event);
154 3
155
        /* @var $searchForm \Symfony\Component\Form\FormInterface */
156
        $searchForm = $builder->getForm();
157 3
158
        $searchForm->handleRequest($request);
159 3
160
        // paginator
161
        $searchData = $searchForm->getData();
162 3
        $qb = $this->productRepository->getQueryBuilderBySearchData($searchData);
163 3
164
        $event = new EventArgs(
165 3
            array(
166
                'searchData' => $searchData,
167 3
                'qb' => $qb,
168 3
            ),
169
            $request
170 3
        );
171
        $this->eventDispatcher->dispatch(EccubeEvents::FRONT_PRODUCT_INDEX_SEARCH, $event);
172 3
        $searchData = $event->getArgument('searchData');
173 3
174
        $pagination = $app['paginator']()->paginate(
175 3
            $qb,
176 3
            !empty($searchData['pageno']) ? $searchData['pageno'] : 1,
177 3
            $searchData['disp_number']->getId()
178 3
        );
179
180
        // addCart form
181
        $forms = array();
182 3
        foreach ($pagination as $Product) {
183 3
            /* @var $builder \Symfony\Component\Form\FormBuilderInterface */
184
            $builder = $this->formFactory->createNamedBuilder(
185 3
                '',
186 3
                AddCartType::class,
187 3
                null,
188 3
                array(
189
                    'product' => $Product,
190 3
                    'allow_extra_fields' => true,
191
                )
192
            );
193
            $addCartForm = $builder->getForm();
194 3
195
            if ($request->getMethod() === 'POST' && (string)$Product->getId() === $request->get('product_id')) {
0 ignored issues
show
Coding Style introduced by
As per coding-style, a cast statement should be followed by a single space.
Loading history...
196 3
                $addCartForm->handleRequest($request);
197
198
                if ($addCartForm->isValid()) {
199
                    $addCartData = $addCartForm->getData();
200
201
                    try {
202
                        $this->cartService->addProduct(
0 ignored issues
show
Bug introduced by
The method save cannot be called on $this->cartService->addP...ddCartData['quantity']) (of type boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
203
                            $addCartData['product_class_id'],
204
                            $addCartData['quantity']
205
                        )->save();
206
                    } catch (CartException $e) {
207
                        $app->addRequestError($e->getMessage());
208
                    }
209
210
                    $event = new EventArgs(
211
                        array(
212
                            'form' => $addCartForm,
213
                            'Product' => $Product,
214
                        ),
215
                        $request
216
                    );
217
                    $this->eventDispatcher->dispatch(EccubeEvents::FRONT_PRODUCT_INDEX_COMPLETE, $event);
218
219
                    if ($event->getResponse() !== null) {
220
                        return $event->getResponse();
221
                    }
222
223
                    return $app->redirect($app->url('cart'));
224
                }
225
            }
226
227
            $forms[$Product->getId()] = $addCartForm->createView();
228 3
        }
229
230
        // 表示件数
231
        $builder = $this->formFactory->createNamedBuilder(
232 3
            'disp_number',
233 3
            ProductListMaxType::class,
234 3
            null,
235 3
            array(
236
                'required' => false,
237 3
                'label' => '表示件数',
238
                'allow_extra_fields' => true,
239
            )
240
        );
241
        if ($request->getMethod() === 'GET') {
242 3
            $builder->setMethod('GET');
243 3
        }
244
245
        $event = new EventArgs(
246 3
            array(
247
                'builder' => $builder,
248 3
            ),
249
            $request
250 3
        );
251
        $this->eventDispatcher->dispatch(EccubeEvents::FRONT_PRODUCT_INDEX_DISP, $event);
252 3
253
        $dispNumberForm = $builder->getForm();
254 3
255
        $dispNumberForm->handleRequest($request);
256 3
257
        // ソート順
258
        $builder = $this->formFactory->createNamedBuilder(
259 3
            'orderby',
260 3
            ProductListOrderByType::class,
261 3
            null,
262 3
            array(
263
                'required' => false,
264 3
                'label' => '表示順',
265
                'allow_extra_fields' => true,
266
            )
267
        );
268
        if ($request->getMethod() === 'GET') {
269 3
            $builder->setMethod('GET');
270 3
        }
271
272
        $event = new EventArgs(
273 3
            array(
274
                'builder' => $builder,
275 3
            ),
276
            $request
277 3
        );
278
        $this->eventDispatcher->dispatch(EccubeEvents::FRONT_PRODUCT_INDEX_ORDER, $event);
279 3
280
        $orderByForm = $builder->getForm();
281 3
282
        $orderByForm->handleRequest($request);
283 3
284
        $Category = $searchForm->get('category_id')->getData();
285 3
286
        return [
287
            'subtitle' => $this->getPageTitle($searchData),
288 3
            'pagination' => $pagination,
289 3
            'search_form' => $searchForm->createView(),
290 3
            'disp_number_form' => $dispNumberForm->createView(),
291 3
            'order_by_form' => $orderByForm->createView(),
292 3
            'forms' => $forms,
293 3
            'Category' => $Category,
294 3
        ];
295
    }
296
297
    /**
0 ignored issues
show
introduced by
Doc comment for parameter "$app" missing
Loading history...
introduced by
Doc comment for parameter "$request" missing
Loading history...
introduced by
Doc comment for parameter "$Product" missing
Loading history...
298
     * 商品詳細画面.
299
     *
300
     * @Method("GET")
301
     * @Route("/products/detail/{id}", name="product_detail", requirements={"id" = "\d+"})
302
     * @Template("Product/detail.twig")
303
     */
0 ignored issues
show
introduced by
Missing @return tag in function comment
Loading history...
304 4
    public function detail(Application $app, Request $request, Product $Product)
305
    {
306 4
        if (!$this->checkVisibility($Product)) {
307
            throw new NotFoundHttpException();
308
        }
309 4
310
        $builder = $this->formFactory->createNamedBuilder(
311 4
            '',
312
            AddCartType::class,
313
            null,
314
            array(
315
                'product' => $Product,
316
                'id_add_product_id' => false,
317 4
            )
318
        );
319
320
        $event = new EventArgs(
321
            array(
322 4
                'builder' => $builder,
323 4
                'Product' => $Product,
324 4
            ),
325 4
            $request
326
        );
327 4
        $this->eventDispatcher->dispatch(EccubeEvents::FRONT_PRODUCT_DETAIL_INITIALIZE, $event);
328
329
        $is_favorite = false;
330
        if ($app->isGranted('ROLE_USER')) {
331
            $Customer = $app->user();
332 4
            $is_favorite = $this->customerFavoriteProductRepository->isFavorite($Customer, $Product);
333
        }
334 4
335 4
        return [
336
            'title' => $this->title,
337 4
            'subtitle' => $Product->getName(),
338
            'form' => $builder->getForm()->createView(),
339 4
            'Product' => $Product,
340
            'is_favorite' => $is_favorite,
341
        ];
342 4
    }
343
344 4
    /**
0 ignored issues
show
introduced by
Doc comment for parameter "$app" missing
Loading history...
introduced by
Doc comment for parameter "$Product" missing
Loading history...
introduced by
Doc comment for parameter "$request" missing
Loading history...
345 3
     * お気に入り追加.
346
     *
347 3
     * @Route("/products/add_favorite/{id}", name="product_add_favorite", requirements={"id" = "\d+"})
348 2
     */
0 ignored issues
show
introduced by
Missing @return tag in function comment
Loading history...
349 2
    public function addFavorite(Application $app, Request $request, Product $Product)
350 2
    {
351 2
        $this->checkVisibility($Product);
352 2
353 2
        $event = new EventArgs(
354
            array(
355 2
                'Product' => $Product,
356
            ),
357 2
            $request
358 2
        );
359
        $this->eventDispatcher->dispatch(EccubeEvents::FRONT_PRODUCT_FAVORITE_ADD_INITIALIZE, $event);
360 2
361
362 2
        if ($app->isGranted('ROLE_USER')) {
363
            $Customer = $app->user();
364 2
            $this->customerFavoriteProductRepository->addFavorite($Customer, $Product);
365
            $this->session->getFlashBag()->set('product_detail.just_added_favorite', $Product->getId());
366
367
            $event = new EventArgs(
368 2
                array(
369
                    'Product' => $Product,
370
                ),
371
                $request
372
            );
373
            $this->eventDispatcher->dispatch(EccubeEvents::FRONT_PRODUCT_FAVORITE_ADD_COMPLETE, $event);
374
375
            return $app->redirect($app->url('product_detail', array('id' => $Product->getId())));
376 View Code Duplication
        } else {
377
            // 非会員の場合、ログイン画面を表示
378
            //  ログイン後の画面遷移先を設定
379
            $app->setLoginTargetPath($app->url('product_add_favorite', array('id' => $Product->getId())));
380
            $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...
381
382
            $event = new EventArgs(
383
                array(
384
                    'Product' => $Product,
385
                ),
386
                $request
387
            );
388
            $this->eventDispatcher->dispatch(EccubeEvents::FRONT_PRODUCT_FAVORITE_ADD_COMPLETE, $event);
389
390
            return $app->redirect($app->url('mypage_login'));
391
        }
392
    }
393
394
    /**
0 ignored issues
show
introduced by
Doc comment for parameter "$app" missing
Loading history...
introduced by
Doc comment for parameter "$request" missing
Loading history...
introduced by
Doc comment for parameter "$Product" missing
Loading history...
395
     * カートに追加.
396
     *
397
     * @Method("POST")
398
     * @Route("/products/add_cart/{id}", name="product_add_cart", requirements={"id" = "\d+"})
399
     */
0 ignored issues
show
introduced by
Missing @return tag in function comment
Loading history...
400
    public function addCart(Application $app, Request $request, Product $Product)
401
    {
402
        // エラーメッセージの配列
403
        $errorMessages = array();
404
405
        if (!$this->checkVisibility($Product)) {
406
            throw new NotFoundHttpException();
407
        }
408
409
        $builder = $this->formFactory->createNamedBuilder(
410
            '',
411
            AddCartType::class,
412
            null,
413
            array(
414
                'product' => $Product,
415
                'id_add_product_id' => false,
416
            )
417
        );
418
419
        $event = new EventArgs(
420
            array(
421
                'builder' => $builder,
422
                'Product' => $Product,
423
            ),
424
            $request
425
        );
426
        $this->eventDispatcher->dispatch(EccubeEvents::FRONT_PRODUCT_CART_ADD_INITIALIZE, $event);
427
428
        /* @var $form \Symfony\Component\Form\FormInterface */
429
        $form = $builder->getForm();
430
        $form->handleRequest($request);
431
432
        if (!$form->isValid()) {
433
            throw new NotFoundHttpException();
434 1
        }
435
436
        $addCartData = $form->getData();
437
438 3
        log_info(
439 3
            'カート追加処理開始',
440
            array(
441
                'product_id' => $Product->getId(),
442
                'product_class_id' => $addCartData['product_class_id'],
443
                'quantity' => $addCartData['quantity'],
444
            )
445
        );
446
447
        // カートへ追加
448
        $this->cartService->addProduct($addCartData['product_class_id'], $addCartData['quantity']);
449 4
450 4
        // 明細の正規化
451 2
        $flow = $this->purchaseFlow;
452 2
        $Cart = $this->cartService->getCart();
453
        $result = $flow->calculate($Cart, $app['eccube.purchase.context']());
454
455
        // 復旧不可のエラーが発生した場合は追加した明細を削除.
456 4
        if ($result->hasError()) {
457 4
            $this->cartService->removeProduct($addCartData['product_class_id']);
458 4
            foreach ($result->getErrors() as $error) {
459 4
                array_push($errorMessages, $error->getMessage());
460 4
            }
461
        }
462
463
        foreach ($result->getWarning() as $warning) {
464
            array_push($errorMessages, $warning->getMessage());
465
        }
466
467
        $this->cartService->save();
468
469
        log_info(
470 3
            'カート追加処理完了',
471
            array(
472 3
                'product_id' => $Product->getId(),
473
                'product_class_id' => $addCartData['product_class_id'],
474 3
                'quantity' => $addCartData['quantity'],
475 1
            )
476
        );
477 2
478
        $event = new EventArgs(
479
            array(
480
                'form' => $form,
481
                'Product' => $Product,
482
            ),
483
            $request
484
        );
485
        $this->eventDispatcher->dispatch(EccubeEvents::FRONT_PRODUCT_CART_ADD_COMPLETE, $event);
486
487
        if ($event->getResponse() !== null) {
488
            return $event->getResponse();
489
        }
490
        
0 ignored issues
show
introduced by
Please trim any trailing whitespace
Loading history...
491
        if ($request->isXmlHttpRequest()) {
492
            // ajaxでのリクエストの場合は結果をjson形式で返す。
493
            
0 ignored issues
show
introduced by
Please trim any trailing whitespace
Loading history...
494
            // 初期化
495
            $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...
496
            $messages = array();
497
498
            if (empty($errorMessages)) {
499
                // エラーが発生していない場合
500
                $done = true;
501
                array_push($messages, 'カートに追加しました。');
502
            } else {
503
                // エラーが発生している場合
504
                $done = false;
505
                $messages = $errorMessages;
506
            }
507
508
            return new JsonResponse(array('done' => $done, 'messages' => $messages));
509
0 ignored issues
show
Coding Style introduced by
Blank line found at end of control structure
Loading history...
510
        } else {
511
            // ajax以外でのリクエストの場合はカート画面へリダイレクト
512
            foreach ($errorMessages as $errorMessage) {
513
                $app->addRequestError($errorMessage);
514
            }
515
516
            return $app->redirect($app->url('cart'));
517
        }
518
    }
519
520
    /**
521
     * ページタイトルの設定
522
     *
523
     * @param  null|array $searchData
524
     * @return str
525
     */
526
    private function getPageTitle($searchData)
527
    {
528
        if (isset($searchData['name']) && !empty($searchData['name'])) {
529
            return '検索結果';
530
        } elseif (isset($searchData['category_id']) && $searchData['category_id']) {
531
            return $searchData['category_id']->getName();
532
        } else {
533
            return '全商品';
534
        }
535
    }
536
537
    /**
538
     * 閲覧可能な商品かどうかを判定
539
     * @param Product $Product
540
     * @return boolean 閲覧可能な場合はtrue
541
     */
542
    private function checkVisibility(Product $Product)
543
    {
544
        $is_admin = $this->session->has('_security_admin');
545
546
        // 管理ユーザの場合はステータスやオプションにかかわらず閲覧可能.
547
        if (!$is_admin) {
548
            // 在庫なし商品の非表示オプションが有効な場合.
549
            if ($this->BaseInfo->isNostockHidden()) {
550
                if (!$Product->getStockFind()) {
551
                    return false;
552
                }
553
            }
554
            // 公開ステータスでない商品は表示しない.
555
            if ($Product->getStatus()->getId() !== ProductStatus::DISPLAY_SHOW) {
556
                return false;
557
            }
558
        }
559
        return true;
0 ignored issues
show
introduced by
Missing blank line before return statement
Loading history...
560
    }
561
}
562