vendor/shopware/core/Content/Seo/SeoResolver.php line 51

  1. <?php declare(strict_types=1);
  2. namespace Shopware\Core\Content\Seo;
  3. use Doctrine\DBAL\Connection;
  4. use Shopware\Core\Framework\DataAbstractionLayer\Dbal\QueryBuilder;
  5. use Shopware\Core\Framework\Log\Package;
  6. use Shopware\Core\Framework\Plugin\Exception\DecorationPatternException;
  7. use Shopware\Core\Framework\Uuid\Uuid;
  8. /**
  9.  * @phpstan-import-type ResolvedSeoUrl from AbstractSeoResolver
  10.  */
  11. #[Package('sales-channel')]
  12. class SeoResolver extends AbstractSeoResolver
  13. {
  14.     /**
  15.      * @internal
  16.      */
  17.     public function __construct(private readonly Connection $connection)
  18.     {
  19.     }
  20.     public function getDecorated(): AbstractSeoResolver
  21.     {
  22.         throw new DecorationPatternException(self::class);
  23.     }
  24.     /**
  25.      * @return ResolvedSeoUrl
  26.      */
  27.     public function resolve(string $languageId, string $salesChannelId, string $pathInfo): array
  28.     {
  29.         $seoPathInfo = ltrim($pathInfo, '/');
  30.         $query = (new QueryBuilder($this->connection))
  31.             ->select('id', 'path_info pathInfo', 'is_canonical isCanonical')
  32.             ->from('seo_url')
  33.             ->where('language_id = :language_id')
  34.             ->andWhere('(sales_channel_id = :sales_channel_id OR sales_channel_id IS NULL)')
  35.             ->andWhere('seo_path_info = :seoPath')
  36.             ->orderBy('seo_path_info')
  37.             ->addOrderBy('sales_channel_id IS NULL') // sales_channel_specific comes first
  38.             ->setMaxResults(1)
  39.             ->setParameter('language_id', Uuid::fromHexToBytes($languageId))
  40.             ->setParameter('sales_channel_id', Uuid::fromHexToBytes($salesChannelId))
  41.             ->setParameter('seoPath', $seoPathInfo);
  42.         $query->setTitle('seo-url::resolve');
  43.         $seoPath = $query->executeQuery()->fetchAssociative();
  44.         $seoPath = $seoPath !== false
  45.             ? $seoPath
  46.             : ['pathInfo' => $seoPathInfo, 'isCanonical' => false];
  47.         if (!$seoPath['isCanonical']) {
  48.             $query = $this->connection->createQueryBuilder()
  49.                 ->select('path_info pathInfo', 'seo_path_info seoPathInfo')
  50.                 ->from('seo_url')
  51.                 ->where('language_id = :language_id')
  52.                 ->andWhere('sales_channel_id = :sales_channel_id')
  53.                 ->andWhere('id != :id')
  54.                 ->andWhere('path_info = :pathInfo')
  55.                 ->andWhere('is_canonical = 1')
  56.                 ->setMaxResults(1)
  57.                 ->setParameter('language_id', Uuid::fromHexToBytes($languageId))
  58.                 ->setParameter('sales_channel_id', Uuid::fromHexToBytes($salesChannelId))
  59.                 ->setParameter('id', $seoPath['id'] ?? '')
  60.                 ->setParameter('pathInfo', '/' . ltrim((string) $seoPath['pathInfo'], '/'));
  61.             $canonical = $query->executeQuery()->fetchAssociative();
  62.             if ($canonical) {
  63.                 $seoPath['canonicalPathInfo'] = '/' . ltrim((string) $canonical['seoPathInfo'], '/');
  64.             }
  65.         }
  66.         $seoPath['pathInfo'] = '/' . ltrim((string) $seoPath['pathInfo'], '/');
  67.         return $seoPath;
  68.     }
  69. }