Completed
Pull Request — experimental/sf (#3456)
by Kiyotaka
87:43 queued 81:34
created

ShoppingController   F

Complexity

Total Complexity 117

Size/Duplication

Total Lines 922
Duplicated Lines 2.71 %

Coupling/Cohesion

Components 1
Dependencies 27

Test Coverage

Coverage 54.01%

Importance

Changes 0
Metric Value
dl 25
loc 922
ccs 209
cts 387
cp 0.5401
rs 1.678
c 0
b 0
f 0
wmc 117
lcom 1
cbo 27

20 Methods

Rating   Name   Duplication   Size   Complexity  
B order() 0 38 7
B isValidPayment() 0 25 6
A complete() 0 37 3
C shipping() 0 67 10
D shippingEdit() 0 109 15
A shoppingError() 0 14 2
A checkToCart() 0 18 4
B initializeOrder() 0 48 5
A createShoppingForm() 0 21 1
B redirectToChange() 0 36 8
A existsOrder() 0 13 2
A __construct() 17 17 1
B index() 0 37 7
B redirectTo() 0 32 7
C confirm() 0 52 12
A login() 0 31 4
C completeOrder() 0 79 14
A doCheckoutOrder() 0 22 5
A afterComplete() 8 48 3
A createPaymentMethod() 0 8 1

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 ShoppingController 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 ShoppingController, 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) LOCKON CO.,LTD. All Rights Reserved.
7
 *
8
 * http://www.lockon.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\Annotation\ForwardOnly;
17
use Eccube\Entity\BaseInfo;
18
use Eccube\Entity\Customer;
19
use Eccube\Entity\CustomerAddress;
20
use Eccube\Entity\Master\OrderStatus;
21
use Eccube\Entity\Order;
22
use Eccube\Entity\Shipping;
23
use Eccube\Event\EccubeEvents;
24
use Eccube\Event\EventArgs;
25
use Eccube\Exception\CartException;
26
use Eccube\Exception\ShoppingException;
27
use Eccube\Form\Type\Front\CustomerLoginType;
28
use Eccube\Form\Type\Front\ShoppingShippingType;
29
use Eccube\Form\Type\Shopping\CustomerAddressType;
30
use Eccube\Form\Type\Shopping\OrderType;
31
use Eccube\Repository\BaseInfoRepository;
32
use Eccube\Repository\CustomerAddressRepository;
33
use Eccube\Repository\OrderRepository;
34
use Eccube\Service\CartService;
35
use Eccube\Service\OrderHelper;
36
use Eccube\Service\Payment\PaymentDispatcher;
37
use Eccube\Service\ShoppingService;
38
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
39
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
40
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
41
use Symfony\Component\Form\FormError;
42
use Symfony\Component\Form\FormInterface;
43
use Symfony\Component\HttpFoundation\ParameterBag;
44
use Symfony\Component\HttpFoundation\Request;
45
use Symfony\Component\HttpFoundation\Response;
46
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
47
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
48
49
class ShoppingController extends AbstractShoppingController
50
{
51
    /**
52
     * @var BaseInfo
53
     */
54
    protected $BaseInfo;
55
56
    /**
57
     * @var OrderHelper
58
     */
59
    protected $orderHelper;
60
61
    /**
62
     * @var CartService
63
     */
64
    protected $cartService;
65
66
    /**
67
     * @var ShoppingService
68
     */
69
    protected $shoppingService;
70
71
    /**
72
     * @var CustomerAddressRepository
73
     */
74
    protected $customerAddressRepository;
75
76
    /**
77
     * @var ParameterBag
78
     */
79
    protected $parameterBag;
80
81
    /**
82
     * ShoppingController constructor.
83
     *
84
     * @param BaseInfoRepository $baseInfoRepository
85
     * @param OrderHelper $orderHelper
86
     * @param CartService $cartService
87
     * @param ShoppingService $shoppingService
88
     * @param CustomerAddressRepository $customerAddressRepository
89
     * @param ParameterBag $parameterBag
90
     */
91 59 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...
92
        BaseInfoRepository $baseInfoRepository,
93
        OrderHelper $orderHelper,
94
        CartService $cartService,
95
        ShoppingService $shoppingService,
96
        CustomerAddressRepository $customerAddressRepository,
97
        OrderRepository $orderRepository,
98
        ParameterBag $parameterBag
99
    ) {
100 59
        $this->BaseInfo = $baseInfoRepository->get();
101 59
        $this->orderHelper = $orderHelper;
102 59
        $this->cartService = $cartService;
103 59
        $this->shoppingService = $shoppingService;
104 59
        $this->customerAddressRepository = $customerAddressRepository;
105 59
        $this->orderRepository = $orderRepository;
106 59
        $this->parameterBag = $parameterBag;
107
    }
108
109
    /**
110
     * 購入画面表示
111
     *
112
     * @Route("/shopping", name="shopping")
113
     * @Template("Shopping/index.twig")
114
     */
115 51
    public function index(Request $request)
0 ignored issues
show
Unused Code introduced by
The parameter $request 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...
116
    {
117
        // カートチェック
118 51
        $response = $this->forwardToRoute('shopping_check_to_cart');
119 51
        if ($response->isRedirection() || $response->getContent()) {
120 1
            return $response;
121
        }
122
123
        // 受注情報を初期化
124 50
        $response = $this->forwardToRoute('shopping_initialize_order');
125 50
        if ($response->isRedirection() || $response->getContent()) {
126
            return $response;
127
        }
128
129
        /** @var Order $Order */
130 50
        $Order = $this->parameterBag->get('Order');
131
132
        // 単価集計
133 50
        $flowResult = $this->validatePurchaseFlow($Order);
134
135
        // 明細が丸められる場合に, カートから注文画面へ遷移できなくなるため, 集計の結果を保存する
136 50
        $this->entityManager->flush();
137
138
        // フォームを生成する
139 50
        $this->forwardToRoute('shopping_create_form');
140
141 50
        if ($flowResult->hasWarning() || $flowResult->hasError()) {
142 10
            return $this->redirectToRoute('cart');
143
        }
144
145 45
        $form = $this->parameterBag->get(OrderType::class);
146
147
        return [
148 45
            'form' => $form->createView(),
149 45
            'Order' => $Order,
150
        ];
151
    }
152
153
    /**
154
     * 購入確認画面から, 他の画面へのリダイレクト.
155
     * 配送業者や支払方法、お問い合わせ情報をDBに保持してから遷移する.
156
     *
157
     * @Route("/shopping/redirect", name="shopping_redirect_to")
158
     * @Template("Shopping/index.twig")
159
     */
160 20
    public function redirectTo(Request $request)
161
    {
162
        // カートチェック
163 20
        $response = $this->forwardToRoute('shopping_check_to_cart');
164 20
        if ($response->isRedirection() || $response->getContent()) {
165
            return $response;
166
        }
167
168
        // 受注の存在チェック
169 20
        $response = $this->forwardToRoute('shopping_exists_order');
170 20
        if ($response->isRedirection() || $response->getContent()) {
171
            return $response;
172
        }
173
174
        // フォームの生成
175 20
        $this->forwardToRoute('shopping_create_form');
176 20
        $form = $this->parameterBag->get(OrderType::class);
177 20
        $form->handleRequest($request);
178
179
        // 各種変更ページへリダイレクトする
180 20
        $response = $this->forwardToRoute('shopping_redirect_to_change');
181 20
        if ($response->isRedirection() || $response->getContent()) {
182 8
            return $response;
183
        }
184 12
        $form = $this->parameterBag->get(OrderType::class);
185 12
        $Order = $this->parameterBag->get('Order');
186
187
        return [
188 12
            'form' => $form->createView(),
189 12
            'Order' => $Order,
190
        ];
191
    }
192
193
    /**
194
     * 購入処理
195
     *
196
     * @Route("/shopping/confirm", name="shopping_confirm")
197
     * @Method("POST")
198
     * @Template("Shopping/confirm.twig")
199
     */
200 5
    public function confirm(Request $request)
201
    {
202
        // カートチェック
203 5
        $response = $this->forwardToRoute('shopping_check_to_cart');
204 5
        if ($response->isRedirection() || $response->getContent()) {
205
            return $response;
206
        }
207
208
        // 受注の存在チェック
209 5
        $response = $this->forwardToRoute('shopping_exists_order');
210 5
        if ($response->isRedirection() || $response->getContent()) {
211
            return $response;
212
        }
213
214
        // フォームの生成
215 5
        $this->forwardToRoute('shopping_create_form');
216 5
        $form = $this->parameterBag->get(OrderType::class);
217 5
        $form->handleRequest($request);
218
219 5
        $form = $this->parameterBag->get(OrderType::class);
220 5
        $Order = $this->parameterBag->get('Order');
221
222 5
        $flowResult = $this->validatePurchaseFlow($Order);
223 5
        if ($flowResult->hasWarning() || $flowResult->hasError()) {
224
            return $this->redirectToRoute('shopping_error');
225
        }
226
227 5
        $paymentMethod = $this->createPaymentMethod($Order, $form);
228
229 5
        $PaymentResult = $paymentMethod->verify();
230
        // エラーの場合は注文入力画面に戻す?
231 5
        if ($PaymentResult instanceof PaymentResult) {
232
            if (!$PaymentResult->isSuccess()) {
233
                $this->entityManager->getConnection()->rollback();
234
235
                $this->addError($PaymentResult->getErrors());
236
            }
237
238
            $response = $PaymentResult->getResponse();
239
            if ($response && ($response->isRedirection() || $response->getContent())) {
240
                $this->entityManager->flush();
241
242
                return $response;
243
            }
244
        }
245 5
        $this->entityManager->flush();
246
247
        return [
248 5
            'form' => $form->createView(),
249 5
            'Order' => $Order,
250
        ];
251
    }
252
253
    /**
254
     * 購入処理
255
     *
256
     * @Route("/shopping/order", name="shopping_order")
257
     * @Method("POST")
258
     * @Template("Shopping/index.twig")
259
     */
260 6
    public function order(Request $request)
261
    {
262
        // カートチェック
263 6
        $response = $this->forwardToRoute('shopping_check_to_cart');
264 6
        if ($response->isRedirection() || $response->getContent()) {
265
            return $response;
266
        }
267
268
        // 受注の存在チェック
269 6
        $response = $this->forwardToRoute('shopping_exists_order');
270 6
        if ($response->isRedirection() || $response->getContent()) {
271
            return $response;
272
        }
273
274
        // form作成
275
        // FIXME イベントハンドラを外から渡したい
276 6
        $this->forwardToRoute('shopping_create_form');
277
278 6
        $form = $this->parameterBag->get(OrderType::class);
279 6
        $Order = $this->parameterBag->get('Order');
280 6
        $usePoint = $Order->getUsePoint();
281
282 6
        $form->handleRequest($request);
283 6
        $Order->setUsePoint($usePoint);
284
285
        // 受注処理
286 6
        $response = $this->forwardToRoute('shopping_complete_order');
287 6
        if ($response->isRedirection() || $response->getContent()) {
288 6
            return $response;
289
        }
290
291
        log_info('購入チェックエラー', [$Order->getId()]);
292
293
        return [
294
            'form' => $form->createView(),
295
            'Order' => $Order,
296
        ];
297
    }
298
299
    /**
300
     * 支払方法バーリデト
301
     */
302
    private function isValidPayment(Application $app, $form)
0 ignored issues
show
Unused Code introduced by
This method is not used, and could be removed.
Loading history...
303
    {
304
        $data = $form->getData();
305
        $paymentId = $data['payment']->getId();
306
        $shippings = $data['shippings'];
307
        $validCount = count($shippings);
308
        foreach ($shippings as $Shipping) {
309
            $payments = $app['eccube.repository.payment']->findPayments($Shipping->getDelivery());
310
            if ($payments == null) {
311
                continue;
312
            }
313
            foreach ($payments as $payment) {
314
                if ($payment['id'] == $paymentId) {
315
                    $validCount--;
316
                    continue;
317
                }
318
            }
319
        }
320
        if ($validCount == 0) {
321
            return true;
322
        }
323
        $form->get('payment')->addError(new FormError('front.shopping.payment.error'));
324
325
        return false;
326
    }
327
328
    /**
329
     * 購入完了画面表示
330
     *
331
     * @Route("/shopping/complete", name="shopping_complete")
332
     * @Template("Shopping/complete.twig")
333
     */
334 1
    public function complete(Request $request)
335
    {
336
        // 受注IDを取得
337 1
        $orderId = $this->session->get($this->sessionOrderKey);
338
339 1
        if (empty($orderId)) {
340
            return $this->redirectToRoute('homepage');
341
        }
342
343 1
        $Order = $this->orderRepository->find($orderId);
344
345 1
        $event = new EventArgs(
346
            [
347 1
                'Order' => $Order,
348
            ],
349 1
            $request
350
        );
351 1
        $this->eventDispatcher->dispatch(EccubeEvents::FRONT_SHOPPING_COMPLETE_INITIALIZE, $event);
352
353 1
        if ($event->getResponse() !== null) {
354
            return $event->getResponse();
355
        }
356
357
        // 受注に関連するセッションを削除
358 1
        $this->session->remove($this->sessionOrderKey);
359 1
        $this->session->remove($this->sessionKey);
360 1
        $this->session->remove($this->sessionCustomerAddressKey);
361
362 1
        log_info('購入処理完了', [$Order->getId()]);
363
364 1
        $hasNextCart = !empty($this->cartService->getCarts());
365
366
        return [
367 1
            'Order' => $Order,
368 1
            'hasNextCart' => $hasNextCart,
369
        ];
370
    }
371
372
    /**
373
     * お届け先の設定一覧からの選択
374
     *
375
     * @Route("/shopping/shipping/{id}", name="shopping_shipping", requirements={"id" = "\d+"})
376
     * @Template("Shopping/shipping.twig")
377
     */
378
    public function shipping(Request $request, Shipping $Shipping)
379
    {
380
        // カートチェック
381
        $response = $this->forwardToRoute('shopping_check_to_cart');
382
        if ($response->isRedirection() || $response->getContent()) {
383
            return $response;
384
        }
385
386
        // 受注の存在チェック
387
        $response = $this->forwardToRoute('shopping_exists_order');
388
        if ($response->isRedirection() || $response->getContent()) {
389
            return $response;
390
        }
391
392
        // 受注に紐づくShippingかどうかのチェック.
393
        /** @var Order $Order */
394
        $Order = $this->parameterBag->get('Order');
395
        if (!$Order->findShipping($Shipping->getId())) {
396
            throw new NotFoundHttpException();
397
        }
398
399
        $builder = $this->formFactory->createBuilder(CustomerAddressType::class, null, [
400
            'customer' => $this->getUser(),
401
            'shipping' => $Shipping,
402
        ]);
403
404
        $form = $builder->getForm();
405
        $form->handleRequest($request);
406
407
        if ($form->isSubmitted() && $form->isValid()) {
408
            log_info('お届先情報更新開始', [$Shipping->getId()]);
409
410
            /** @var CustomerAddress $CustomerAddress */
411
            $CustomerAddress = $form['addresses']->getData();
412
413
            // お届け先情報を更新
414
            $Shipping->setFromCustomerAddress($CustomerAddress);
415
416
            // 合計金額の再計算
417
            $flowResult = $this->validatePurchaseFlow($Order);
418
            if ($flowResult->hasWarning() || $flowResult->hasError()) {
419
                return $this->redirectToRoute('shopping_error');
420
            }
421
422
            // 配送先を更新
423
            $this->entityManager->flush();
424
425
            $event = new EventArgs(
426
                [
427
                    'Order' => $Order,
428
                    'Shipping' => $Shipping,
429
                ],
430
                $request
431
            );
432
            $this->eventDispatcher->dispatch(EccubeEvents::FRONT_SHOPPING_SHIPPING_COMPLETE, $event);
433
434
            log_info('お届先情報更新完了', [$Shipping->getId()]);
435
436
            return $this->redirectToRoute('shopping');
437
        }
438
439
        return [
440
            'form' => $form->createView(),
441
            'Customer' => $this->getUser(),
442
            'shippingId' => $Shipping->getId(),
443
        ];
444
    }
445
446
    /**
447
     * お届け先の設定(非会員でも使用する)
448
     *
449
     * @Route("/shopping/shipping_edit/{id}", name="shopping_shipping_edit", requirements={"id" = "\d+"})
450
     * @Template("Shopping/shipping_edit.twig")
451
     */
452
    public function shippingEdit(Request $request, $id)
453
    {
454
        // 配送先住所最大値判定
455
        $Customer = $this->getUser();
456
        if ($this->isGranted('IS_AUTHENTICATED_FULLY')) {
457
            $addressCurrNum = count($this->getUser()->getCustomerAddresses());
458
            $addressMax = $this->eccubeConfig['eccube_deliv_addr_max'];
459
            if ($addressCurrNum >= $addressMax) {
460
                throw new NotFoundHttpException(trans('shoppingcontroller.text.error.number_of_address'));
461
            }
462
        }
463
464
        // カートチェック
465
        $response = $this->forwardToRoute('shopping_check_to_cart');
466
        if ($response->isRedirection() || $response->getContent()) {
467
            return $response;
468
        }
469
470
        // 受注の存在チェック
471
        $response = $this->forwardToRoute('shopping_exists_order');
472
        if ($response->isRedirection() || $response->getContent()) {
473
            return $response;
474
        }
475
476
        /** @var Order $Order */
477
        $Order = $this->parameterBag->get('Order');
478
479
        $Shipping = $Order->findShipping($id);
480
        if (!$Shipping) {
481
            throw new NotFoundHttpException(trans('shoppingcontroller.text.error.set_address'));
482
        }
483
        if ($this->isGranted('IS_AUTHENTICATED_FULLY')) {
484
            $Shipping->clearCustomerAddress();
485
        }
486
487
        $CustomerAddress = new CustomerAddress();
488
        if ($this->isGranted('IS_AUTHENTICATED_FULLY')) {
489
            $CustomerAddress->setCustomer($Customer);
0 ignored issues
show
Bug introduced by
It seems like $Customer defined by $this->getUser() on line 455 can also be of type object; however, Eccube\Entity\CustomerAddress::setCustomer() does only seem to accept null|object<Eccube\Entity\Customer>, 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...
490
        } else {
491
            $CustomerAddress->setFromShipping($Shipping);
492
        }
493
494
        $builder = $this->formFactory->createBuilder(ShoppingShippingType::class, $CustomerAddress);
495
496
        $event = new EventArgs(
497
            [
498
                'builder' => $builder,
499
                'Order' => $Order,
500
                'Shipping' => $Shipping,
501
                'CustomerAddress' => $CustomerAddress,
502
            ],
503
            $request
504
        );
505
        $this->eventDispatcher->dispatch(EccubeEvents::FRONT_SHOPPING_SHIPPING_EDIT_INITIALIZE, $event);
506
507
        $form = $builder->getForm();
508
509
        $form->handleRequest($request);
510
511
        if ($form->isSubmitted() && $form->isValid()) {
512
            log_info('お届け先追加処理開始', ['id' => $Order->getId(), 'shipping' => $id]);
513
514
            // 会員の場合、お届け先情報を新規登録
515
            $Shipping->setFromCustomerAddress($CustomerAddress);
516
517
            if ($Customer instanceof Customer) {
518
                $this->entityManager->persist($CustomerAddress);
519
                log_info(
520
                    '新規お届け先登録',
521
                    [
522
                        'id' => $Order->getId(),
523
                        'shipping' => $id,
524
                        'customer address' => $CustomerAddress->getId(),
525
                    ]
526
                );
527
            }
528
529
            // 配送料金の設定
530
            $this->shoppingService->setShippingDeliveryFee($Shipping);
531
532
            // 合計金額の再計算
533
            $flowResult = $this->validatePurchaseFlow($Order);
534
            if ($flowResult->hasWarning() || $flowResult->hasError()) {
535
                return $this->redirectToRoute('shopping_error');
536
            }
537
538
            // 配送先を更新
539
            $this->entityManager->flush();
540
541
            $event = new EventArgs(
542
                [
543
                    'form' => $form,
544
                    'Shipping' => $Shipping,
545
                    'CustomerAddress' => $CustomerAddress,
546
                ],
547
                $request
548
            );
549
            $this->eventDispatcher->dispatch(EccubeEvents::FRONT_SHOPPING_SHIPPING_EDIT_COMPLETE, $event);
550
551
            log_info('お届け先追加処理完了', ['id' => $Order->getId(), 'shipping' => $id]);
552
553
            return $this->redirectToRoute('shopping');
554
        }
555
556
        return [
557
            'form' => $form->createView(),
558
            'shippingId' => $id,
559
        ];
560
    }
561
562
    /**
563
     * ログイン
564
     *
565
     * @Route("/shopping/login", name="shopping_login")
566
     * @Template("Shopping/login.twig")
567
     */
568 2
    public function login(Request $request, AuthenticationUtils $authenticationUtils)
569
    {
570 2
        if ($this->isGranted('IS_AUTHENTICATED_FULLY')) {
571
            return $this->redirectToRoute('shopping');
572
        }
573
574
        /* @var $form \Symfony\Component\Form\FormInterface */
575 2
        $builder = $this->formFactory->createNamedBuilder('', CustomerLoginType::class);
576
577 2
        if ($this->isGranted('IS_AUTHENTICATED_REMEMBERED')) {
578
            $Customer = $this->getUser();
579
            if ($Customer) {
580
                $builder->get('login_email')->setData($Customer->getEmail());
581
            }
582
        }
583
584 2
        $event = new EventArgs(
585
            [
586 2
                'builder' => $builder,
587
            ],
588 2
            $request
589
        );
590 2
        $this->eventDispatcher->dispatch(EccubeEvents::FRONT_SHOPPING_LOGIN_INITIALIZE, $event);
591
592 2
        $form = $builder->getForm();
593
594
        return [
595 2
            'error' => $authenticationUtils->getLastAuthenticationError(),
596 2
            'form' => $form->createView(),
597
        ];
598
    }
599
600
    /**
601
     * 購入エラー画面表示
602
     *
603
     * @Route("/shopping/error", name="shopping_error")
604
     * @Template("Shopping/shopping_error.twig")
605
     */
606 1
    public function shoppingError(Request $request)
607
    {
608 1
        $event = new EventArgs(
609 1
            [],
610 1
            $request
611
        );
612 1
        $this->eventDispatcher->dispatch(EccubeEvents::FRONT_SHOPPING_SHIPPING_ERROR_COMPLETE, $event);
613
614 1
        if ($event->getResponse() !== null) {
615
            return $event->getResponse();
616
        }
617
618 1
        return [];
619
    }
620
621
    /**
622
     * カート画面のチェック
623
     *
624
     * @ForwardOnly
625
     * @Route("/shopping/check_to_cart", name="shopping_check_to_cart")
626
     */
627 55
    public function checkToCart(Request $request)
628
    {
629 55
        $Cart = $this->cartService->getCart();
630 55
        if ($Cart && count($Cart->getCartItems()) > 0) {
631 53
            $divide = $request->getSession()->get('cart.divide');
632 53
            if ($divide) {
633
                log_info('種別が異なる商品がカートと結合されたためカート画面にリダイレクト');
634
635
                return $this->redirectToRoute('cart');
636
            }
637
638 53
            return new Response();
639
        }
640 2
        log_info('カートに商品が入っていないためショッピングカート画面にリダイレクト');
641
642
        // カートが存在しない時はエラー
643 2
        return $this->redirectToRoute('cart');
644
    }
645
646
    /**
647
     * 受注情報を初期化する.
648
     *
649
     * @ForwardOnly
650
     * @Route("/shopping/initialize_order", name="shopping_initialize_order")
651
     */
652 50
    public function initializeOrder(Request $request)
0 ignored issues
show
Unused Code introduced by
The parameter $request 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...
653
    {
654
        // 購入処理中の受注情報を取得
655 50
        $Order = $this->shoppingService->getOrder(OrderStatus::PROCESSING);
656
657
        // 初回アクセス(受注情報がない)の場合は, 受注情報を作成
658 50
        if (is_null($Order)) {
659
            // 未ログインの場合, ログイン画面へリダイレクト.
660 34
            if (!$this->isGranted('IS_AUTHENTICATED_FULLY')) {
661
                // 非会員でも一度会員登録されていればショッピング画面へ遷移
662
                $Customer = $this->shoppingService->getNonMember($this->sessionKey);
663
664
                if (is_null($Customer)) {
665
                    log_info('未ログインのためログイン画面にリダイレクト');
666
667
                    return $this->redirectToRoute('shopping_login');
668
                }
669
            } else {
670 34
                $Customer = $this->getUser();
671
            }
672
673
            try {
674
                // 受注情報を作成
675
                //$Order = $app['eccube.service.shopping']->createOrder($Customer);
676 34
                $Order = $this->orderHelper->createProcessingOrder(
677 34
                    $Customer,
678 34
                    $this->cartService->getCart()->getCartItems()
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Eccube\Entity\ItemHolderInterface as the method getCartItems() does only exist in the following implementations of said interface: Eccube\Entity\Cart.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
679
                );
680 34
                $this->cartService->setPreOrderId($Order->getPreOrderId());
681 34
                $this->cartService->save();
682
            } catch (CartException $e) {
683
                log_error('初回受注情報作成エラー', [$e->getMessage()]);
684
                $this->addRequestError($e->getMessage());
685
686
                return $this->redirectToRoute('cart');
687
            }
688
689
            // セッション情報を削除
690 34
            $this->session->remove($this->sessionOrderKey);
691
        }
692
693
        // 受注関連情報を最新状態に更新
694 50
        $this->entityManager->refresh($Order);
695
696 50
        $this->parameterBag->set('Order', $Order);
697
698 50
        return new Response();
699
    }
700
701
    /**
702
     * フォームを作成し, イベントハンドラを設定する
703
     *
704
     * @ForwardOnly
705
     * @Route("/shopping/create_form", name="shopping_create_form")
706
     */
707 50
    public function createShoppingForm(Request $request)
708
    {
709 50
        $Order = $this->parameterBag->get('Order');
710
        // フォームの生成
711 50
        $builder = $this->formFactory->createBuilder(OrderType::class, $Order);
712
713 50
        $event = new EventArgs(
714
                [
715 50
                    'builder' => $builder,
716 50
                    'Order' => $Order,
717
                ],
718 50
                $request
719
            );
720 50
        $this->eventDispatcher->dispatch(EccubeEvents::FRONT_SHOPPING_INDEX_INITIALIZE, $event);
721
722 50
        $form = $builder->getForm();
723
724 50
        $this->parameterBag->set(OrderType::class, $form);
725
726 50
        return new Response();
727
    }
728
729
    /**
730
     * mode に応じて各変更ページへリダイレクトする.
731
     *
732
     * @ForwardOnly
733
     * @Route("/shopping/redirect_to_change", name="shopping_redirect_to_change")
734
     */
735 20
    public function redirectToChange(Request $request)
0 ignored issues
show
Unused Code introduced by
The parameter $request 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...
736
    {
737 20
        $form = $this->parameterBag->get(OrderType::class);
738
739
        // requestのバインド後、Calculatorに再集計させる
740
        //$app['eccube.service.calculate']($Order, $Order->getCustomer())->calculate();
741
742
        // 支払い方法の変更や配送業者の変更があった場合はDBに保持する.
743 20
        if ($form->isSubmitted() && $form->isValid()) {
744
            // POSTされたデータをDBに保持.
745 8
            $this->entityManager->flush();
746
747 8
            $mode = $form['mode']->getData();
748 8
            switch ($mode) {
749 7
                case 'shipping_change':
750
                    // お届け先設定一覧へリダイレクト
751 1
                    $param = $form['param']->getData();
752
753 1
                    return $this->redirectToRoute('shopping_shipping', ['id' => $param]);
754 7
                case 'shipping_edit_change':
755
                    // お届け先設定一覧へリダイレクト
756
                    $param = $form['param']->getData();
757
758
                    return $this->redirectToRoute('shopping_shipping_edit', ['id' => $param]);
759 7
                case 'shipping_multiple_change':
760
                    // 複数配送設定へリダイレクト
761
                    return $this->redirectToRoute('shopping_shipping_multiple');
762 7
                case 'payment':
763 7
                case 'delivery':
764
                default:
765 7
                    return $this->redirectToRoute('shopping');
766
            }
767
        }
768
769 12
        return new Response();
770
    }
771
772
    /**
773
     * 受注の存在チェック
774
     *
775
     * @ForwardOnly
776
     * @Route("/shopping/exists_order", name="shopping_exists_order")
777
     */
778 26
    public function existsOrder(Request $request)
0 ignored issues
show
Unused Code introduced by
The parameter $request 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...
779
    {
780 26
        $Order = $this->shoppingService->getOrder(OrderStatus::PROCESSING);
781 26
        if (!$Order) {
782
            log_info('購入処理中の受注情報がないため購入エラー');
783
            $this->addError('front.shopping.order.error');
784
785
            return $this->redirectToRoute('shopping_error');
786
        }
787 26
        $this->parameterBag->set('Order', $Order);
788
789 26
        return new Response();
790
    }
791
792
    /**
793
     * 受注完了処理
794
     *
795
     * @ForwardOnly
796
     * @Route("/shopping/complete_order", name="shopping_complete_order")
797
     */
798 6
    public function completeOrder(Request $request)
0 ignored issues
show
Unused Code introduced by
The parameter $request 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...
799
    {
800 6
        $form = $this->parameterBag->get(OrderType::class);
801
802 6
        if ($form->isSubmitted() && $form->isValid()) {
803
            /** @var Order $Order */
804 6
            $Order = $form->getData();
805 6
            log_info('購入処理開始', [$Order->getId()]);
806
807
            // トランザクション制御
808 6
            $em = $this->entityManager;
809 6
            $em->getConnection()->beginTransaction();
810
            try {
811
                // お問い合わせ、配送時間などのフォーム項目をセット
812
                // FormTypeで更新されるため不要
813
                //$app['eccube.service.shopping']->setFormData($Order, $data);
814
815 6
                $flowResult = $this->validatePurchaseFlow($Order);
816 6
                if ($flowResult->hasWarning() || $flowResult->hasError()) {
817
                    // TODO エラーメッセージ
818
                    throw new ShoppingException();
819
                }
820
821 6
                $paymentMethod = $this->createPaymentMethod($Order, $form);
822
823
                // 必要に応じて別のコントローラへ forward or redirect(移譲)
824 6
                $dispatcher = $paymentMethod->apply(); // 決済処理中.
825
                // 一旦、決済処理中になった後は、購入処理中に戻せない。キャンセル or 購入完了の仕様とする
826
                // ステータス履歴も保持しておく? 在庫引き当ての仕様もセットで。
827 6
                if ($dispatcher instanceof PaymentDispatcher) {
828
                    $response = $dispatcher->getResponse();
829
                    $this->entityManager->flush();
830
                    $this->entityManager->commit();
831
832
                    if ($response && ($response->isRedirection() || $response->getContent())) {
833
                        return $response;
834
                    }
835
836
                    if ($dispatcher->isForward()) {
837
                        return $this->forwardToRoute($dispatcher->getRoute(), $dispatcher->getPathParameters(), $dispatcher->getQueryParameters());
838
                    } else {
839
                        return $this->redirectToRoute($dispatcher->getRoute(), array_merge($dispatcher->getPathParameters(), $dispatcher->getQueryParameters()));
840
                    }
841
                }
842
843
                // 決済実行
844 6
                $response = $this->forwardToRoute('shopping_do_checkout_order');
845 6
                $this->entityManager->flush();
846 6
                $this->entityManager->commit();
847
848 6
                if ($response->isRedirection() || $response->getContent()) {
849
                    return $response;
850
                }
851
852 6
                log_info('購入処理完了', [$Order->getId()]);
853
            } catch (ShoppingException $e) {
854
                log_error('購入エラー', [$e->getMessage()]);
855
856
                $this->entityManager->getConnection()->rollback();
857
858
                $this->log($e);
0 ignored issues
show
Bug introduced by
The method log() does not seem to exist on object<Eccube\Controller\ShoppingController>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
859
                $this->addError($e->getMessage());
860
861
                return $this->redirectToRoute('shopping_error');
862
            } catch (\Exception $e) {
863
                log_error('予期しないエラー', [$e->getMessage()]);
864
865
                $this->entityManager->getConnection()->rollback();
866
867
                $this->addError('front.shopping.system.error');
868
869
                return $this->redirectToRoute('shopping_error');
870
            }
871
872 6
            return $this->forwardToRoute('shopping_after_complete');
873
        }
874
875
        return new Response();
876
    }
877
878
    /**
879
     * 決済完了処理
880
     *
881
     * @ForwardOnly
882
     * @Route("/shopping/do_checkout_order", name="shopping_do_checkout_order")
883
     */
884 6
    public function doCheckoutOrder(Request $request)
0 ignored issues
show
Unused Code introduced by
The parameter $request 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...
885
    {
886 6
        $form = $this->parameterBag->get(OrderType::class);
887 6
        $Order = $this->parameterBag->get('Order');
888
889 6
        $paymentMethod = $this->createPaymentMethod($Order, $form);
890
891
        // 決済実行
892 6
        $PaymentResult = $paymentMethod->checkout();
893 6
        $response = $PaymentResult->getResponse();
894 6
        if ($response && ($response->isRedirection() || $response->getContent())) {
895
            return $response;
896
        }
897
898 6
        if (!$PaymentResult->isSuccess()) {
899
            $this->entityManager->getConnection()->rollback();
900
901
            $this->addError($PaymentResult->getErrors());
902
        }
903
904 6
        return new Response();
905
    }
906
907
    /**
908
     * 受注完了の後処理
909
     *
910
     * @ForwardOnly
911
     * @Route("/shopping/after_complete", name="shopping_after_complete")
912
     */
913 6
    public function afterComplete(Request $request)
914
    {
915 6
        $form = $this->parameterBag->get(OrderType::class);
916 6
        $Order = $this->parameterBag->get('Order');
917
918
        // カート削除
919 6
        $this->cartService->clear();
920
921 6
        $event = new EventArgs(
922
            [
923 6
                'form' => $form,
924 6
                'Order' => $Order,
925
            ],
926 6
            $request
927
        );
928 6
        $this->eventDispatcher->dispatch(EccubeEvents::FRONT_SHOPPING_CONFIRM_PROCESSING, $event);
929
930 6 View Code Duplication
        if ($event->getResponse() !== null) {
931
            log_info('イベントレスポンス返却', [$Order->getId()]);
932
933
            return $event->getResponse();
934
        }
935
936
        // 受注IDをセッションにセット
937 6
        $this->session->set($this->sessionOrderKey, $Order->getId());
938
939
        // メール送信
940 6
        $MailHistory = $this->shoppingService->sendOrderMail($Order);
941
942 6
        $event = new EventArgs(
943
            [
944 6
                'form' => $form,
945 6
                'Order' => $Order,
946 6
                'MailHistory' => $MailHistory,
947
            ],
948 6
            $request
949
        );
950 6
        $this->eventDispatcher->dispatch(EccubeEvents::FRONT_SHOPPING_CONFIRM_COMPLETE, $event);
951
952 6 View Code Duplication
        if ($event->getResponse() !== null) {
953
            log_info('イベントレスポンス返却', [$Order->getId()]);
954
955
            return $event->getResponse();
956
        }
957
958
        // 完了画面表示
959 6
        return $this->redirectToRoute('shopping_complete');
960
    }
961
962 6
    private function createPaymentMethod(Order $Order, FormInterface $form)
963
    {
964 6
        $PaymentMethod = $this->container->get($Order->getPayment()->getMethodClass());
965 6
        $PaymentMethod->setOrder($Order);
966 6
        $PaymentMethod->setFormType($form);
967
968 6
        return $PaymentMethod;
969
    }
970
}
971