vendor/shopware/core/Content/Seo/SeoUrlUpdater.php line 114

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. namespace Shopware\Core\Content\Seo;
  3. use Doctrine\DBAL\Connection;
  4. use Shopware\Core\Content\Seo\SeoUrlRoute\SeoUrlRouteRegistry;
  5. use Shopware\Core\Defaults;
  6. use Shopware\Core\Framework\Api\Context\SystemSource;
  7. use Shopware\Core\Framework\Context;
  8. use Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface;
  9. use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
  10. use Shopware\Core\Framework\Feature;
  11. use Shopware\Core\Framework\Log\Package;
  12. use Shopware\Core\System\Language\LanguageEntity;
  13. use Shopware\Core\System\SalesChannel\SalesChannelCollection;
  14. /**
  15.  * This class can be used to regenerate the seo urls for a route and an offset at ids.
  16.  */
  17. #[Package('sales-channel')]
  18. class SeoUrlUpdater
  19. {
  20.     /**
  21.      * @var EntityRepositoryInterface
  22.      */
  23.     private $languageRepository;
  24.     /**
  25.      * @var SeoUrlRouteRegistry
  26.      */
  27.     private $seoUrlRouteRegistry;
  28.     /**
  29.      * @var SeoUrlGenerator
  30.      */
  31.     private $seoUrlGenerator;
  32.     /**
  33.      * @var SeoUrlPersister
  34.      */
  35.     private $seoUrlPersister;
  36.     /**
  37.      * @var Connection
  38.      */
  39.     private $connection;
  40.     /**
  41.      * @var EntityRepositoryInterface
  42.      */
  43.     private $salesChannelRepository;
  44.     /**
  45.      * @internal
  46.      */
  47.     public function __construct(
  48.         EntityRepositoryInterface $languageRepository,
  49.         SeoUrlRouteRegistry $seoUrlRouteRegistry,
  50.         SeoUrlGenerator $seoUrlGenerator,
  51.         SeoUrlPersister $seoUrlPersister,
  52.         Connection $connection,
  53.         EntityRepositoryInterface $salesChannelRepository
  54.     ) {
  55.         $this->languageRepository $languageRepository;
  56.         $this->seoUrlRouteRegistry $seoUrlRouteRegistry;
  57.         $this->seoUrlGenerator $seoUrlGenerator;
  58.         $this->seoUrlPersister $seoUrlPersister;
  59.         $this->connection $connection;
  60.         $this->salesChannelRepository $salesChannelRepository;
  61.     }
  62.     public function update(string $routeName, array $ids): void
  63.     {
  64.         $templates $this->loadTemplates([$routeName]);
  65.         if (empty($templates)) {
  66.             return;
  67.         }
  68.         $context Context::createDefaultContext();
  69.         $languages $this->languageRepository->search(new Criteria(), $context);
  70.         $languageChains $this->fetchLanguageChains($languages->getEntities()->getElements());
  71.         $salesChannels $this->fetchSalesChannels();
  72.         $route $this->seoUrlRouteRegistry->findByRouteName($routeName);
  73.         if (!$route) {
  74.             throw new \RuntimeException(sprintf('Route by name %s not found'$routeName));
  75.         }
  76.         foreach ($templates as $config) {
  77.             $salesChannelId $config['salesChannelId'];
  78.             $languageId $config['languageId'];
  79.             $template $config['template'] ?? '';
  80.             if ($template === '') {
  81.                 continue;
  82.             }
  83.             $chain $languageChains[$languageId];
  84.             $context = new Context(new SystemSource(), [], Defaults::CURRENCY$chain);
  85.             $context->setConsiderInheritance(true);
  86.             $salesChannel $salesChannels->get($salesChannelId);
  87.             if ($salesChannel === null && Feature::isActive('FEATURE_NEXT_13410')) {
  88.                 continue;
  89.             }
  90.             // generate new seo urls
  91.             $urls $this->seoUrlGenerator->generate($ids$template$route$context$salesChannel);
  92.             // persist seo urls to storage
  93.             $this->seoUrlPersister->updateSeoUrls($context$routeName$ids$urls$salesChannel);
  94.         }
  95.     }
  96.     private function loadTemplates(array $routes): array
  97.     {
  98.         $domains $this->connection->fetchAllAssociative(
  99.             'SELECT DISTINCT
  100.                LOWER(HEX(sales_channel.id)) as salesChannelId,
  101.                LOWER(HEX(domains.language_id)) as languageId
  102.              FROM sales_channel_domain as domains
  103.              INNER JOIN sales_channel
  104.                ON domains.sales_channel_id = sales_channel.id
  105.                AND sales_channel.active = 1'
  106.         );
  107.         if ($routes === [] || $domains === []) {
  108.             return [];
  109.         }
  110.         $modified $this->connection->fetchAllAssociative(
  111.             'SELECT LOWER(HEX(sales_channel_id)) as sales_channel_id, route_name, template
  112.              FROM seo_url_template
  113.              WHERE route_name IN (:routes)',
  114.             ['routes' => $routes],
  115.             ['routes' => Connection::PARAM_STR_ARRAY]
  116.         );
  117.         if ($modified === []) {
  118.             return [];
  119.         }
  120.         $grouped = [];
  121.         foreach ($modified as $template) {
  122.             $grouped[$template['sales_channel_id']][$template['route_name']] = $template['template'];
  123.         }
  124.         if (!\array_key_exists(''$grouped)) {
  125.             throw new \RuntimeException('Default templates not configured');
  126.         }
  127.         $defaults $grouped[''];
  128.         $result = [];
  129.         foreach ($domains as $domain) {
  130.             $salesChannelId $domain['salesChannelId'];
  131.             foreach ($routes as $route) {
  132.                 $template $defaults[$route];
  133.                 if (isset($grouped[$salesChannelId][$route])) {
  134.                     $template $grouped[$salesChannelId][$route];
  135.                 }
  136.                 $result[] = [
  137.                     'salesChannelId' => $salesChannelId,
  138.                     'languageId' => $domain['languageId'],
  139.                     'route' => $route,
  140.                     'template' => $template,
  141.                 ];
  142.             }
  143.         }
  144.         return $result;
  145.     }
  146.     private function fetchSalesChannels(): SalesChannelCollection
  147.     {
  148.         $context Context::createDefaultContext();
  149.         /** @var SalesChannelCollection $entities */
  150.         $entities $this->salesChannelRepository->search(new Criteria(), $context)->getEntities();
  151.         return $entities;
  152.     }
  153.     private function fetchLanguageChains(array $languages): array
  154.     {
  155.         $languageChains = [];
  156.         /** @var LanguageEntity $language */
  157.         foreach ($languages as $language) {
  158.             $languageId $language->getId();
  159.             $languageChains[$languageId] = [
  160.                 $languageId,
  161.                 $language->getParentId(),
  162.                 Defaults::LANGUAGE_SYSTEM,
  163.             ];
  164.         }
  165.         return $languageChains;
  166.     }
  167. }