Completed
Pull Request — 4.0 (#3650)
by chihiro
06:12
created

ShoppingController::order()   B

Complexity

Conditions 7
Paths 4

Size

Total Lines 38

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 8.0504

Importance

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