Completed
Pull Request — 4.0 (#3650)
by chihiro
08:30 queued 01:16
created

ShoppingController   F

Complexity

Total Complexity 81

Size/Duplication

Total Lines 739
Duplicated Lines 7.71 %

Coupling/Cohesion

Components 1
Dependencies 29

Test Coverage

Coverage 51.17%

Importance

Changes 0
Metric Value
dl 57
loc 739
ccs 153
cts 299
cp 0.5117
rs 1.861
c 0
b 0
f 0
wmc 81
lcom 1
cbo 29

13 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 11 1
B index() 6 41 6
B redirectTo() 6 66 9
C confirm() 23 79 13
C checkout() 6 102 11
A complete() 0 39 3
B shipping() 0 64 8
C shippingEdit() 0 79 10
A login() 0 31 4
A error() 0 21 3
A createPaymentMethod() 0 8 1
B executeApply() 0 32 6
B executeCheckout() 16 24 6

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\Entity\CustomerAddress;
17
use Eccube\Entity\Order;
18
use Eccube\Entity\Shipping;
19
use Eccube\Event\EccubeEvents;
20
use Eccube\Event\EventArgs;
21
use Eccube\Exception\ShoppingException;
22
use Eccube\Form\Type\Front\CustomerLoginType;
23
use Eccube\Form\Type\Front\ShoppingShippingType;
24
use Eccube\Form\Type\Shopping\CustomerAddressType;
25
use Eccube\Form\Type\Shopping\OrderType;
26
use Eccube\Repository\OrderRepository;
27
use Eccube\Service\CartService;
28
use Eccube\Service\MailService;
29
use Eccube\Service\OrderHelper;
30
use Eccube\Service\Payment\PaymentDispatcher;
31
use Eccube\Service\Payment\PaymentMethodInterface;
32
use Eccube\Service\PurchaseFlow\PurchaseContext;
33
use Eccube\Service\PurchaseFlow\PurchaseFlow;
34
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
35
use Symfony\Component\Form\FormInterface;
36
use Symfony\Component\HttpFoundation\Request;
37
use Symfony\Component\Routing\Annotation\Route;
38
use Symfony\Component\Routing\RouterInterface;
39
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
40
41
class ShoppingController extends AbstractShoppingController
42
{
43
    /**
44
     * @var CartService
45
     */
46
    protected $cartService;
47
48
    /**
49
     * @var MailService
50
     */
51
    protected $mailService;
52
53
    /**
54
     * @var OrderHelper
55
     */
56
    protected $orderHelper;
57
58
    /**
59
     * @var OrderRepository
60
     */
61
    protected $orderRepository;
62
63
    public function __construct(
64
        CartService $cartService,
65
        MailService $mailService,
66
        OrderRepository $orderRepository,
67
        OrderHelper $orderHelper
68
    ) {
69
        $this->cartService = $cartService;
70
        $this->mailService = $mailService;
71
        $this->orderRepository = $orderRepository;
72
        $this->orderHelper = $orderHelper;
73
    }
74
75
    /**
76
     * 注文手続き画面を表示する
77
     *
78
     * 未ログインまたはRememberMeログインの場合はログイン画面に遷移させる.
79
     * ただし、非会員でお客様情報を入力済の場合は遷移させない.
80
     *
81
     * カート情報から受注データを生成し, `pre_order_id`でカートと受注の紐付けを行う.
82
     * 既に受注が生成されている場合(pre_order_idで取得できる場合)は, 受注の生成を行わずに画面を表示する.
83
     *
84
     * purchaseFlowの集計処理実行後, warning/errorがあればカートに戻す.
85
     *
86
     * @Route("/shopping", name="shopping")
87
     * @Template("Shopping/index.twig")
88
     */
89
    public function index()
90
    {
91 59
        // ログイン状態のチェック.
92
        if ($this->orderHelper->isLoginRequired()) {
93
            log_info('[注文手続] 未ログインもしくはRememberMeログインのため, ログイン画面に遷移します.');
94
95
            return $this->redirectToRoute('shopping_login');
96
        }
97
98
        // カートチェック.
99
        $Cart = $this->cartService->getCart();
100 59
        if (!$this->orderHelper->verifyCart($Cart)) {
101 59
            log_info('[注文手続] カートが購入フローへ遷移できない状態のため, カート画面に遷移します.');
102 59
103 59
            return $this->redirectToRoute('cart');
104 59
        }
105 59
106 59
        // 受注の初期化.
107
        log_info('[注文手続] 受注の初期化処理を開始します.');
108
        $Customer = $this->getUser() ? $this->getUser() : $this->orderHelper->getNonMember();
109
        $Order = $this->orderHelper->initializeOrder($Cart, $Customer);
0 ignored issues
show
Bug introduced by
It seems like $Cart defined by $this->cartService->getCart() on line 99 can be null; however, Eccube\Service\OrderHelper::initializeOrder() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
110
111
        // 集計処理.
112
        log_info('[注文手続] 集計処理を開始します.', [$Order->getId()]);
113
        $flowResult = $this->validatePurchaseFlow($Order);
114
        $this->entityManager->flush();
115 51
116 View Code Duplication
        if ($flowResult->hasWarning() || $flowResult->hasError()) {
117
            log_info('[注文手続] WarningもしくはErrorが発生したためカート画面へ遷移します.',
118 51
                [$flowResult->getWarning(), $flowResult->getErrors()]);
119 51
120 1
            return $this->redirectToRoute('cart');
121
        }
122
123
        $form = $this->createForm(OrderType::class, $Order);
124 50
125 50
        return [
126
            'form' => $form->createView(),
127
            'Order' => $Order,
128
        ];
129
    }
130 50
131
    /**
132
     * 他画面への遷移を行う.
133 50
     *
134
     * お届け先編集画面など, 他画面へ遷移する際に, フォームの値をDBに保存してからリダイレクトさせる.
135
     * フォームの`redirect_to`パラメータの値にリダイレクトを行う.
136 50
     * `redirect_to`パラメータはpath('遷移先のルーティング')が渡される必要がある.
137
     *
138
     * 外部のURLやPathを渡された場合($router->matchで展開出来ない場合)は, 購入エラーとする.
139 50
     *
140
     * プラグインやカスタマイズでこの機能を使う場合は, twig側で以下のように記述してください.
141 50
     *
142 10
     * <button data-redirect-target="click" data-path="path('ルーティング')">更新する</button>
143
     *
144
     * data-redirect-targetは, click/change/blur等のイベント名を指定してください。
145 45
     * data-pathは任意のパラメータです. 指定しない場合, 注文手続き画面へリダイレクトします.
146
     *
147
     * @Route("/shopping/redirect_to", name="shopping_redirect_to", methods={"POST"})
148 45
     * @Template("Shopping/index.twig")
149 45
     */
150
    public function redirectTo(Request $request, RouterInterface $router)
151
    {
152
        // ログイン状態のチェック.
153
        if ($this->orderHelper->isLoginRequired()) {
154
            log_info('[リダイレクト] 未ログインもしくはRememberMeログインのため, ログイン画面に遷移します.');
155
156
            return $this->redirectToRoute('shopping_login');
157
        }
158
159
        // 受注の存在チェック.
160 20
        $preOrderId = $this->cartService->getPreOrderId();
161
        $Order = $this->orderHelper->getPurchaseProcessingOrder($preOrderId);
162
        if (!$Order) {
163 20
            log_info('[リダイレクト] 購入処理中の受注が存在しません.');
164 20
165
            return $this->redirectToRoute('shopping_error');
166
        }
167
168
        $form = $this->createForm(OrderType::class, $Order);
169 20
        $form->handleRequest($request);
170 20
171
        if ($form->isSubmitted() && $form->isValid()) {
172
            log_info('[リダイレクト] 集計処理を開始します.', [$Order->getId()]);
173
            $flowResult = $this->validatePurchaseFlow($Order);
174
            $this->entityManager->flush();
175 20
176 20 View Code Duplication
            if ($flowResult->hasWarning() || $flowResult->hasError()) {
177 20
                log_info('[リダイレクト] WarningもしくはErrorが発生したためカート画面へ遷移します.',
178
                    [$flowResult->getWarning(), $flowResult->getErrors()]);
179
180 20
                return $this->redirectToRoute('shopping_error');
181 20
            }
182 8
183
            $redirectTo = $form['redirect_to']->getData();
184 12
            if (empty($redirectTo)) {
185 12
                log_info('[リダイレクト] リダイレクト先未指定のため注文手続き画面へ遷移します.');
186
187
                return $this->redirectToRoute('shopping');
188 12
            }
189 12
190
            try {
191
                // リダイレクト先のチェック.
192
                $result = $router->match($redirectTo);
193
                // パラメータのみ抽出
194
                $params = array_filter($result, function ($key) {
195
                    return 0 !== \strpos($key, '_');
196
                }, ARRAY_FILTER_USE_KEY);
197
198
                log_info('[リダイレクト] リダイレクトを実行します.', [$result['_route'], $params]);
199
200 5
                // pathからurlを再構築してリダイレクト.
201
                return $this->redirectToRoute($result['_route'], $params);
202
            } catch (\Exception $e) {
203 5
                log_info('[リダイレクト] URLの形式が不正です', [$redirectTo, $e->getMessage()]);
204 5
205
                return $this->redirectToRoute('shopping_error');
206
            }
207
        }
208
209 5
        log_info('[リダイレクト] フォームエラーのため, 注文手続き画面を表示します.', [$Order->getId()]);
210 5
211
        return [
212
            'form' => $form->createView(),
213
            'Order' => $Order,
214
        ];
215 5
    }
216 5
217 5
    /**
218
     * 注文確認画面を表示する.
219 5
     *
220 5
     * ここではPaymentMethod::verifyがコールされます.
221
     * PaymentMethod::verifyではクレジットカードの有効性チェック等, 注文手続きを進められるかどうかのチェック処理を行う事を想定しています.
222 5
     * PaymentMethod::verifyでエラーが発生した場合は, 注文手続き画面へリダイレクトします.
223 5
     *
224
     * @Route("/shopping/confirm", name="shopping_confirm", methods={"POST"})
225
     * @Template("Shopping/confirm.twig")
226
     */
227 5
    public function confirm(Request $request)
228
    {
229 5
        // ログイン状態のチェック.
230
        if ($this->orderHelper->isLoginRequired()) {
231 5
            log_info('[注文確認] 未ログインもしくはRememberMeログインのため, ログイン画面に遷移します.');
232
233
            return $this->redirectToRoute('shopping_login');
234
        }
235
236
        // 受注の存在チェック
237
        $preOrderId = $this->cartService->getPreOrderId();
238
        $Order = $this->orderHelper->getPurchaseProcessingOrder($preOrderId);
239
        if (!$Order) {
240
            log_info('[注文確認] 購入処理中の受注が存在しません.', [$preOrderId]);
241
242
            return $this->redirectToRoute('shopping_error');
243
        }
244
245 5
        $form = $this->createForm(OrderType::class, $Order);
246
        $form->handleRequest($request);
247
248 5
        if ($form->isSubmitted() && $form->isValid()) {
249 5
            log_info('[注文確認] 集計処理を開始します.', [$Order->getId()]);
250
            $flowResult = $this->validatePurchaseFlow($Order);
251
            $this->entityManager->flush();
252
253 View Code Duplication
            if ($flowResult->hasWarning() || $flowResult->hasError()) {
254
                log_info('[注文確認] WarningもしくはErrorが発生したためカート画面へ遷移します.',
255
                    [$flowResult->getWarning(), $flowResult->getErrors()]);
256
257
                return $this->redirectToRoute('shopping_error');
258
            }
259
260 6
            log_info('[注文確認] PaymentMethod::verifyを実行します.', [$Order->getPayment()->getMethodClass()]);
261
            $paymentMethod = $this->createPaymentMethod($Order, $form);
262
            $PaymentResult = $paymentMethod->verify();
263 6
264 6
            if ($PaymentResult) {
265 View Code Duplication
                if (!$PaymentResult->isSuccess()) {
266
                    $this->entityManager->rollback();
267
                    foreach ($PaymentResult->getErrors() as $error) {
268
                        $this->addError($error);
269 6
                    }
270 6
271
                    log_info('[注文確認] PaymentMethod::verifyのエラーのため, 注文手続き画面へ遷移します.', [$PaymentResult->getErrors()]);
272
273
                    return $this->redirectToRoute('shopping');
274
                }
275
276 6
                $response = $PaymentResult->getResponse();
277 View Code Duplication
                if ($response && ($response->isRedirection() || $response->getContent())) {
278 6
                    $this->entityManager->flush();
279 6
280 6
                    log_info('[注文確認] PaymentMethod::verifyが指定したレスポンスを表示します.');
281
282 6
                    return $response;
283 6
                }
284
            }
285
286 6
            $this->entityManager->flush();
287 6
288 6
            log_info('[注文確認] 注文確認画面を表示します.');
289
290
            return [
291
                'form' => $form->createView(),
292
                'Order' => $Order,
293
            ];
294
        }
295
296
        log_info('[注文確認] フォームエラーのため, 注文手続画面を表示します.', [$Order->getId()]);
297
298
        // FIXME @Templateの差し替え.
299
        $request->attributes->set('_template', new Template(['template' => 'shopping/index.twig']));
300
301
        return [
302
            'form' => $form->createView(),
303
            'Order' => $Order,
304
        ];
305
    }
306
307
    /**
308
     * 注文処理を行う.
309
     *
310
     * 決済プラグインによる決済処理および注文の確定処理を行います.
311
     *
312
     * @Route("/shopping/checkout", name="shopping_checkout", methods={"POST"})
313
     * @Template("Shopping/confirm.twig")
314
     */
315
    public function checkout(Request $request)
316
    {
317
        // ログイン状態のチェック.
318
        if ($this->orderHelper->isLoginRequired()) {
319
            log_info('[注文処理] 未ログインもしくはRememberMeログインのため, ログイン画面に遷移します.');
320
321
            return $this->redirectToRoute('shopping_login');
322
        }
323
324
        // 受注の存在チェック
325
        $preOrderId = $this->cartService->getPreOrderId();
326
        $Order = $this->orderHelper->getPurchaseProcessingOrder($preOrderId);
327
        if (!$Order) {
328
            log_info('[注文処理] 購入処理中の受注が存在しません.', [$preOrderId]);
329
330
            return $this->redirectToRoute('shopping_error');
331
        }
332
333
        // フォームの生成.
334 1
        $form = $this->createForm(OrderType::class, $Order);
335
        $form->handleRequest($request);
336
337 1
        if ($form->isSubmitted() && $form->isValid()) {
338
            log_info('[注文処理] 注文処理を開始します.', [$Order->getId()]);
339 1
340
            try {
341
                /*
342
                 * 集計処理
343 1
                 */
344
                log_info('[注文処理] 集計処理を開始します.', [$Order->getId()]);
345 1
                $flowResult = $this->validatePurchaseFlow($Order);
346
                $this->entityManager->flush();
347 1
348 View Code Duplication
                if ($flowResult->hasWarning() || $flowResult->hasError()) {
349 1
                    log_info('[注文処理] WarningもしくはErrorが発生したためカート画面へ遷移します.',
350
                        [$flowResult->getWarning(), $flowResult->getErrors()]);
351 1
352
                    return $this->redirectToRoute('shopping_error');
353 1
                }
354
355
                log_info('[注文処理] PaymentMethodを取得します.', [$Order->getPayment()->getMethodClass()]);
356
                $paymentMethod = $this->createPaymentMethod($Order, $form);
357
358 1
                /*
359 1
                 * 決済実行(前処理)
360 1
                 */
361
                log_info('[注文処理] PaymentMethod::applyを実行します.');
362 1
                if ($response = $this->executeApply($paymentMethod)) {
363
                    return $response;
364 1
                }
365
366
                /*
367 1
                 * 決済実行
368 1
                 *
369
                 * PaymentMethod::checkoutでは決済処理が行われ, 正常に処理出来た場合はPurchaseFlow::commitがコールされます.
370
                 */
371
                log_info('[注文処理] PaymentMethod::checkoutを実行します.');
372
                if ($response = $this->executeCheckout($paymentMethod)) {
373
                    return $response;
374
                }
375
376
                $this->entityManager->flush();
377
378
                log_info('[注文処理] 注文処理が完了しました.', [$Order->getId()]);
379
            } catch (ShoppingException $e) {
380
                log_error('[注文処理] 購入エラーが発生しました.', [$e->getMessage()]);
381
382
                $this->entityManager->rollback();
383
384
                $this->addError($e->getMessage());
385
386
                return $this->redirectToRoute('shopping_error');
387
            } catch (\Exception $e) {
388
                log_error('[注文確認] 予期しないエラーが発生しました.', [$e->getMessage()]);
389
390
                $this->entityManager->rollback();
391
392
                $this->addError('front.shopping.system_error');
393
394
                return $this->redirectToRoute('shopping_error');
395
            }
396
397
            // カート削除
398
            log_info('[注文処理] カートをクリアします.', [$Order->getId()]);
399
            $this->cartService->clear();
400
401
            // 受注IDをセッションにセット
402
            $this->session->set(OrderHelper::SESSION_ORDER_ID, $Order->getId());
403
404
            // メール送信
405
            log_info('[注文処理] 注文メールの送信を行います.', [$Order->getId()]);
406
            $this->mailService->sendOrderMail($Order);
407
408
            log_info('[注文処理] 注文処理が完了しました. 購入完了画面へ遷移します.', [$Order->getId()]);
409
410
            return $this->redirectToRoute('shopping_complete');
411
        }
412
413
        log_info('[注文処理] フォームエラーのため, 購入エラー画面へ遷移します.', [$Order->getId()]);
414
415
        return $this->redirectToRoute('shopping_error');
416
    }
417
418
    /**
419
     * 購入完了画面を表示する.
420
     *
421
     * @Route("/shopping/complete", name="shopping_complete")
422
     * @Template("Shopping/complete.twig")
423
     */
424
    public function complete(Request $request)
425
    {
426
        log_info('[注文完了] 注文完了画面を表示します.');
427
428
        // 受注IDを取得
429
        $orderId = $this->session->get(OrderHelper::SESSION_ORDER_ID);
430
431
        if (empty($orderId)) {
432
            log_info('[注文完了] 受注IDを取得できないため, トップページへ遷移します.');
433
434
            return $this->redirectToRoute('homepage');
435
        }
436
437
        $Order = $this->orderRepository->find($orderId);
438
439
        $event = new EventArgs(
440
            [
441
                'Order' => $Order,
442
            ],
443
            $request
444
        );
445
        $this->eventDispatcher->dispatch(EccubeEvents::FRONT_SHOPPING_COMPLETE_INITIALIZE, $event);
446
447
        if ($event->getResponse() !== null) {
448
            return $event->getResponse();
449
        }
450
451
        log_info('[注文完了] 購入フローのセッションをクリアします. ');
452
        $this->orderHelper->removeSession();
453
454
        $hasNextCart = !empty($this->cartService->getCarts());
455
456
        log_info('[注文完了] 注文完了画面を表示しました. ', [$hasNextCart]);
457
458
        return [
459
            'Order' => $Order,
460
            'hasNextCart' => $hasNextCart,
461
        ];
462
    }
463
464
    /**
465
     * お届け先選択画面.
466
     *
467
     * 会員ログイン時, お届け先を選択する画面を表示する
468
     * 非会員の場合はこの画面は使用しない。
469
     *
470
     * @Route("/shopping/shipping/{id}", name="shopping_shipping", requirements={"id" = "\d+"})
471
     * @Template("Shopping/shipping.twig")
472
     */
473
    public function shipping(Request $request, Shipping $Shipping)
474
    {
475
        // ログイン状態のチェック.
476
        if ($this->orderHelper->isLoginRequired()) {
477
            return $this->redirectToRoute('shopping_login');
478
        }
479
480
        // 受注の存在チェック
481
        $preOrderId = $this->cartService->getPreOrderId();
482
        $Order = $this->orderHelper->getPurchaseProcessingOrder($preOrderId);
483
        if (!$Order) {
484
            return $this->redirectToRoute('shopping_error');
485
        }
486
487
        // 受注に紐づくShippingかどうかのチェック.
488
        if (!$Order->findShipping($Shipping->getId())) {
489
            return $this->redirectToRoute('shopping_error');
490
        }
491
492
        $builder = $this->formFactory->createBuilder(CustomerAddressType::class, null, [
493
            'customer' => $this->getUser(),
494
            'shipping' => $Shipping,
495
        ]);
496
497
        $form = $builder->getForm();
498
        $form->handleRequest($request);
499
500
        if ($form->isSubmitted() && $form->isValid()) {
501
            log_info('お届先情報更新開始', [$Shipping->getId()]);
502
503
            /** @var CustomerAddress $CustomerAddress */
504
            $CustomerAddress = $form['addresses']->getData();
505
506
            // お届け先情報を更新
507
            $Shipping->setFromCustomerAddress($CustomerAddress);
508
509
            // 合計金額の再計算
510
            $flowResult = $this->validatePurchaseFlow($Order);
511
            $this->entityManager->flush();
512
513
            if ($flowResult->hasWarning() || $flowResult->hasError()) {
514
                return $this->redirectToRoute('shopping_error');
515
            }
516
517
            $event = new EventArgs(
518
                [
519
                    'Order' => $Order,
520
                    'Shipping' => $Shipping,
521
                ],
522
                $request
523
            );
524
            $this->eventDispatcher->dispatch(EccubeEvents::FRONT_SHOPPING_SHIPPING_COMPLETE, $event);
525
526
            log_info('お届先情報更新完了', [$Shipping->getId()]);
527
528
            return $this->redirectToRoute('shopping');
529
        }
530
531
        return [
532
            'form' => $form->createView(),
533
            'Customer' => $this->getUser(),
534
            'shippingId' => $Shipping->getId(),
535
        ];
536
    }
537
538
    /**
539
     * お届け先の新規作成または編集画面.
540
     *
541
     * 会員時は新しいお届け先を作成する.
542
     * 非会員時は選択されたお届け先の編集を行う.
543
     *
544
     * @Route("/shopping/shipping_edit/{id}", name="shopping_shipping_edit", requirements={"id" = "\d+"})
545
     * @Template("Shopping/shipping_edit.twig")
546
     */
547
    public function shippingEdit(Request $request, Shipping $Shipping)
548
    {
549
        // ログイン状態のチェック.
550
        if ($this->orderHelper->isLoginRequired()) {
551
            return $this->redirectToRoute('shopping_login');
552
        }
553
554
        // 受注の存在チェック
555
        $preOrderId = $this->cartService->getPreOrderId();
556
        $Order = $this->orderHelper->getPurchaseProcessingOrder($preOrderId);
557
        if (!$Order) {
558
            return $this->redirectToRoute('shopping_error');
559
        }
560
561
        // 受注に紐づくShippingかどうかのチェック.
562
        if (!$Order->findShipping($Shipping->getId())) {
563
            return $this->redirectToRoute('shopping_error');
564
        }
565
566
        $CustomerAddress = new CustomerAddress();
567
        if ($this->isGranted('IS_AUTHENTICATED_FULLY')) {
568 2
            // ログイン時は会員と紐付け
569
            $CustomerAddress->setCustomer($this->getUser());
570 2
        } else {
571
            // 非会員時はお届け先をセット
572
            $CustomerAddress->setFromShipping($Shipping);
573
        }
574
        $builder = $this->formFactory->createBuilder(ShoppingShippingType::class, $CustomerAddress);
575 2
576
        $event = new EventArgs(
577 2
            [
578
                'builder' => $builder,
579
                'Order' => $Order,
580
                'Shipping' => $Shipping,
581
                'CustomerAddress' => $CustomerAddress,
582
            ],
583
            $request
584 2
        );
585
        $this->eventDispatcher->dispatch(EccubeEvents::FRONT_SHOPPING_SHIPPING_EDIT_INITIALIZE, $event);
586 2
587
        $form = $builder->getForm();
588 2
        $form->handleRequest($request);
589
590 2
        if ($form->isSubmitted() && $form->isValid()) {
591
            log_info('お届け先追加処理開始', ['order_id' => $Order->getId(), 'shipping_id' => $Shipping->getId()]);
592 2
593
            $Shipping->setFromCustomerAddress($CustomerAddress);
594
595 2
            if ($this->isGranted('IS_AUTHENTICATED_FULLY')) {
596 2
                $this->entityManager->persist($CustomerAddress);
597
            }
598
599
            // 合計金額の再計算
600
            $flowResult = $this->validatePurchaseFlow($Order);
601
            $this->entityManager->flush();
602
            if ($flowResult->hasWarning() || $flowResult->hasError()) {
603
                return $this->redirectToRoute('shopping_error');
604
            }
605
606 1
            $event = new EventArgs(
607
                [
608 1
                    'form' => $form,
609 1
                    'Shipping' => $Shipping,
610 1
                    'CustomerAddress' => $CustomerAddress,
611
                ],
612 1
                $request
613
            );
614 1
            $this->eventDispatcher->dispatch(EccubeEvents::FRONT_SHOPPING_SHIPPING_EDIT_COMPLETE, $event);
615
616
            log_info('お届け先追加処理完了', ['order_id' => $Order->getId(), 'shipping_id' => $Shipping->getId()]);
617
618 1
            return $this->redirectToRoute('shopping');
619
        }
620
621
        return [
622
            'form' => $form->createView(),
623
            'shippingId' => $Shipping->getId(),
624
        ];
625
    }
626
627 55
    /**
628
     * ログイン画面.
629 55
     *
630 55
     * @Route("/shopping/login", name="shopping_login")
631 53
     * @Template("Shopping/login.twig")
632 53
     */
633
    public function login(Request $request, AuthenticationUtils $authenticationUtils)
634
    {
635
        if ($this->isGranted('IS_AUTHENTICATED_FULLY')) {
636
            return $this->redirectToRoute('shopping');
637
        }
638 53
639
        /* @var $form \Symfony\Component\Form\FormInterface */
640 2
        $builder = $this->formFactory->createNamedBuilder('', CustomerLoginType::class);
641
642
        if ($this->isGranted('IS_AUTHENTICATED_REMEMBERED')) {
643 2
            $Customer = $this->getUser();
644
            if ($Customer) {
645
                $builder->get('login_email')->setData($Customer->getEmail());
646
            }
647
        }
648
649
        $event = new EventArgs(
650
            [
651
                'builder' => $builder,
652 50
            ],
653
            $request
654
        );
655 50
        $this->eventDispatcher->dispatch(EccubeEvents::FRONT_SHOPPING_LOGIN_INITIALIZE, $event);
656
657
        $form = $builder->getForm();
658 50
659
        return [
660 34
            'error' => $authenticationUtils->getLastAuthenticationError(),
661
            'form' => $form->createView(),
662
        ];
663
    }
664
665
    /**
666
     * 購入エラー画面.
667
     *
668
     * @Route("/shopping/error", name="shopping_error")
669
     * @Template("Shopping/shopping_error.twig")
670 34
     */
671
    public function error(Request $request, PurchaseFlow $cartPurchaseFlow)
672
    {
673
        // 受注とカートのずれを合わせるため, カートのPurchaseFlowをコールする.
674
        $Cart = $this->cartService->getCart();
675
        if (null !== $Cart) {
676 34
            $cartPurchaseFlow->validate($Cart, new PurchaseContext());
677 34
            $this->cartService->save();
678 34
        }
679
680 34
        $event = new EventArgs(
681 34
            [],
682
            $request
683
        );
684
        $this->eventDispatcher->dispatch(EccubeEvents::FRONT_SHOPPING_SHIPPING_ERROR_COMPLETE, $event);
685
686
        if ($event->getResponse() !== null) {
687
            return $event->getResponse();
688
        }
689
690 34
        return [];
691
    }
692
693
    /**
694 50
     * PaymentMethodをコンテナから取得する.
695
     *
696 50
     * @param Order $Order
697
     * @param FormInterface $form
698 50
     *
699
     * @return PaymentMethodInterface
700
     */
701
    private function createPaymentMethod(Order $Order, FormInterface $form)
702
    {
703
        $PaymentMethod = $this->container->get($Order->getPayment()->getMethodClass());
704
        $PaymentMethod->setOrder($Order);
705
        $PaymentMethod->setFormType($form);
706
707 50
        return $PaymentMethod;
708
    }
709 50
710
    /**
711 50
     * PaymentMethod::applyを実行する.
712
     *
713 50
     * @param PaymentMethodInterface $paymentMethod
714
     * @return \Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\Response
715 50
     */
716 50
    protected function executeApply(PaymentMethodInterface $paymentMethod)
717
    {
718 50
        $dispatcher = $paymentMethod->apply(); // 決済処理中.
719
720 50
        // リンク式決済のように他のサイトへ遷移する場合などは, dispatcherに処理を移譲する.
721
        if ($dispatcher instanceof PaymentDispatcher) {
722 50
            $response = $dispatcher->getResponse();
723
            $this->entityManager->flush();
724 50
725
            // dispatcherがresponseを保持している場合はresponseを返す
726 50
            if ($response && ($response->isRedirection() || $response->getContent())) {
727
                log_info('[注文処理] PaymentMethod::applyが指定したレスポンスを表示します.');
728
729
                return $response;
730
            }
731
732
            // forwardすることも可能.
733
            if ($dispatcher->isForward()) {
734
                log_info('[注文処理] PaymentMethod::applyによりForwardします.',
735 20
                    [$dispatcher->getRoute(), $dispatcher->getPathParameters(), $dispatcher->getQueryParameters(),]);
736
737 20
                return $this->forwardToRoute($dispatcher->getRoute(), $dispatcher->getPathParameters(),
738
                    $dispatcher->getQueryParameters());
739
            } else {
740
                log_info('[注文処理] PaymentMethod::applyによりリダイレクトします.',
741
                    [$dispatcher->getRoute(), $dispatcher->getPathParameters(), $dispatcher->getQueryParameters(),]);
742
743 20
                return $this->redirectToRoute($dispatcher->getRoute(),
744
                    array_merge($dispatcher->getPathParameters(), $dispatcher->getQueryParameters()));
745 8
            }
746
        }
747 8
    }
748 8
749 7
    /**
750
     * PaymentMethod::checkoutを実行する.
751 1
     *
752
     * @param PaymentMethodInterface $paymentMethod
753 1
     * @return \Symfony\Component\HttpFoundation\RedirectResponse|\Symfony\Component\HttpFoundation\Response
754 7
     */
755
    protected function executeCheckout(PaymentMethodInterface $paymentMethod)
756
    {
757
        $PaymentResult = $paymentMethod->checkout();
758
        $response = $PaymentResult->getResponse();
759 7
        // PaymentResultがresponseを保持している場合はresponseを返す
760 View Code Duplication
        if ($response && ($response->isRedirection() || $response->getContent())) {
761
            $this->entityManager->flush();
762 7
            log_info('[注文処理] PaymentMethod::checkoutが指定したレスポンスを表示します.');
763 7
764
            return $response;
765 7
        }
766
767
        // エラー時はロールバックして購入エラーとする.
768 View Code Duplication
        if (!$PaymentResult->isSuccess()) {
769 12
            $this->entityManager->rollback();
770
            foreach ($PaymentResult->getErrors() as $error) {
771
                $this->addError($error);
772
            }
773
774
            log_info('[注文処理] PaymentMethod::checkoutのエラーのため, 購入エラー画面へ遷移します.', [$PaymentResult->getErrors()]);
775
776
            return $this->redirectToRoute('shopping_error');
777
        }
778 26
    }
779
}
780