Passed
Push — 6.5.0.0 ( b977f8...246a8a )
by Christian
14:42 queued 12s
created

CheckoutController::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 12
Code Lines 0

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 0
nc 1
nop 10
dl 0
loc 12
rs 10
c 0
b 0
f 0

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php declare(strict_types=1);
2
3
namespace Shopware\Storefront\Controller;
4
5
use Shopware\Core\Checkout\Cart\Error\Error;
6
use Shopware\Core\Checkout\Cart\Error\ErrorCollection;
7
use Shopware\Core\Checkout\Cart\Exception\InvalidCartException;
8
use Shopware\Core\Checkout\Cart\SalesChannel\CartLoadRoute;
9
use Shopware\Core\Checkout\Cart\SalesChannel\CartService;
10
use Shopware\Core\Checkout\Customer\SalesChannel\AbstractLogoutRoute;
11
use Shopware\Core\Checkout\Order\Exception\EmptyCartException;
12
use Shopware\Core\Checkout\Order\SalesChannel\OrderService;
13
use Shopware\Core\Checkout\Payment\Exception\InvalidOrderException;
14
use Shopware\Core\Checkout\Payment\Exception\PaymentProcessException;
15
use Shopware\Core\Checkout\Payment\Exception\UnknownPaymentMethodException;
16
use Shopware\Core\Checkout\Payment\PaymentService;
17
use Shopware\Core\Framework\Log\Package;
18
use Shopware\Core\Framework\Validation\DataBag\RequestDataBag;
19
use Shopware\Core\Framework\Validation\Exception\ConstraintViolationException;
20
use Shopware\Core\Profiling\Profiler;
21
use Shopware\Core\System\SalesChannel\SalesChannelContext;
22
use Shopware\Core\System\SystemConfig\SystemConfigService;
23
use Shopware\Storefront\Checkout\Cart\Error\PaymentMethodChangedError;
24
use Shopware\Storefront\Checkout\Cart\Error\ShippingMethodChangedError;
25
use Shopware\Storefront\Framework\AffiliateTracking\AffiliateTrackingListener;
26
use Shopware\Storefront\Page\Checkout\Cart\CheckoutCartPageLoadedHook;
27
use Shopware\Storefront\Page\Checkout\Cart\CheckoutCartPageLoader;
28
use Shopware\Storefront\Page\Checkout\Confirm\CheckoutConfirmPageLoadedHook;
29
use Shopware\Storefront\Page\Checkout\Confirm\CheckoutConfirmPageLoader;
30
use Shopware\Storefront\Page\Checkout\Finish\CheckoutFinishPageLoadedHook;
31
use Shopware\Storefront\Page\Checkout\Finish\CheckoutFinishPageLoader;
32
use Shopware\Storefront\Page\Checkout\Offcanvas\CheckoutInfoWidgetLoadedHook;
33
use Shopware\Storefront\Page\Checkout\Offcanvas\CheckoutOffcanvasWidgetLoadedHook;
34
use Shopware\Storefront\Page\Checkout\Offcanvas\OffcanvasCartPageLoader;
35
use Symfony\Component\HttpFoundation\RedirectResponse;
36
use Symfony\Component\HttpFoundation\Request;
37
use Symfony\Component\HttpFoundation\Response;
38
use Symfony\Component\HttpFoundation\Session\SessionInterface;
39
use Symfony\Component\Routing\Annotation\Route;
40
41
/**
42
 * @internal
43
 */
44
#[Route(defaults: ['_routeScope' => ['storefront']])]
45
#[Package('storefront')]
46
class CheckoutController extends StorefrontController
47
{
48
    private const REDIRECTED_FROM_SAME_ROUTE = 'redirected';
49
50
    /**
51
     * @internal
52
     */
53
    public function __construct(
54
        private readonly CartService $cartService,
55
        private readonly CheckoutCartPageLoader $cartPageLoader,
56
        private readonly CheckoutConfirmPageLoader $confirmPageLoader,
57
        private readonly CheckoutFinishPageLoader $finishPageLoader,
58
        private readonly OrderService $orderService,
59
        private readonly PaymentService $paymentService,
60
        private readonly OffcanvasCartPageLoader $offcanvasCartPageLoader,
61
        private readonly SystemConfigService $config,
62
        private readonly AbstractLogoutRoute $logoutRoute,
63
        private readonly CartLoadRoute $cartLoadRoute
64
    ) {
65
    }
66
67
    #[Route(path: '/checkout/cart', name: 'frontend.checkout.cart.page', options: ['seo' => false], defaults: ['_noStore' => true], methods: ['GET'])]
68
    public function cartPage(Request $request, SalesChannelContext $context): Response
69
    {
70
        $page = $this->cartPageLoader->load($request, $context);
71
        $cart = $page->getCart();
72
        $cartErrors = $cart->getErrors();
73
74
        $this->hook(new CheckoutCartPageLoadedHook($page, $context));
75
76
        $this->addCartErrors($cart);
77
78
        if (!$request->query->getBoolean(self::REDIRECTED_FROM_SAME_ROUTE) && $this->routeNeedsReload($cartErrors)) {
79
            $cartErrors->clear();
80
81
            // To prevent redirect loops add the identifier that the request already got redirected from the same origin
82
            return $this->redirectToRoute(
83
                'frontend.checkout.cart.page',
84
                [...$request->query->all(), ...[self::REDIRECTED_FROM_SAME_ROUTE => true]],
85
            );
86
        }
87
        $cartErrors->clear();
88
89
        return $this->renderStorefront('@Storefront/storefront/page/checkout/cart/index.html.twig', ['page' => $page]);
90
    }
91
92
    #[Route(path: '/checkout/cart.json', name: 'frontend.checkout.cart.json', methods: ['GET'], options: ['seo' => false], defaults: ['XmlHttpRequest' => true])]
93
    public function cartJson(Request $request, SalesChannelContext $context): Response
94
    {
95
        return $this->cartLoadRoute->load($request, $context);
96
    }
97
98
    #[Route(path: '/checkout/confirm', name: 'frontend.checkout.confirm.page', options: ['seo' => false], defaults: ['XmlHttpRequest' => true, '_noStore' => true], methods: ['GET'])]
99
    public function confirmPage(Request $request, SalesChannelContext $context): Response
100
    {
101
        if (!$context->getCustomer()) {
102
            return $this->redirectToRoute('frontend.checkout.register.page');
103
        }
104
105
        if ($this->cartService->getCart($context->getToken(), $context)->getLineItems()->count() === 0) {
106
            return $this->redirectToRoute('frontend.checkout.cart.page');
107
        }
108
109
        $page = $this->confirmPageLoader->load($request, $context);
110
        $cart = $page->getCart();
111
        $cartErrors = $cart->getErrors();
112
113
        $this->hook(new CheckoutConfirmPageLoadedHook($page, $context));
114
115
        $this->addCartErrors($cart);
116
117
        if (!$request->query->getBoolean(self::REDIRECTED_FROM_SAME_ROUTE) && $this->routeNeedsReload($cartErrors)) {
118
            $cartErrors->clear();
119
120
            // To prevent redirect loops add the identifier that the request already got redirected from the same origin
121
            return $this->redirectToRoute(
122
                'frontend.checkout.confirm.page',
123
                [...$request->query->all(), ...[self::REDIRECTED_FROM_SAME_ROUTE => true]],
124
            );
125
        }
126
127
        return $this->renderStorefront('@Storefront/storefront/page/checkout/confirm/index.html.twig', ['page' => $page]);
128
    }
129
130
    #[Route(path: '/checkout/finish', name: 'frontend.checkout.finish.page', options: ['seo' => false], defaults: ['_noStore' => true], methods: ['GET'])]
131
    public function finishPage(Request $request, SalesChannelContext $context, RequestDataBag $dataBag): Response
132
    {
133
        if ($context->getCustomer() === null) {
134
            return $this->redirectToRoute('frontend.checkout.register.page');
135
        }
136
137
        $page = $this->finishPageLoader->load($request, $context);
138
139
        $this->hook(new CheckoutFinishPageLoadedHook($page, $context));
140
141
        if ($page->isPaymentFailed() === true) {
142
            return $this->redirectToRoute(
143
                'frontend.account.edit-order.page',
144
                [
145
                    'orderId' => $request->get('orderId'),
146
                    'error-code' => 'CHECKOUT__UNKNOWN_ERROR',
147
                ]
148
            );
149
        }
150
151
        if ($context->getCustomer()->getGuest() && $this->config->get('core.cart.logoutGuestAfterCheckout', $context->getSalesChannelId())) {
152
            $this->logoutRoute->logout($context, $dataBag);
153
        }
154
155
        return $this->renderStorefront('@Storefront/storefront/page/checkout/finish/index.html.twig', ['page' => $page]);
156
    }
157
158
    #[Route(path: '/checkout/order', name: 'frontend.checkout.finish.order', options: ['seo' => false], methods: ['POST'])]
159
    public function order(RequestDataBag $data, SalesChannelContext $context, Request $request): Response
160
    {
161
        if (!$context->getCustomer()) {
162
            return $this->redirectToRoute('frontend.checkout.register.page');
163
        }
164
165
        try {
166
            $this->addAffiliateTracking($data, $request->getSession());
167
168
            $orderId = Profiler::trace('checkout-order', fn () => $this->orderService->createOrder($data, $context));
169
        } catch (ConstraintViolationException $formViolations) {
170
            return $this->forwardToRoute('frontend.checkout.confirm.page', ['formViolations' => $formViolations]);
171
        } catch (InvalidCartException | Error | EmptyCartException) {
172
            $this->addCartErrors(
173
                $this->cartService->getCart($context->getToken(), $context)
174
            );
175
176
            return $this->forwardToRoute('frontend.checkout.confirm.page');
177
        }
178
179
        try {
180
            $finishUrl = $this->generateUrl('frontend.checkout.finish.page', ['orderId' => $orderId]);
181
            $errorUrl = $this->generateUrl('frontend.account.edit-order.page', ['orderId' => $orderId]);
182
183
            $response = Profiler::trace('handle-payment', fn (): ?RedirectResponse => $this->paymentService->handlePaymentByOrder($orderId, $data, $context, $finishUrl, $errorUrl));
184
185
            return $response ?? new RedirectResponse($finishUrl);
186
        } catch (PaymentProcessException | InvalidOrderException | UnknownPaymentMethodException) {
187
            return $this->forwardToRoute('frontend.checkout.finish.page', ['orderId' => $orderId, 'changedPayment' => false, 'paymentFailed' => true]);
188
        }
189
    }
190
191
    #[Route(path: '/widgets/checkout/info', name: 'frontend.checkout.info', defaults: ['XmlHttpRequest' => true], methods: ['GET'])]
192
    public function info(Request $request, SalesChannelContext $context): Response
193
    {
194
        $cart = $this->cartService->getCart($context->getToken(), $context);
195
        if ($cart->getLineItems()->count() <= 0) {
196
            return new Response(null, Response::HTTP_NO_CONTENT);
197
        }
198
199
        $page = $this->offcanvasCartPageLoader->load($request, $context);
200
201
        $this->hook(new CheckoutInfoWidgetLoadedHook($page, $context));
202
203
        $response = $this->renderStorefront('@Storefront/storefront/layout/header/actions/cart-widget.html.twig', ['page' => $page]);
204
        $response->headers->set('x-robots-tag', 'noindex');
205
206
        return $response;
207
    }
208
209
    #[Route(path: '/checkout/offcanvas', name: 'frontend.cart.offcanvas', options: ['seo' => false], defaults: ['XmlHttpRequest' => true], methods: ['GET'])]
210
    public function offcanvas(Request $request, SalesChannelContext $context): Response
211
    {
212
        $page = $this->offcanvasCartPageLoader->load($request, $context);
213
214
        $this->hook(new CheckoutOffcanvasWidgetLoadedHook($page, $context));
215
216
        $cart = $page->getCart();
217
        $this->addCartErrors($cart);
218
        $cartErrors = $cart->getErrors();
219
220
        if (!$request->query->getBoolean(self::REDIRECTED_FROM_SAME_ROUTE) && $this->routeNeedsReload($cartErrors)) {
221
            $cartErrors->clear();
222
223
            // To prevent redirect loops add the identifier that the request already got redirected from the same origin
224
            return $this->redirectToRoute(
225
                'frontend.cart.offcanvas',
226
                [...$request->query->all(), ...[self::REDIRECTED_FROM_SAME_ROUTE => true]],
227
            );
228
        }
229
230
        $cartErrors->clear();
231
232
        return $this->renderStorefront('@Storefront/storefront/component/checkout/offcanvas-cart.html.twig', ['page' => $page]);
233
    }
234
235
    private function addAffiliateTracking(RequestDataBag $dataBag, SessionInterface $session): void
236
    {
237
        $affiliateCode = $session->get(AffiliateTrackingListener::AFFILIATE_CODE_KEY);
238
        $campaignCode = $session->get(AffiliateTrackingListener::CAMPAIGN_CODE_KEY);
239
        if ($affiliateCode) {
240
            $dataBag->set(AffiliateTrackingListener::AFFILIATE_CODE_KEY, $affiliateCode);
241
        }
242
243
        if ($campaignCode) {
244
            $dataBag->set(AffiliateTrackingListener::CAMPAIGN_CODE_KEY, $campaignCode);
245
        }
246
    }
247
248
    private function routeNeedsReload(ErrorCollection $cartErrors): bool
249
    {
250
        foreach ($cartErrors as $error) {
251
            if ($error instanceof ShippingMethodChangedError || $error instanceof PaymentMethodChangedError) {
252
                return true;
253
            }
254
        }
255
256
        return false;
257
    }
258
}
259