vendor/shopware/storefront/Controller/StorefrontController.php line 243

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. namespace Shopware\Storefront\Controller;
  3. use Shopware\Core\Checkout\Cart\Cart;
  4. use Shopware\Core\Checkout\Cart\Error\Error;
  5. use Shopware\Core\Checkout\Cart\Error\ErrorRoute;
  6. use Shopware\Core\Content\Seo\SeoUrlPlaceholderHandlerInterface;
  7. use Shopware\Core\Framework\Adapter\Twig\TemplateFinder;
  8. use Shopware\Core\Framework\Feature;
  9. use Shopware\Core\Framework\Log\Package;
  10. use Shopware\Core\Framework\Routing\RequestTransformerInterface;
  11. use Shopware\Core\Framework\Script\Execution\Hook;
  12. use Shopware\Core\Framework\Script\Execution\ScriptExecutor;
  13. use Shopware\Core\PlatformRequest;
  14. use Shopware\Core\Profiling\Profiler;
  15. use Shopware\Core\System\SystemConfig\SystemConfigService;
  16. use Shopware\Storefront\Event\StorefrontRenderEvent;
  17. use Shopware\Storefront\Framework\Routing\RequestTransformer;
  18. use Shopware\Storefront\Framework\Routing\Router;
  19. use Shopware\Storefront\Framework\Routing\StorefrontResponse;
  20. use Shopware\Storefront\Framework\Twig\Extension\IconCacheTwigFilter;
  21. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  22. use Symfony\Component\HttpFoundation\Request;
  23. use Symfony\Component\HttpFoundation\Response;
  24. use Symfony\Component\HttpKernel\EventListener\AbstractSessionListener;
  25. use Twig\Environment;
  26. #[Package('storefront')]
  27. abstract class StorefrontController extends AbstractController
  28. {
  29. public const SUCCESS = 'success';
  30. public const DANGER = 'danger';
  31. public const INFO = 'info';
  32. public const WARNING = 'warning';
  33. private Environment $twig;
  34. public function setTwig(Environment $twig): void
  35. {
  36. $this->twig = $twig;
  37. }
  38. protected function renderStorefront(string $view, array $parameters = []): Response
  39. {
  40. $request = $this->container->get('request_stack')->getCurrentRequest();
  41. if ($request === null) {
  42. $request = new Request();
  43. }
  44. $salesChannelContext = $request->attributes->get(PlatformRequest::ATTRIBUTE_SALES_CHANNEL_CONTEXT_OBJECT);
  45. /* @feature-deprecated $view will be original template in StorefrontRenderEvent from 6.5.0.0 */
  46. if (Feature::isActive('FEATURE_NEXT_17275')) {
  47. $event = new StorefrontRenderEvent($view, $parameters, $request, $salesChannelContext);
  48. } else {
  49. $inheritedView = $this->getTemplateFinder()->find($view);
  50. $event = new StorefrontRenderEvent($inheritedView, $parameters, $request, $salesChannelContext);
  51. }
  52. $this->container->get('event_dispatcher')->dispatch($event);
  53. $iconCacheEnabled = $this->getSystemConfigService()->get('core.storefrontSettings.iconCache');
  54. /** @deprecated tag:v6.5.0 - icon cache will be true by default. */
  55. if ($iconCacheEnabled || (Feature::isActive('v6.5.0.0') && $iconCacheEnabled === null)) {
  56. IconCacheTwigFilter::enable();
  57. }
  58. $response = Profiler::trace('twig-rendering', function () use ($view, $event) {
  59. return $this->render($view, $event->getParameters(), new StorefrontResponse());
  60. });
  61. /** @deprecated tag:v6.5.0 - icon cache will be true by default. */
  62. if ($iconCacheEnabled || (Feature::isActive('v6.5.0.0') && $iconCacheEnabled === null)) {
  63. IconCacheTwigFilter::disable();
  64. }
  65. if (!$response instanceof StorefrontResponse) {
  66. throw new \RuntimeException('Symfony render implementation changed. Providing a response is no longer supported');
  67. }
  68. $host = $request->attributes->get(RequestTransformer::STOREFRONT_URL);
  69. $seoUrlReplacer = $this->container->get(SeoUrlPlaceholderHandlerInterface::class);
  70. $content = $response->getContent();
  71. if ($content !== false) {
  72. $response->setContent(
  73. $seoUrlReplacer->replace($content, $host, $salesChannelContext)
  74. );
  75. }
  76. $response->setData($parameters);
  77. $response->setContext($salesChannelContext);
  78. $response->headers->set(AbstractSessionListener::NO_AUTO_CACHE_CONTROL_HEADER, '1');
  79. $response->headers->set('Content-Type', 'text/html');
  80. return $response;
  81. }
  82. protected function trans(string $snippet, array $parameters = []): string
  83. {
  84. return $this->container
  85. ->get('translator')
  86. ->trans($snippet, $parameters);
  87. }
  88. protected function createActionResponse(Request $request): Response
  89. {
  90. if ($request->get('redirectTo') || $request->get('redirectTo') === '') {
  91. $params = $this->decodeParam($request, 'redirectParameters');
  92. $redirectTo = $request->get('redirectTo');
  93. if ($redirectTo) {
  94. return $this->redirectToRoute($redirectTo, $params);
  95. }
  96. return $this->redirectToRoute('frontend.home.page', $params);
  97. }
  98. if ($request->get('forwardTo')) {
  99. $params = $this->decodeParam($request, 'forwardParameters');
  100. return $this->forwardToRoute($request->get('forwardTo'), [], $params);
  101. }
  102. return new Response();
  103. }
  104. protected function forwardToRoute(string $routeName, array $attributes = [], array $routeParameters = []): Response
  105. {
  106. $router = $this->container->get('router');
  107. $url = $this->generateUrl($routeName, $routeParameters, Router::PATH_INFO);
  108. // for the route matching the request method is set to "GET" because
  109. // this method is not ought to be used as a post passthrough
  110. // rather it shall return templates or redirects to display results of the request ahead
  111. $method = $router->getContext()->getMethod();
  112. $router->getContext()->setMethod(Request::METHOD_GET);
  113. $route = $router->match($url);
  114. $router->getContext()->setMethod($method);
  115. $request = $this->container->get('request_stack')->getCurrentRequest();
  116. if ($request === null) {
  117. $request = new Request();
  118. }
  119. $attributes = array_merge(
  120. $this->container->get(RequestTransformerInterface::class)->extractInheritableAttributes($request),
  121. $route,
  122. $attributes,
  123. ['_route_params' => $routeParameters]
  124. );
  125. return $this->forward($route['_controller'], $attributes, $routeParameters);
  126. }
  127. protected function decodeParam(Request $request, string $param): array
  128. {
  129. $params = $request->get($param);
  130. if (\is_string($params)) {
  131. $params = json_decode($params, true);
  132. }
  133. if (empty($params)) {
  134. $params = [];
  135. }
  136. return $params;
  137. }
  138. protected function addCartErrors(Cart $cart, ?\Closure $filter = null): void
  139. {
  140. $errors = $cart->getErrors();
  141. if ($filter !== null) {
  142. $errors = $errors->filter($filter);
  143. }
  144. $groups = [
  145. 'info' => $errors->getNotices(),
  146. 'warning' => $errors->getWarnings(),
  147. 'danger' => $errors->getErrors(),
  148. ];
  149. $request = $this->container->get('request_stack')->getMainRequest();
  150. $exists = [];
  151. if ($request && $request->hasSession() && method_exists($session = $request->getSession(), 'getFlashBag')) {
  152. $exists = $session->getFlashBag()->peekAll();
  153. }
  154. $flat = [];
  155. foreach ($exists as $messages) {
  156. $flat = array_merge($flat, $messages);
  157. }
  158. /** @var array<string, Error[]> $groups */
  159. foreach ($groups as $type => $errors) {
  160. foreach ($errors as $error) {
  161. $parameters = [];
  162. foreach ($error->getParameters() as $key => $value) {
  163. $parameters['%' . $key . '%'] = $value;
  164. }
  165. if ($error->getRoute() instanceof ErrorRoute) {
  166. $parameters['%url%'] = $this->generateUrl(
  167. $error->getRoute()->getKey(),
  168. $error->getRoute()->getParams()
  169. );
  170. }
  171. $message = $this->trans('checkout.' . $error->getMessageKey(), $parameters);
  172. if (\in_array($message, $flat, true)) {
  173. continue;
  174. }
  175. $this->addFlash($type, $message);
  176. }
  177. }
  178. }
  179. protected function renderView(string $view, array $parameters = []): string
  180. {
  181. $view = $this->getTemplateFinder()->find($view);
  182. if (isset($this->twig)) {
  183. return $this->twig->render($view, $parameters);
  184. }
  185. Feature::triggerDeprecationOrThrow(
  186. 'v6.5.0.0',
  187. sprintf('Class %s does not have twig injected. Add to your service definition a method call to setTwig with the twig instance', static::class)
  188. );
  189. return parent::renderView($view, $parameters);
  190. }
  191. protected function getTemplateFinder(): TemplateFinder
  192. {
  193. return $this->container->get(TemplateFinder::class);
  194. }
  195. protected function hook(Hook $hook): void
  196. {
  197. $this->container->get(ScriptExecutor::class)->execute($hook);
  198. }
  199. protected function getSystemConfigService(): SystemConfigService
  200. {
  201. return $this->container->get(SystemConfigService::class);
  202. }
  203. }