Completed
Pull Request — experimental/sf (#3398)
by
unknown
59:00 queued 41:52
created

ShoppingController::checkToCart()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 4.128

Importance

Changes 0
Metric Value
cc 4
nc 3
nop 1
dl 0
loc 18
rs 9.6666
c 0
b 0
f 0
ccs 8
cts 10
cp 0.8
crap 4.128
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\CustomerAddressRepository;
32
use Eccube\Repository\OrderRepository;
33
use Eccube\Service\CartService;
34
use Eccube\Service\OrderHelper;
35
use Eccube\Service\Payment\PaymentDispatcher;
36
use Eccube\Service\ShoppingService;
37
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
38
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
39
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
40
use Symfony\Component\Form\FormError;
41
use Symfony\Component\Form\FormInterface;
42
use Symfony\Component\HttpFoundation\ParameterBag;
43
use Symfony\Component\HttpFoundation\Request;
44
use Symfony\Component\HttpFoundation\Response;
45
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
46
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
47
48
class ShoppingController extends AbstractShoppingController
49
{
50
    /**
51
     * @var BaseInfo
52
     */
53
    protected $BaseInfo;
54
55
    /**
56
     * @var OrderHelper
57
     */
58
    protected $orderHelper;
59
60
    /**
61
     * @var CartService
62
     */
63
    protected $cartService;
64
65
    /**
66
     * @var ShoppingService
67
     */
68
    protected $shoppingService;
69
70
    /**
71
     * @var CustomerAddressRepository
72
     */
73
    protected $customerAddressRepository;
74
75
    /**
76
     * @var ParameterBag
77
     */
78
    protected $parameterBag;
79
80
    /**
81
     * ShoppingController constructor.
82
     *
83
     * @param BaseInfo $BaseInfo
84
     * @param OrderHelper $orderHelper
85
     * @param CartService $cartService
86
     * @param ShoppingService $shoppingService
87
     * @param CustomerAddressRepository $customerAddressRepository
88
     * @param ParameterBag $parameterBag
89
     */
90 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...
91
        BaseInfo $BaseInfo,
92
        OrderHelper $orderHelper,
93
        CartService $cartService,
94
        ShoppingService $shoppingService,
95
        CustomerAddressRepository $customerAddressRepository,
96
        OrderRepository $orderRepository,
97
        ParameterBag $parameterBag
98
    ) {
99 59
        $this->BaseInfo = $BaseInfo;
100 59
        $this->orderHelper = $orderHelper;
101 59
        $this->cartService = $cartService;
102 59
        $this->shoppingService = $shoppingService;
103 59
        $this->customerAddressRepository = $customerAddressRepository;
104 59
        $this->orderRepository = $orderRepository;
105 59
        $this->parameterBag = $parameterBag;
106
    }
107
108
    /**
109
     * 購入画面表示
110
     *
111
     * @Route("/shopping", name="shopping")
112
     * @Template("Shopping/index.twig")
113
     */
114 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...
115
    {
116
        // カートチェック
117 51
        $response = $this->forwardToRoute('shopping_check_to_cart');
118 51
        if ($response->isRedirection() || $response->getContent()) {
119 1
            return $response;
120
        }
121
122
        // 受注情報を初期化
123 50
        $response = $this->forwardToRoute('shopping_initialize_order');
124 50
        if ($response->isRedirection() || $response->getContent()) {
125
            return $response;
126
        }
127
128
        /** @var Order $Order */
129 50
        $Order = $this->parameterBag->get('Order');
130
131
        // 単価集計
132 50
        $flowResult = $this->validatePurchaseFlow($Order);
133
134
        // 明細が丸められる場合に, カートから注文画面へ遷移できなくなるため, 集計の結果を保存する
135 50
        $this->entityManager->flush();
136
137
        // フォームを生成する
138 50
        $this->forwardToRoute('shopping_create_form');
139
140 50
        if ($flowResult->hasWarning() || $flowResult->hasError()) {
141 10
            return $this->redirectToRoute('cart');
142
        }
143
144 45
        $form = $this->parameterBag->get(OrderType::class);
145
146
        return [
147 45
            'form' => $form->createView(),
148 45
            'Order' => $Order,
149
        ];
150
    }
151
152
    /**
153
     * 購入確認画面から, 他の画面へのリダイレクト.
154
     * 配送業者や支払方法、お問い合わせ情報をDBに保持してから遷移する.
155
     *
156
     * @Route("/shopping/redirect", name="shopping_redirect_to")
157
     * @Template("Shopping/index.twig")
158
     */
159 20
    public function redirectTo(Request $request)
160
    {
161
        // カートチェック
162 20
        $response = $this->forwardToRoute('shopping_check_to_cart');
163 20
        if ($response->isRedirection() || $response->getContent()) {
164
            return $response;
165
        }
166
167
        // 受注の存在チェック
168 20
        $response = $this->forwardToRoute('shopping_exists_order');
169 20
        if ($response->isRedirection() || $response->getContent()) {
170
            return $response;
171
        }
172
173
        // フォームの生成
174 20
        $this->forwardToRoute('shopping_create_form');
175 20
        $form = $this->parameterBag->get(OrderType::class);
176 20
        $form->handleRequest($request);
177
178
        // 各種変更ページへリダイレクトする
179 20
        $response = $this->forwardToRoute('shopping_redirect_to_change');
180 20
        if ($response->isRedirection() || $response->getContent()) {
181 8
            return $response;
182
        }
183 12
        $form = $this->parameterBag->get(OrderType::class);
184 12
        $Order = $this->parameterBag->get('Order');
185
186
        return [
187 12
            'form' => $form->createView(),
188 12
            'Order' => $Order,
189
        ];
190
    }
191
192
    /**
193
     * 購入処理
194
     *
195
     * @Route("/shopping/confirm", name="shopping_confirm")
196
     * @Method("POST")
197
     * @Template("Shopping/confirm.twig")
198
     */
199 5
    public function confirm(Request $request)
200
    {
201
        // カートチェック
202 5
        $response = $this->forwardToRoute('shopping_check_to_cart');
203 5
        if ($response->isRedirection() || $response->getContent()) {
204
            return $response;
205
        }
206
207
        // 受注の存在チェック
208 5
        $response = $this->forwardToRoute('shopping_exists_order');
209 5
        if ($response->isRedirection() || $response->getContent()) {
210
            return $response;
211
        }
212
213
        // フォームの生成
214 5
        $this->forwardToRoute('shopping_create_form');
215 5
        $form = $this->parameterBag->get(OrderType::class);
216 5
        $form->handleRequest($request);
217
218 5
        $form = $this->parameterBag->get(OrderType::class);
219 5
        $Order = $this->parameterBag->get('Order');
220
221
        // フォームエラーチェック
222 5
        if (!$form->isValid()) {
223
            $response = $this->forwardToRoute('shopping_redirect_to');
224
225
            return $response;
226
        }
227
228 5
        $flowResult = $this->validatePurchaseFlow($Order);
229 5
        if ($flowResult->hasWarning() || $flowResult->hasError()) {
230
            return $this->redirectToRoute('shopping_error');
231
        }
232
233 5
        $paymentMethod = $this->createPaymentMethod($Order, $form);
234
235 5
        $PaymentResult = $paymentMethod->verify();
236
        // エラーの場合は注文入力画面に戻す?
237 5
        if ($PaymentResult instanceof PaymentResult) {
238
            if (!$PaymentResult->isSuccess()) {
239
                $this->entityManager->getConnection()->rollback();
240
241
                $this->addError($PaymentResult->getErrors());
242
            }
243
244
            $response = $PaymentResult->getResponse();
245
            if ($response && ($response->isRedirection() || $response->getContent())) {
246
                $this->entityManager->flush();
247
248
                return $response;
249
            }
250
        }
251 5
        $this->entityManager->flush();
252
253
        return [
254 5
            'form' => $form->createView(),
255 5
            'Order' => $Order,
256
        ];
257
    }
258
259
    /**
260
     * 購入処理
261
     *
262
     * @Route("/shopping/order", name="shopping_order")
263
     * @Method("POST")
264
     * @Template("Shopping/index.twig")
265
     */
266 6
    public function order(Request $request)
267
    {
268
        // カートチェック
269 6
        $response = $this->forwardToRoute('shopping_check_to_cart');
270 6
        if ($response->isRedirection() || $response->getContent()) {
271
            return $response;
272
        }
273
274
        // 受注の存在チェック
275 6
        $response = $this->forwardToRoute('shopping_exists_order');
276 6
        if ($response->isRedirection() || $response->getContent()) {
277
            return $response;
278
        }
279
280
        // form作成
281
        // FIXME イベントハンドラを外から渡したい
282 6
        $this->forwardToRoute('shopping_create_form');
283
284 6
        $form = $this->parameterBag->get(OrderType::class);
285 6
        $Order = $this->parameterBag->get('Order');
286 6
        $usePoint = $Order->getUsePoint();
287
288 6
        $form->handleRequest($request);
289 6
        $Order->setUsePoint($usePoint);
290
291
        // 受注処理
292 6
        $response = $this->forwardToRoute('shopping_complete_order');
293 6
        if ($response->isRedirection() || $response->getContent()) {
294 6
            return $response;
295
        }
296
297
        log_info('購入チェックエラー', [$Order->getId()]);
298
299
        return [
300
            'form' => $form->createView(),
301
            'Order' => $Order,
302
        ];
303
    }
304
305
    /**
306
     * 支払方法バーリデト
307
     */
308
    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...
309
    {
310
        $data = $form->getData();
311
        $paymentId = $data['payment']->getId();
312
        $shippings = $data['shippings'];
313
        $validCount = count($shippings);
314
        foreach ($shippings as $Shipping) {
315
            $payments = $app['eccube.repository.payment']->findPayments($Shipping->getDelivery());
316
            if ($payments == null) {
317
                continue;
318
            }
319
            foreach ($payments as $payment) {
320
                if ($payment['id'] == $paymentId) {
321
                    $validCount--;
322
                    continue;
323
                }
324
            }
325
        }
326
        if ($validCount == 0) {
327
            return true;
328
        }
329
        $form->get('payment')->addError(new FormError('front.shopping.payment.error'));
330
331
        return false;
332
    }
333
334
    /**
335
     * 購入完了画面表示
336
     *
337
     * @Route("/shopping/complete", name="shopping_complete")
338
     * @Template("Shopping/complete.twig")
339
     */
340 1
    public function complete(Request $request)
341
    {
342
        // 受注IDを取得
343 1
        $orderId = $this->session->get($this->sessionOrderKey);
344
345 1
        if (empty($orderId)) {
346
            return $this->redirectToRoute('homepage');
347
        }
348
349 1
        $Order = $this->orderRepository->find($orderId);
350
351 1
        $event = new EventArgs(
352
            [
353 1
                'Order' => $Order,
354
            ],
355 1
            $request
356
        );
357 1
        $this->eventDispatcher->dispatch(EccubeEvents::FRONT_SHOPPING_COMPLETE_INITIALIZE, $event);
358
359 1
        if ($event->getResponse() !== null) {
360
            return $event->getResponse();
361
        }
362
363
        // 受注に関連するセッションを削除
364 1
        $this->session->remove($this->sessionOrderKey);
365 1
        $this->session->remove($this->sessionKey);
366 1
        $this->session->remove($this->sessionCustomerAddressKey);
367
368 1
        log_info('購入処理完了', [$Order->getId()]);
369
370 1
        $hasNextCart = !empty($this->cartService->getCarts());
371
372
        return [
373 1
            'Order' => $Order,
374 1
            'hasNextCart' => $hasNextCart,
375
        ];
376
    }
377
378
    /**
379
     * お届け先の設定一覧からの選択
380
     *
381
     * @Route("/shopping/shipping/{id}", name="shopping_shipping", requirements={"id" = "\d+"})
382
     * @Template("Shopping/shipping.twig")
383
     */
384
    public function shipping(Request $request, Shipping $Shipping)
385
    {
386
        // カートチェック
387
        $response = $this->forwardToRoute('shopping_check_to_cart');
388
        if ($response->isRedirection() || $response->getContent()) {
389
            return $response;
390
        }
391
392
        // 受注の存在チェック
393
        $response = $this->forwardToRoute('shopping_exists_order');
394
        if ($response->isRedirection() || $response->getContent()) {
395
            return $response;
396
        }
397
398
        // 受注に紐づくShippingかどうかのチェック.
399
        /** @var Order $Order */
400
        $Order = $this->parameterBag->get('Order');
401
        if (!$Order->findShipping($Shipping->getId())) {
402
            throw new NotFoundHttpException();
403
        }
404
405
        $builder = $this->formFactory->createBuilder(CustomerAddressType::class, null, [
406
            'customer' => $this->getUser(),
407
            'shipping' => $Shipping,
408
        ]);
409
410
        $form = $builder->getForm();
411
        $form->handleRequest($request);
412
413
        if ($form->isSubmitted() && $form->isValid()) {
414
            log_info('お届先情報更新開始', [$Shipping->getId()]);
415
416
            /** @var CustomerAddress $CustomerAddress */
417
            $CustomerAddress = $form['addresses']->getData();
418
419
            // お届け先情報を更新
420
            $Shipping->setFromCustomerAddress($CustomerAddress);
421
422
            // 合計金額の再計算
423
            $flowResult = $this->validatePurchaseFlow($Order);
424
            if ($flowResult->hasWarning() || $flowResult->hasError()) {
425
                return $this->redirectToRoute('shopping_error');
426
            }
427
428
            // 配送先を更新
429
            $this->entityManager->flush();
430
431
            $event = new EventArgs(
432
                [
433
                    'Order' => $Order,
434
                    'Shipping' => $Shipping,
435
                ],
436
                $request
437
            );
438
            $this->eventDispatcher->dispatch(EccubeEvents::FRONT_SHOPPING_SHIPPING_COMPLETE, $event);
439
440
            log_info('お届先情報更新完了', [$Shipping->getId()]);
441
442
            return $this->redirectToRoute('shopping');
443
        }
444
445
        return [
446
            'form' => $form->createView(),
447
            'Customer' => $this->getUser(),
448
            'shippingId' => $Shipping->getId(),
449
        ];
450
    }
451
452
    /**
453
     * お届け先の設定(非会員でも使用する)
454
     *
455
     * @Route("/shopping/shipping_edit/{id}", name="shopping_shipping_edit", requirements={"id" = "\d+"})
456
     * @Template("Shopping/shipping_edit.twig")
457
     */
458
    public function shippingEdit(Request $request, $id)
459
    {
460
        // 配送先住所最大値判定
461
        $Customer = $this->getUser();
462
        if ($this->isGranted('IS_AUTHENTICATED_FULLY')) {
463
            $addressCurrNum = count($this->getUser()->getCustomerAddresses());
464
            $addressMax = $this->eccubeConfig['eccube_deliv_addr_max'];
465
            if ($addressCurrNum >= $addressMax) {
466
                throw new NotFoundHttpException(trans('shoppingcontroller.text.error.number_of_address'));
467
            }
468
        }
469
470
        // カートチェック
471
        $response = $this->forwardToRoute('shopping_check_to_cart');
472
        if ($response->isRedirection() || $response->getContent()) {
473
            return $response;
474
        }
475
476
        // 受注の存在チェック
477
        $response = $this->forwardToRoute('shopping_exists_order');
478
        if ($response->isRedirection() || $response->getContent()) {
479
            return $response;
480
        }
481
482
        /** @var Order $Order */
483
        $Order = $this->parameterBag->get('Order');
484
485
        $Shipping = $Order->findShipping($id);
486
        if (!$Shipping) {
487
            throw new NotFoundHttpException(trans('shoppingcontroller.text.error.set_address'));
488
        }
489
        if ($this->isGranted('IS_AUTHENTICATED_FULLY')) {
490
            $Shipping->clearCustomerAddress();
491
        }
492
493
        $CustomerAddress = new CustomerAddress();
494
        if ($this->isGranted('IS_AUTHENTICATED_FULLY')) {
495
            $CustomerAddress->setCustomer($Customer);
0 ignored issues
show
Bug introduced by
It seems like $Customer defined by $this->getUser() on line 461 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...
496
        } else {
497
            $CustomerAddress->setFromShipping($Shipping);
498
        }
499
500
        $builder = $this->formFactory->createBuilder(ShoppingShippingType::class, $CustomerAddress);
501
502
        $event = new EventArgs(
503
            [
504
                'builder' => $builder,
505
                'Order' => $Order,
506
                'Shipping' => $Shipping,
507
                'CustomerAddress' => $CustomerAddress,
508
            ],
509
            $request
510
        );
511
        $this->eventDispatcher->dispatch(EccubeEvents::FRONT_SHOPPING_SHIPPING_EDIT_INITIALIZE, $event);
512
513
        $form = $builder->getForm();
514
515
        $form->handleRequest($request);
516
517
        if ($form->isSubmitted() && $form->isValid()) {
518
            log_info('お届け先追加処理開始', ['id' => $Order->getId(), 'shipping' => $id]);
519
520
            // 会員の場合、お届け先情報を新規登録
521
            $Shipping->setFromCustomerAddress($CustomerAddress);
522
523
            if ($Customer instanceof Customer) {
524
                $this->entityManager->persist($CustomerAddress);
525
                log_info(
526
                    '新規お届け先登録',
527
                    [
528
                        'id' => $Order->getId(),
529
                        'shipping' => $id,
530
                        'customer address' => $CustomerAddress->getId(),
531
                    ]
532
                );
533
            }
534
535
            // 配送料金の設定
536
            $this->shoppingService->setShippingDeliveryFee($Shipping);
537
538
            // 合計金額の再計算
539
            $flowResult = $this->validatePurchaseFlow($Order);
540
            if ($flowResult->hasWarning() || $flowResult->hasError()) {
541
                return $this->redirectToRoute('shopping_error');
542
            }
543
544
            // 配送先を更新
545
            $this->entityManager->flush();
546
547
            $event = new EventArgs(
548
                [
549
                    'form' => $form,
550
                    'Shipping' => $Shipping,
551
                    'CustomerAddress' => $CustomerAddress,
552
                ],
553
                $request
554
            );
555
            $this->eventDispatcher->dispatch(EccubeEvents::FRONT_SHOPPING_SHIPPING_EDIT_COMPLETE, $event);
556
557
            log_info('お届け先追加処理完了', ['id' => $Order->getId(), 'shipping' => $id]);
558
559
            return $this->redirectToRoute('shopping');
560
        }
561
562
        return [
563
            'form' => $form->createView(),
564
            'shippingId' => $id,
565
        ];
566
    }
567
568
    /**
569
     * ログイン
570
     *
571
     * @Route("/shopping/login", name="shopping_login")
572
     * @Template("Shopping/login.twig")
573
     */
574 2
    public function login(Request $request, AuthenticationUtils $authenticationUtils)
575
    {
576 2
        if ($this->isGranted('IS_AUTHENTICATED_FULLY')) {
577
            return $this->redirectToRoute('shopping');
578
        }
579
580
        /* @var $form \Symfony\Component\Form\FormInterface */
581 2
        $builder = $this->formFactory->createNamedBuilder('', CustomerLoginType::class);
582
583 2
        if ($this->isGranted('IS_AUTHENTICATED_REMEMBERED')) {
584
            $Customer = $this->getUser();
585
            if ($Customer) {
586
                $builder->get('login_email')->setData($Customer->getEmail());
587
            }
588
        }
589
590 2
        $event = new EventArgs(
591
            [
592 2
                'builder' => $builder,
593
            ],
594 2
            $request
595
        );
596 2
        $this->eventDispatcher->dispatch(EccubeEvents::FRONT_SHOPPING_LOGIN_INITIALIZE, $event);
597
598 2
        $form = $builder->getForm();
599
600
        return [
601 2
            'error' => $authenticationUtils->getLastAuthenticationError(),
602 2
            'form' => $form->createView(),
603
        ];
604
    }
605
606
    /**
607
     * 購入エラー画面表示
608
     *
609
     * @Route("/shopping/error", name="shopping_error")
610
     * @Template("Shopping/shopping_error.twig")
611
     */
612 1
    public function shoppingError(Request $request)
613
    {
614 1
        $event = new EventArgs(
615 1
            [],
616 1
            $request
617
        );
618 1
        $this->eventDispatcher->dispatch(EccubeEvents::FRONT_SHOPPING_SHIPPING_ERROR_COMPLETE, $event);
619
620 1
        if ($event->getResponse() !== null) {
621
            return $event->getResponse();
622
        }
623
624 1
        return [];
625
    }
626
627
    /**
628
     * カート画面のチェック
629
     *
630
     * @ForwardOnly
631
     * @Route("/shopping/check_to_cart", name="shopping_check_to_cart")
632
     */
633 55
    public function checkToCart(Request $request)
634
    {
635 55
        $Cart = $this->cartService->getCart();
636 55
        if ($Cart && count($Cart->getCartItems()) > 0) {
637 53
            $divide = $request->getSession()->get('cart.divide');
638 53
            if ($divide) {
639
                log_info('種別が異なる商品がカートと結合されたためカート画面にリダイレクト');
640
641
                return $this->redirectToRoute('cart');
642
            }
643
644 53
            return new Response();
645
        }
646 2
        log_info('カートに商品が入っていないためショッピングカート画面にリダイレクト');
647
648
        // カートが存在しない時はエラー
649 2
        return $this->redirectToRoute('cart');
650
    }
651
652
    /**
653
     * 受注情報を初期化する.
654
     *
655
     * @ForwardOnly
656
     * @Route("/shopping/initialize_order", name="shopping_initialize_order")
657
     */
658 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...
659
    {
660
        // 購入処理中の受注情報を取得
661 50
        $Order = $this->shoppingService->getOrder(OrderStatus::PROCESSING);
662
663
        // 初回アクセス(受注情報がない)の場合は, 受注情報を作成
664 50
        if (is_null($Order)) {
665
            // 未ログインの場合, ログイン画面へリダイレクト.
666 34
            if (!$this->isGranted('IS_AUTHENTICATED_FULLY')) {
667
                // 非会員でも一度会員登録されていればショッピング画面へ遷移
668
                $Customer = $this->shoppingService->getNonMember($this->sessionKey);
669
670
                if (is_null($Customer)) {
671
                    log_info('未ログインのためログイン画面にリダイレクト');
672
673
                    return $this->redirectToRoute('shopping_login');
674
                }
675
            } else {
676 34
                $Customer = $this->getUser();
677
            }
678
679
            try {
680
                // 受注情報を作成
681
                //$Order = $app['eccube.service.shopping']->createOrder($Customer);
682 34
                $Order = $this->orderHelper->createProcessingOrder(
683 34
                    $Customer,
684 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...
685
                );
686 34
                $this->cartService->setPreOrderId($Order->getPreOrderId());
687 34
                $this->cartService->save();
688
            } catch (CartException $e) {
689
                log_error('初回受注情報作成エラー', [$e->getMessage()]);
690
                $this->addRequestError($e->getMessage());
691
692
                return $this->redirectToRoute('cart');
693
            }
694
695
            // セッション情報を削除
696 34
            $this->session->remove($this->sessionOrderKey);
697
        }
698
699
        // 受注関連情報を最新状態に更新
700 50
        $this->entityManager->refresh($Order);
701
702 50
        $this->parameterBag->set('Order', $Order);
703
704 50
        return new Response();
705
    }
706
707
    /**
708
     * フォームを作成し, イベントハンドラを設定する
709
     *
710
     * @ForwardOnly
711
     * @Route("/shopping/create_form", name="shopping_create_form")
712
     */
713 50
    public function createShoppingForm(Request $request)
714
    {
715 50
        $Order = $this->parameterBag->get('Order');
716
        // フォームの生成
717 50
        $builder = $this->formFactory->createBuilder(OrderType::class, $Order);
718
719 50
        $event = new EventArgs(
720
                [
721 50
                    'builder' => $builder,
722 50
                    'Order' => $Order,
723
                ],
724 50
                $request
725
            );
726 50
        $this->eventDispatcher->dispatch(EccubeEvents::FRONT_SHOPPING_INDEX_INITIALIZE, $event);
727
728 50
        $form = $builder->getForm();
729
730 50
        $this->parameterBag->set(OrderType::class, $form);
731
732 50
        return new Response();
733
    }
734
735
    /**
736
     * mode に応じて各変更ページへリダイレクトする.
737
     *
738
     * @ForwardOnly
739
     * @Route("/shopping/redirect_to_change", name="shopping_redirect_to_change")
740
     */
741 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...
742
    {
743 20
        $form = $this->parameterBag->get(OrderType::class);
744
745
        // requestのバインド後、Calculatorに再集計させる
746
        //$app['eccube.service.calculate']($Order, $Order->getCustomer())->calculate();
747
748
        // 支払い方法の変更や配送業者の変更があった場合はDBに保持する.
749 20
        if ($form->isSubmitted() && $form->isValid()) {
750
            // POSTされたデータをDBに保持.
751 8
            $this->entityManager->flush();
752
753 8
            $mode = $form['mode']->getData();
754 8
            switch ($mode) {
755 7
                case 'shipping_change':
756
                    // お届け先設定一覧へリダイレクト
757 1
                    $param = $form['param']->getData();
758
759 1
                    return $this->redirectToRoute('shopping_shipping', ['id' => $param]);
760 7
                case 'shipping_edit_change':
761
                    // お届け先設定一覧へリダイレクト
762
                    $param = $form['param']->getData();
763
764
                    return $this->redirectToRoute('shopping_shipping_edit', ['id' => $param]);
765 7
                case 'shipping_multiple_change':
766
                    // 複数配送設定へリダイレクト
767
                    return $this->redirectToRoute('shopping_shipping_multiple');
768 7
                case 'payment':
769 7
                case 'delivery':
770
                default:
771 7
                    return $this->redirectToRoute('shopping');
772
            }
773
        }
774
775 12
        return new Response();
776
    }
777
778
    /**
779
     * 受注の存在チェック
780
     *
781
     * @ForwardOnly
782
     * @Route("/shopping/exists_order", name="shopping_exists_order")
783
     */
784 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...
785
    {
786 26
        $Order = $this->shoppingService->getOrder(OrderStatus::PROCESSING);
787 26
        if (!$Order) {
788
            log_info('購入処理中の受注情報がないため購入エラー');
789
            $this->addError('front.shopping.order.error');
790
791
            return $this->redirectToRoute('shopping_error');
792
        }
793 26
        $this->parameterBag->set('Order', $Order);
794
795 26
        return new Response();
796
    }
797
798
    /**
799
     * 受注完了処理
800
     *
801
     * @ForwardOnly
802
     * @Route("/shopping/complete_order", name="shopping_complete_order")
803
     */
804 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...
805
    {
806 6
        $form = $this->parameterBag->get(OrderType::class);
807
808 6
        if ($form->isSubmitted() && $form->isValid()) {
809
            /** @var Order $Order */
810 6
            $Order = $form->getData();
811 6
            log_info('購入処理開始', [$Order->getId()]);
812
813
            // トランザクション制御
814 6
            $em = $this->entityManager;
815 6
            $em->getConnection()->beginTransaction();
816
            try {
817
                // お問い合わせ、配送時間などのフォーム項目をセット
818
                // FormTypeで更新されるため不要
819
                //$app['eccube.service.shopping']->setFormData($Order, $data);
820
821 6
                $flowResult = $this->validatePurchaseFlow($Order);
822 6
                if ($flowResult->hasWarning() || $flowResult->hasError()) {
823
                    // TODO エラーメッセージ
824
                    throw new ShoppingException();
825
                }
826
827 6
                $paymentMethod = $this->createPaymentMethod($Order, $form);
828
829
                // 必要に応じて別のコントローラへ forward or redirect(移譲)
830 6
                $dispatcher = $paymentMethod->apply(); // 決済処理中.
831
                // 一旦、決済処理中になった後は、購入処理中に戻せない。キャンセル or 購入完了の仕様とする
832
                // ステータス履歴も保持しておく? 在庫引き当ての仕様もセットで。
833 6
                if ($dispatcher instanceof PaymentDispatcher) {
834
                    $response = $dispatcher->getResponse();
835
                    $this->entityManager->flush();
836
                    $this->entityManager->commit();
837
838
                    if ($response && ($response->isRedirection() || $response->getContent())) {
839
                        return $response;
840
                    }
841
842
                    if ($dispatcher->isForward()) {
843
                        return $this->forwardToRoute($dispatcher->getRoute(), $dispatcher->getPathParameters(), $dispatcher->getQueryParameters());
844
                    } else {
845
                        return $this->redirectToRoute($dispatcher->getRoute(), array_merge($dispatcher->getPathParameters(), $dispatcher->getQueryParameters()));
846
                    }
847
                }
848
849
                // 決済実行
850 6
                $response = $this->forwardToRoute('shopping_do_checkout_order');
851 6
                $this->entityManager->flush();
852 6
                $this->entityManager->commit();
853
854 6
                if ($response->isRedirection() || $response->getContent()) {
855
                    return $response;
856
                }
857
858 6
                log_info('購入処理完了', [$Order->getId()]);
859
            } catch (ShoppingException $e) {
860
                log_error('購入エラー', [$e->getMessage()]);
861
862
                $this->entityManager->getConnection()->rollback();
863
864
                $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...
865
                $this->addError($e->getMessage());
866
867
                return $this->redirectToRoute('shopping_error');
868
            } catch (\Exception $e) {
869
                log_error('予期しないエラー', [$e->getMessage()]);
870
871
                $this->entityManager->getConnection()->rollback();
872
873
                $this->addError('front.shopping.system.error');
874
875
                return $this->redirectToRoute('shopping_error');
876
            }
877
878 6
            return $this->forwardToRoute('shopping_after_complete');
879
        }
880
881
        return new Response();
882
    }
883
884
    /**
885
     * 決済完了処理
886
     *
887
     * @ForwardOnly
888
     * @Route("/shopping/do_checkout_order", name="shopping_do_checkout_order")
889
     */
890 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...
891
    {
892 6
        $form = $this->parameterBag->get(OrderType::class);
893 6
        $Order = $this->parameterBag->get('Order');
894
895 6
        $paymentMethod = $this->createPaymentMethod($Order, $form);
896
897
        // 決済実行
898 6
        $PaymentResult = $paymentMethod->checkout();
899 6
        $response = $PaymentResult->getResponse();
900 6
        if ($response && ($response->isRedirection() || $response->getContent())) {
901
            return $response;
902
        }
903
904 6
        if (!$PaymentResult->isSuccess()) {
905
            $this->entityManager->getConnection()->rollback();
906
907
            $this->addError($PaymentResult->getErrors());
908
        }
909
910 6
        return new Response();
911
    }
912
913
    /**
914
     * 受注完了の後処理
915
     *
916
     * @ForwardOnly
917
     * @Route("/shopping/after_complete", name="shopping_after_complete")
918
     */
919 6
    public function afterComplete(Request $request)
920
    {
921 6
        $form = $this->parameterBag->get(OrderType::class);
922 6
        $Order = $this->parameterBag->get('Order');
923
924
        // カート削除
925 6
        $this->cartService->clear();
926
927 6
        $event = new EventArgs(
928
            [
929 6
                'form' => $form,
930 6
                'Order' => $Order,
931
            ],
932 6
            $request
933
        );
934 6
        $this->eventDispatcher->dispatch(EccubeEvents::FRONT_SHOPPING_CONFIRM_PROCESSING, $event);
935
936 6 View Code Duplication
        if ($event->getResponse() !== null) {
937
            log_info('イベントレスポンス返却', [$Order->getId()]);
938
939
            return $event->getResponse();
940
        }
941
942
        // 受注IDをセッションにセット
943 6
        $this->session->set($this->sessionOrderKey, $Order->getId());
944
945
        // メール送信
946 6
        $MailHistory = $this->shoppingService->sendOrderMail($Order);
947
948 6
        $event = new EventArgs(
949
            [
950 6
                'form' => $form,
951 6
                'Order' => $Order,
952 6
                'MailHistory' => $MailHistory,
953
            ],
954 6
            $request
955
        );
956 6
        $this->eventDispatcher->dispatch(EccubeEvents::FRONT_SHOPPING_CONFIRM_COMPLETE, $event);
957
958 6 View Code Duplication
        if ($event->getResponse() !== null) {
959
            log_info('イベントレスポンス返却', [$Order->getId()]);
960
961
            return $event->getResponse();
962
        }
963
964
        // 完了画面表示
965 6
        return $this->redirectToRoute('shopping_complete');
966
    }
967
968 6
    private function createPaymentMethod(Order $Order, FormInterface $form)
969
    {
970 6
        $PaymentMethod = $this->container->get($Order->getPayment()->getMethodClass());
971 6
        $PaymentMethod->setOrder($Order);
972 6
        $PaymentMethod->setFormType($form);
973
974 6
        return $PaymentMethod;
975
    }
976
}
977