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

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