Passed
Push — trunk ( ae19db...a5489a )
by Christian
13:52 queued 12s
created

StorefrontController::getSubscribedServices()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 11
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 7
nc 1
nop 0
dl 0
loc 11
rs 10
c 0
b 0
f 0
1
<?php declare(strict_types=1);
2
3
namespace Shopware\Storefront\Controller;
4
5
use Shopware\Core\Checkout\Cart\Cart;
6
use Shopware\Core\Checkout\Cart\Error\Error;
7
use Shopware\Core\Checkout\Cart\Error\ErrorRoute;
8
use Shopware\Core\Content\Seo\SeoUrlPlaceholderHandlerInterface;
9
use Shopware\Core\Framework\Adapter\Twig\TemplateFinder;
10
use Shopware\Core\Framework\Log\Package;
11
use Shopware\Core\Framework\Routing\RequestTransformerInterface;
12
use Shopware\Core\Framework\Script\Execution\Hook;
13
use Shopware\Core\Framework\Script\Execution\ScriptExecutor;
14
use Shopware\Core\PlatformRequest;
15
use Shopware\Core\Profiling\Profiler;
16
use Shopware\Core\System\SystemConfig\SystemConfigService;
17
use Shopware\Storefront\Event\StorefrontRedirectEvent;
18
use Shopware\Storefront\Event\StorefrontRenderEvent;
19
use Shopware\Storefront\Framework\Routing\RequestTransformer;
20
use Shopware\Storefront\Framework\Routing\Router;
21
use Shopware\Storefront\Framework\Routing\StorefrontResponse;
22
use Shopware\Storefront\Framework\Twig\Extension\IconCacheTwigFilter;
0 ignored issues
show
Bug introduced by
The type Shopware\Storefront\Fram...ion\IconCacheTwigFilter was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
23
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
24
use Symfony\Component\HttpFoundation\RedirectResponse;
25
use Symfony\Component\HttpFoundation\Request;
26
use Symfony\Component\HttpFoundation\Response;
27
use Symfony\Component\HttpFoundation\Session\FlashBagAwareSessionInterface;
28
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
29
use Symfony\Contracts\Service\Attribute\Required;
30
use Twig\Environment;
31
32
#[Package('storefront')]
33
abstract class StorefrontController extends AbstractController
34
{
35
    public const SUCCESS = 'success';
36
    public const DANGER = 'danger';
37
    public const INFO = 'info';
38
    public const WARNING = 'warning';
39
40
    private ?Environment $twig = null;
41
42
    #[Required]
43
    public function setTwig(Environment $twig): void
44
    {
45
        $this->twig = $twig;
46
    }
47
48
    public static function getSubscribedServices(): array
49
    {
50
        $services = parent::getSubscribedServices();
51
52
        $services['event_dispatcher'] = EventDispatcherInterface::class;
53
        $services[SystemConfigService::class] = SystemConfigService::class;
54
        $services[TemplateFinder::class] = TemplateFinder::class;
55
        $services[SeoUrlPlaceholderHandlerInterface::class] = SeoUrlPlaceholderHandlerInterface::class;
56
        $services[ScriptExecutor::class] = ScriptExecutor::class;
57
58
        return $services;
59
    }
60
61
    /**
62
     * @param array<string, mixed> $parameters
63
     */
64
    protected function renderStorefront(string $view, array $parameters = []): Response
65
    {
66
        $request = $this->container->get('request_stack')->getCurrentRequest();
67
68
        if ($request === null) {
69
            $request = new Request();
70
        }
71
72
        $salesChannelContext = $request->attributes->get(PlatformRequest::ATTRIBUTE_SALES_CHANNEL_CONTEXT_OBJECT);
73
74
        $event = new StorefrontRenderEvent($view, $parameters, $request, $salesChannelContext);
75
76
        $this->container->get('event_dispatcher')->dispatch($event);
77
78
        $iconCacheEnabled = $this->getSystemConfigService()->get('core.storefrontSettings.iconCache') ?? true;
79
80
        if ($iconCacheEnabled) {
81
            IconCacheTwigFilter::enable();
82
        }
83
84
        $response = Profiler::trace('twig-rendering', fn () => $this->render($view, $event->getParameters(), new StorefrontResponse()));
85
86
        if ($iconCacheEnabled) {
87
            IconCacheTwigFilter::disable();
88
        }
89
90
        if (!$response instanceof StorefrontResponse) {
91
            throw new \RuntimeException('Symfony render implementation changed. Providing a response is no longer supported');
92
        }
93
94
        $host = $request->attributes->get(RequestTransformer::STOREFRONT_URL);
95
96
        $seoUrlReplacer = $this->container->get(SeoUrlPlaceholderHandlerInterface::class);
97
        $content = $response->getContent();
98
        if ($content !== false) {
0 ignored issues
show
introduced by
The condition $content !== false is always true.
Loading history...
99
            $response->setContent(
100
                $seoUrlReplacer->replace($content, $host, $salesChannelContext)
101
            );
102
        }
103
104
        $response->setData($parameters);
0 ignored issues
show
Deprecated Code introduced by
The function Shopware\Storefront\Fram...rontResponse::setData() has been deprecated: tag:v6.6.0 - parameter `$data` will be strictly typed to `array` ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

104
        /** @scrutinizer ignore-deprecated */ $response->setData($parameters);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
105
        $response->setContext($salesChannelContext);
106
107
        $response->headers->set('Content-Type', 'text/html');
108
109
        return $response;
110
    }
111
112
    /**
113
     * @param array<string, mixed> $parameters
114
     */
115
    protected function trans(string $snippet, array $parameters = []): string
116
    {
117
        return $this->container
118
            ->get('translator')
119
            ->trans($snippet, $parameters);
120
    }
121
122
    protected function createActionResponse(Request $request): Response
123
    {
124
        if ($request->get('redirectTo') || $request->get('redirectTo') === '') {
125
            $params = $this->decodeParam($request, 'redirectParameters');
126
127
            $redirectTo = $request->get('redirectTo');
128
129
            if ($redirectTo) {
130
                return $this->redirectToRoute($redirectTo, $params);
131
            }
132
133
            return $this->redirectToRoute('frontend.home.page', $params);
134
        }
135
136
        if ($request->get('forwardTo')) {
137
            $params = $this->decodeParam($request, 'forwardParameters');
138
139
            return $this->forwardToRoute($request->get('forwardTo'), [], $params);
140
        }
141
142
        return new Response();
143
    }
144
145
    /**
146
     * @param array<string, mixed> $attributes
147
     * @param array<string, mixed> $routeParameters
148
     */
149
    protected function forwardToRoute(string $routeName, array $attributes = [], array $routeParameters = []): Response
150
    {
151
        $router = $this->container->get('router');
152
153
        $url = $this->generateUrl($routeName, $routeParameters, Router::PATH_INFO);
154
155
        // for the route matching the request method is set to "GET" because
156
        // this method is not ought to be used as a post passthrough
157
        // rather it shall return templates or redirects to display results of the request ahead
158
        $method = $router->getContext()->getMethod();
159
        $router->getContext()->setMethod(Request::METHOD_GET);
160
161
        $route = $router->match($url);
162
        $router->getContext()->setMethod($method);
163
164
        $request = $this->container->get('request_stack')->getCurrentRequest();
165
166
        if ($request === null) {
167
            $request = new Request();
168
        }
169
170
        $attributes = array_merge(
171
            $this->container->get(RequestTransformerInterface::class)->extractInheritableAttributes($request),
172
            $route,
173
            $attributes,
174
            ['_route_params' => $routeParameters]
175
        );
176
177
        return $this->forward($route['_controller'], $attributes, $routeParameters);
178
    }
179
180
    /**
181
     * @return array<string, mixed>
182
     */
183
    protected function decodeParam(Request $request, string $param): array
184
    {
185
        $params = $request->get($param);
186
187
        if (\is_string($params)) {
188
            $params = json_decode($params, true);
189
        }
190
191
        if (empty($params)) {
192
            $params = [];
193
        }
194
195
        return $params;
196
    }
197
198
    protected function addCartErrors(Cart $cart, ?\Closure $filter = null): void
199
    {
200
        $errors = $cart->getErrors();
201
        if ($filter !== null) {
202
            $errors = $errors->filter($filter);
203
        }
204
205
        $groups = [
206
            'info' => $errors->getNotices(),
207
            'warning' => $errors->getWarnings(),
208
            'danger' => $errors->getErrors(),
209
        ];
210
211
        $request = $this->container->get('request_stack')->getMainRequest();
212
        $exists = [];
213
214
        if ($request && $request->hasSession() && $request->getSession() instanceof FlashBagAwareSessionInterface) {
215
            $exists = $request->getSession()->getFlashBag()->peekAll();
216
        }
217
218
        $flat = [];
219
        foreach ($exists as $messages) {
220
            $flat = array_merge($flat, $messages);
221
        }
222
223
        /** @var array<string, Error[]> $groups */
224
        foreach ($groups as $type => $errors) {
225
            foreach ($errors as $error) {
226
                $parameters = [];
227
228
                foreach ($error->getParameters() as $key => $value) {
229
                    $parameters['%' . $key . '%'] = $value;
230
                }
231
232
                if ($error->getRoute() instanceof ErrorRoute) {
233
                    $parameters['%url%'] = $this->generateUrl(
234
                        $error->getRoute()->getKey(),
235
                        $error->getRoute()->getParams()
236
                    );
237
                }
238
239
                $message = $this->trans('checkout.' . $error->getMessageKey(), $parameters);
240
241
                if (\in_array($message, $flat, true)) {
242
                    continue;
243
                }
244
245
                $this->addFlash($type, $message);
246
            }
247
        }
248
    }
249
250
    /**
251
     * @param array<string, mixed> $parameters
252
     */
253
    protected function redirectToRoute(string $route, array $parameters = [], int $status = Response::HTTP_FOUND): RedirectResponse
254
    {
255
        $event = new StorefrontRedirectEvent($route, $parameters, $status);
256
        $this->container->get('event_dispatcher')->dispatch($event);
257
258
        return parent::redirectToRoute($event->getRoute(), $event->getParameters(), $event->getStatus());
259
    }
260
261
    /**
262
     * @param array<string, mixed> $parameters
263
     */
264
    protected function renderView(string $view, array $parameters = []): string
265
    {
266
        $view = $this->getTemplateFinder()->find($view);
267
268
        if ($this->twig !== null) {
269
            return $this->twig->render($view, $parameters);
270
        }
271
272
        throw new \Exception(
273
            sprintf('Class %s does not have twig injected. Add to your service definition a method call to setTwig with the twig instance', static::class)
274
        );
275
    }
276
277
    protected function getTemplateFinder(): TemplateFinder
278
    {
279
        return $this->container->get(TemplateFinder::class);
280
    }
281
282
    protected function hook(Hook $hook): void
283
    {
284
        $this->container->get(ScriptExecutor::class)->execute($hook);
285
    }
286
287
    protected function getSystemConfigService(): SystemConfigService
288
    {
289
        return $this->container->get(SystemConfigService::class);
290
    }
291
}
292