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

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