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

  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\Log\Package;
  9. use Shopware\Core\Framework\Routing\RequestTransformerInterface;
  10. use Shopware\Core\Framework\Script\Execution\Hook;
  11. use Shopware\Core\Framework\Script\Execution\ScriptExecutor;
  12. use Shopware\Core\PlatformRequest;
  13. use Shopware\Core\Profiling\Profiler;
  14. use Shopware\Core\System\SystemConfig\SystemConfigService;
  15. use Shopware\Storefront\Event\StorefrontRedirectEvent;
  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\RedirectResponse;
  23. use Symfony\Component\HttpFoundation\Request;
  24. use Symfony\Component\HttpFoundation\Response;
  25. use Symfony\Component\HttpFoundation\Session\FlashBagAwareSessionInterface;
  26. use Twig\Environment;
  27. #[Package('storefront')]
  28. abstract class StorefrontController extends AbstractController
  29. {
  30.     public const SUCCESS 'success';
  31.     public const DANGER 'danger';
  32.     public const INFO 'info';
  33.     public const WARNING 'warning';
  34.     private ?Environment $twig null;
  35.     public function setTwig(Environment $twig): void
  36.     {
  37.         $this->twig $twig;
  38.     }
  39.     /**
  40.      * @param array<string, mixed> $parameters
  41.      */
  42.     protected function renderStorefront(string $view, array $parameters = []): Response
  43.     {
  44.         $request $this->container->get('request_stack')->getCurrentRequest();
  45.         if ($request === null) {
  46.             $request = new Request();
  47.         }
  48.         $salesChannelContext $request->attributes->get(PlatformRequest::ATTRIBUTE_SALES_CHANNEL_CONTEXT_OBJECT);
  49.         $event = new StorefrontRenderEvent($view$parameters$request$salesChannelContext);
  50.         $this->container->get('event_dispatcher')->dispatch($event);
  51.         $iconCacheEnabled $this->getSystemConfigService()->get('core.storefrontSettings.iconCache') ?? true;
  52.         if ($iconCacheEnabled) {
  53.             IconCacheTwigFilter::enable();
  54.         }
  55.         $response Profiler::trace('twig-rendering', fn () => $this->render($view$event->getParameters(), new StorefrontResponse()));
  56.         if ($iconCacheEnabled) {
  57.             IconCacheTwigFilter::disable();
  58.         }
  59.         if (!$response instanceof StorefrontResponse) {
  60.             throw new \RuntimeException('Symfony render implementation changed. Providing a response is no longer supported');
  61.         }
  62.         $host $request->attributes->get(RequestTransformer::STOREFRONT_URL);
  63.         $seoUrlReplacer $this->container->get(SeoUrlPlaceholderHandlerInterface::class);
  64.         $content $response->getContent();
  65.         if ($content !== false) {
  66.             $response->setContent(
  67.                 $seoUrlReplacer->replace($content$host$salesChannelContext)
  68.             );
  69.         }
  70.         $response->setData($parameters);
  71.         $response->setContext($salesChannelContext);
  72.         $response->headers->set('Content-Type''text/html');
  73.         return $response;
  74.     }
  75.     /**
  76.      * @param array<string, mixed> $parameters
  77.      */
  78.     protected function trans(string $snippet, array $parameters = []): string
  79.     {
  80.         return $this->container
  81.             ->get('translator')
  82.             ->trans($snippet$parameters);
  83.     }
  84.     protected function createActionResponse(Request $request): Response
  85.     {
  86.         if ($request->get('redirectTo') || $request->get('redirectTo') === '') {
  87.             $params $this->decodeParam($request'redirectParameters');
  88.             $redirectTo $request->get('redirectTo');
  89.             if ($redirectTo) {
  90.                 return $this->redirectToRoute($redirectTo$params);
  91.             }
  92.             return $this->redirectToRoute('frontend.home.page'$params);
  93.         }
  94.         if ($request->get('forwardTo')) {
  95.             $params $this->decodeParam($request'forwardParameters');
  96.             return $this->forwardToRoute($request->get('forwardTo'), [], $params);
  97.         }
  98.         return new Response();
  99.     }
  100.     /**
  101.      * @param array<string, mixed> $attributes
  102.      * @param array<string, mixed> $routeParameters
  103.      */
  104.     protected function forwardToRoute(string $routeName, array $attributes = [], array $routeParameters = []): Response
  105.     {
  106.         $router $this->container->get('router');
  107.         $url $this->generateUrl($routeName$routeParametersRouter::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.     /**
  128.      * @return array<string, mixed>
  129.      */
  130.     protected function decodeParam(Request $requeststring $param): array
  131.     {
  132.         $params $request->get($param);
  133.         if (\is_string($params)) {
  134.             $params json_decode($paramstrue);
  135.         }
  136.         if (empty($params)) {
  137.             $params = [];
  138.         }
  139.         return $params;
  140.     }
  141.     protected function addCartErrors(Cart $cart, ?\Closure $filter null): void
  142.     {
  143.         $errors $cart->getErrors();
  144.         if ($filter !== null) {
  145.             $errors $errors->filter($filter);
  146.         }
  147.         $groups = [
  148.             'info' => $errors->getNotices(),
  149.             'warning' => $errors->getWarnings(),
  150.             'danger' => $errors->getErrors(),
  151.         ];
  152.         $request $this->container->get('request_stack')->getMainRequest();
  153.         $exists = [];
  154.         if ($request && $request->hasSession() && $request->getSession() instanceof FlashBagAwareSessionInterface) {
  155.             $exists $request->getSession()->getFlashBag()->peekAll();
  156.         }
  157.         $flat = [];
  158.         foreach ($exists as $messages) {
  159.             $flat array_merge($flat$messages);
  160.         }
  161.         /** @var array<string, Error[]> $groups */
  162.         foreach ($groups as $type => $errors) {
  163.             foreach ($errors as $error) {
  164.                 $parameters = [];
  165.                 foreach ($error->getParameters() as $key => $value) {
  166.                     $parameters['%' $key '%'] = $value;
  167.                 }
  168.                 if ($error->getRoute() instanceof ErrorRoute) {
  169.                     $parameters['%url%'] = $this->generateUrl(
  170.                         $error->getRoute()->getKey(),
  171.                         $error->getRoute()->getParams()
  172.                     );
  173.                 }
  174.                 $message $this->trans('checkout.' $error->getMessageKey(), $parameters);
  175.                 if (\in_array($message$flattrue)) {
  176.                     continue;
  177.                 }
  178.                 $this->addFlash($type$message);
  179.             }
  180.         }
  181.     }
  182.     /**
  183.      * @param array<string, mixed> $parameters
  184.      */
  185.     protected function redirectToRoute(string $route, array $parameters = [], int $status Response::HTTP_FOUND): RedirectResponse
  186.     {
  187.         $event = new StorefrontRedirectEvent($route$parameters$status);
  188.         $this->container->get('event_dispatcher')->dispatch($event);
  189.         return parent::redirectToRoute($event->getRoute(), $event->getParameters(), $event->getStatus());
  190.     }
  191.     /**
  192.      * @param array<string, mixed> $parameters
  193.      */
  194.     protected function renderView(string $view, array $parameters = []): string
  195.     {
  196.         $view $this->getTemplateFinder()->find($view);
  197.         if ($this->twig !== null) {
  198.             return $this->twig->render($view$parameters);
  199.         }
  200.         throw new \Exception(
  201.             sprintf('Class %s does not have twig injected. Add to your service definition a method call to setTwig with the twig instance', static::class)
  202.         );
  203.     }
  204.     protected function getTemplateFinder(): TemplateFinder
  205.     {
  206.         return $this->container->get(TemplateFinder::class);
  207.     }
  208.     protected function hook(Hook $hook): void
  209.     {
  210.         $this->container->get(ScriptExecutor::class)->execute($hook);
  211.     }
  212.     protected function getSystemConfigService(): SystemConfigService
  213.     {
  214.         return $this->container->get(SystemConfigService::class);
  215.     }
  216. }