vendor/shopware/core/Content/Seo/SalesChannel/StoreApiSeoResolver.php line 74

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. namespace Shopware\Core\Content\Seo\SalesChannel;
  3. use Shopware\Core\Content\Category\CategoryEntity;
  4. use Shopware\Core\Content\Product\SalesChannel\SalesChannelProductEntity;
  5. use Shopware\Core\Content\Seo\SeoUrl\SeoUrlCollection;
  6. use Shopware\Core\Content\Seo\SeoUrl\SeoUrlEntity;
  7. use Shopware\Core\Content\Seo\SeoUrlRoute\SeoUrlRouteInterface as SeoUrlRouteConfigRoute;
  8. use Shopware\Core\Content\Seo\SeoUrlRoute\SeoUrlRouteRegistry;
  9. use Shopware\Core\Framework\DataAbstractionLayer\DefinitionInstanceRegistry;
  10. use Shopware\Core\Framework\DataAbstractionLayer\Entity;
  11. use Shopware\Core\Framework\DataAbstractionLayer\Search\AggregationResult\AggregationResultCollection;
  12. use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
  13. use Shopware\Core\Framework\DataAbstractionLayer\Search\EntitySearchResult;
  14. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsAnyFilter;
  15. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
  16. use Shopware\Core\Framework\DataAbstractionLayer\Search\Sorting\FieldSorting;
  17. use Shopware\Core\Framework\Struct\Collection;
  18. use Shopware\Core\Framework\Struct\Struct;
  19. use Shopware\Core\PlatformRequest;
  20. use Shopware\Core\System\SalesChannel\Entity\SalesChannelDefinitionInstanceRegistry;
  21. use Shopware\Core\System\SalesChannel\Entity\SalesChannelRepositoryInterface;
  22. use Shopware\Core\System\SalesChannel\SalesChannelContext;
  23. use Shopware\Core\System\SalesChannel\StoreApiResponse;
  24. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  25. use Symfony\Component\HttpKernel\Event\ResponseEvent;
  26. use Symfony\Component\HttpKernel\KernelEvents;
  27. class StoreApiSeoResolver implements EventSubscriberInterface
  28. {
  29.     /**
  30.      * @var SalesChannelRepositoryInterface
  31.      */
  32.     private $salesChannelRepository;
  33.     /**
  34.      * @var DefinitionInstanceRegistry
  35.      */
  36.     private $definitionInstanceRegistry;
  37.     /**
  38.      * @var SeoUrlRouteRegistry
  39.      */
  40.     private $seoUrlRouteRegistry;
  41.     /**
  42.      * @var SalesChannelDefinitionInstanceRegistry
  43.      */
  44.     private $salesChannelDefinitionInstanceRegistry;
  45.     /**
  46.      * @internal
  47.      */
  48.     public function __construct(
  49.         SalesChannelRepositoryInterface $salesChannelRepository,
  50.         DefinitionInstanceRegistry $definitionInstanceRegistry,
  51.         SalesChannelDefinitionInstanceRegistry $salesChannelDefinitionInstanceRegistry,
  52.         SeoUrlRouteRegistry $seoUrlRouteRegistry
  53.     ) {
  54.         $this->salesChannelRepository $salesChannelRepository;
  55.         $this->definitionInstanceRegistry $definitionInstanceRegistry;
  56.         $this->seoUrlRouteRegistry $seoUrlRouteRegistry;
  57.         $this->salesChannelDefinitionInstanceRegistry $salesChannelDefinitionInstanceRegistry;
  58.     }
  59.     public static function getSubscribedEvents(): array
  60.     {
  61.         return [
  62.             KernelEvents::RESPONSE => ['addSeoInformation'10000],
  63.         ];
  64.     }
  65.     public function addSeoInformation(ResponseEvent $event): void
  66.     {
  67.         $response $event->getResponse();
  68.         if (!$response instanceof StoreApiResponse) {
  69.             return;
  70.         }
  71.         if (!$event->getRequest()->headers->has(PlatformRequest::HEADER_INCLUDE_SEO_URLS)) {
  72.             return;
  73.         }
  74.         $dataBag = new SeoResolverData();
  75.         $this->find($dataBag$response->getObject());
  76.         $this->enrich($dataBag$event->getRequest()->attributes->get(PlatformRequest::ATTRIBUTE_SALES_CHANNEL_CONTEXT_OBJECT));
  77.     }
  78.     private function find(SeoResolverData $dataStruct $struct): void
  79.     {
  80.         if ($struct instanceof AggregationResultCollection) {
  81.             foreach ($struct as $item) {
  82.                 $this->findStruct($data$item);
  83.             }
  84.         }
  85.         if ($struct instanceof EntitySearchResult) {
  86.             foreach ($struct->getEntities() as $entity) {
  87.                 $this->findStruct($data$entity);
  88.             }
  89.         }
  90.         if ($struct instanceof Collection) {
  91.             foreach ($struct as $item) {
  92.                 $this->findStruct($data$item);
  93.             }
  94.         }
  95.         $this->findStruct($data$struct);
  96.     }
  97.     private function findStruct(SeoResolverData $dataStruct $struct): void
  98.     {
  99.         if ($struct instanceof Entity) {
  100.             $definition $this->definitionInstanceRegistry->getByEntityClass($struct) ?? $this->salesChannelDefinitionInstanceRegistry->getByEntityClass($struct);
  101.             if ($definition && $definition->isSeoAware()) {
  102.                 $data->add($definition->getEntityName(), $struct);
  103.             }
  104.         }
  105.         foreach ($struct->getVars() as $item) {
  106.             if ($item instanceof Collection) {
  107.                 foreach ($item as $collectionItem) {
  108.                     if ($collectionItem instanceof Struct) {
  109.                         $this->findStruct($data$collectionItem);
  110.                     }
  111.                 }
  112.             } elseif ($item instanceof Struct) {
  113.                 $this->findStruct($data$item);
  114.             }
  115.         }
  116.     }
  117.     private function enrich(SeoResolverData $dataSalesChannelContext $context): void
  118.     {
  119.         foreach ($data->getEntities() as $definition) {
  120.             $definition = (string) $definition;
  121.             $ids $data->getIds($definition);
  122.             $routes $this->seoUrlRouteRegistry->findByDefinition($definition);
  123.             if (\count($routes) === 0) {
  124.                 continue;
  125.             }
  126.             $routes array_map(static function (SeoUrlRouteConfigRoute $seoUrlRoute) {
  127.                 return $seoUrlRoute->getConfig()->getRouteName();
  128.             }, $routes);
  129.             $criteria = new Criteria();
  130.             $criteria->addFilter(new EqualsFilter('isCanonical'true));
  131.             $criteria->addFilter(new EqualsAnyFilter('routeName'$routes));
  132.             $criteria->addFilter(new EqualsAnyFilter('foreignKey'$ids));
  133.             $criteria->addFilter(new EqualsFilter('languageId'$context->getContext()->getLanguageId()));
  134.             $criteria->addSorting(new FieldSorting('salesChannelId'));
  135.             /** @var SeoUrlEntity $url */
  136.             foreach ($this->salesChannelRepository->search($criteria$context) as $url) {
  137.                 /** @var SalesChannelProductEntity|CategoryEntity $entity */
  138.                 $entity $data->get($definition$url->getForeignKey());
  139.                 if ($entity->getSeoUrls() === null) {
  140.                     $entity->setSeoUrls(new SeoUrlCollection());
  141.                 }
  142.                 /** @phpstan-ignore-next-line - will complain that 'getSeoUrls' might be null, but we will set it if it is null */
  143.                 $entity->getSeoUrls()->add($url);
  144.             }
  145.         }
  146.     }
  147. }