vendor/contao/core-bundle/src/Routing/AbstractPageRouteProvider.php line 42

Open in your IDE?
  1. <?php
  2. declare(strict_types=1);
  3. /*
  4.  * This file is part of Contao.
  5.  *
  6.  * (c) Leo Feyer
  7.  *
  8.  * @license LGPL-3.0-or-later
  9.  */
  10. namespace Contao\CoreBundle\Routing;
  11. use Contao\CoreBundle\Framework\ContaoFramework;
  12. use Contao\CoreBundle\Routing\Page\PageRegistry;
  13. use Contao\CoreBundle\Routing\Page\PageRoute;
  14. use Contao\CoreBundle\Util\LocaleUtil;
  15. use Contao\Model\Collection;
  16. use Contao\PageModel;
  17. use Symfony\Cmf\Component\Routing\Candidates\CandidatesInterface;
  18. use Symfony\Cmf\Component\Routing\RouteProviderInterface;
  19. use Symfony\Component\HttpFoundation\Request;
  20. use Symfony\Component\Routing\Route;
  21. abstract class AbstractPageRouteProvider implements RouteProviderInterface
  22. {
  23.     protected ContaoFramework $framework;
  24.     protected CandidatesInterface $candidates;
  25.     protected PageRegistry $pageRegistry;
  26.     public function __construct(ContaoFramework $frameworkCandidatesInterface $candidatesPageRegistry $pageRegistry)
  27.     {
  28.         $this->framework $framework;
  29.         $this->candidates $candidates;
  30.         $this->pageRegistry $pageRegistry;
  31.     }
  32.     /**
  33.      * @return array<PageModel>
  34.      */
  35.     protected function findCandidatePages(Request $request): array
  36.     {
  37.         $candidates array_map('strval'$this->candidates->getCandidates($request));
  38.         if (empty($candidates)) {
  39.             return [];
  40.         }
  41.         $ids = [];
  42.         $aliases = [];
  43.         foreach ($candidates as $candidate) {
  44.             if (preg_match('/^[1-9]\d*$/'$candidate)) {
  45.                 $ids[] = (int) $candidate;
  46.             } else {
  47.                 $aliases[] = $candidate;
  48.             }
  49.         }
  50.         $conditions = [];
  51.         if (!empty($ids)) {
  52.             $conditions[] = 'tl_page.id IN ('.implode(','$ids).')';
  53.         }
  54.         if (!empty($aliases)) {
  55.             $conditions[] = 'tl_page.alias IN ('.implode(','array_fill(0, \count($aliases), '?')).')';
  56.         }
  57.         $pageModel $this->framework->getAdapter(PageModel::class);
  58.         $pages $pageModel->findBy([implode(' OR '$conditions)], $aliases);
  59.         if (!$pages instanceof Collection) {
  60.             return [];
  61.         }
  62.         /** @var array<PageModel> $models */
  63.         $models $pages->getModels();
  64.         return array_filter($models, fn (PageModel $model) => $this->pageRegistry->isRoutable($model));
  65.     }
  66.     /**
  67.      * @return array<int>
  68.      */
  69.     protected function getPageIdsFromNames(array $names): array
  70.     {
  71.         $ids = [];
  72.         foreach ($names as $name) {
  73.             if (!== strncmp($name'tl_page.'8)) {
  74.                 continue;
  75.             }
  76.             [, $id] = explode('.'$name);
  77.             if (!preg_match('/^[1-9]\d*$/'$id)) {
  78.                 continue;
  79.             }
  80.             $ids[] = (int) $id;
  81.         }
  82.         return array_unique($ids);
  83.     }
  84.     protected function compareRoutes(Route $aRoute $b, array $languages null): int
  85.     {
  86.         if ('' !== $a->getHost() && '' === $b->getHost()) {
  87.             return -1;
  88.         }
  89.         if ('' === $a->getHost() && '' !== $b->getHost()) {
  90.             return 1;
  91.         }
  92.         /** @var PageModel|null $pageA */
  93.         $pageA $a->getDefault('pageModel');
  94.         /** @var PageModel|null $pageB */
  95.         $pageB $b->getDefault('pageModel');
  96.         // Check if the page models are valid (should always be the case, as routes are generated from pages)
  97.         if (!$pageA instanceof PageModel || !$pageB instanceof PageModel) {
  98.             return 0;
  99.         }
  100.         $langA null;
  101.         $langB null;
  102.         if (null !== $languages && $pageA->rootLanguage !== $pageB->rootLanguage) {
  103.             $fallbackA LocaleUtil::getFallbacks($pageA->rootLanguage);
  104.             $fallbackB LocaleUtil::getFallbacks($pageB->rootLanguage);
  105.             $langA $this->getLocalePriority($fallbackA$fallbackB$languages);
  106.             $langB $this->getLocalePriority($fallbackB$fallbackA$languages);
  107.             if (null === $langA && null === $langB && LocaleUtil::getPrimaryLanguage($pageA->rootLanguage) === LocaleUtil::getPrimaryLanguage($pageB->rootLanguage)) {
  108.                 // If both pages have the same language without region and neither region has a priority,
  109.                 // (e.g. user prefers "de" but we have "de-CH" and "de-DE"), sort by their root page order.
  110.                 $langA $pageA->rootSorting;
  111.                 $langB $pageB->rootSorting;
  112.             }
  113.         }
  114.         if (null === $langA && null === $langB) {
  115.             if ($pageA->rootIsFallback && !$pageB->rootIsFallback) {
  116.                 return -1;
  117.             }
  118.             if ($pageB->rootIsFallback && !$pageA->rootIsFallback) {
  119.                 return 1;
  120.             }
  121.         } else {
  122.             if (null === $langA && null !== $langB) {
  123.                 return 1;
  124.             }
  125.             if (null !== $langA && null === $langB) {
  126.                 return -1;
  127.             }
  128.             if ($langA $langB) {
  129.                 return -1;
  130.             }
  131.             if ($langA $langB) {
  132.                 return 1;
  133.             }
  134.         }
  135.         if ('root' !== $pageA->type && 'root' === $pageB->type) {
  136.             return -1;
  137.         }
  138.         if ('root' === $pageA->type && 'root' !== $pageB->type) {
  139.             return 1;
  140.         }
  141.         if ($pageA->routePriority !== $pageB->routePriority) {
  142.             return $pageB->routePriority <=> $pageA->routePriority;
  143.         }
  144.         $pathA $a instanceof PageRoute && $a->getUrlSuffix() ? substr($a->getPath(), 0, -\strlen($a->getUrlSuffix())) : $a->getPath();
  145.         $pathB $b instanceof PageRoute && $b->getUrlSuffix() ? substr($b->getPath(), 0, -\strlen($b->getUrlSuffix())) : $b->getPath();
  146.         // Prioritize the default behaviour when "requireItem" is enabled
  147.         if ($pathA === $pathB && '{!parameters}' === substr($pathA, -13)) {
  148.             $paramA $a->getRequirement('parameters');
  149.             $paramB $b->getRequirement('parameters');
  150.             if ('/.+?' === $paramA && '(/.+?)?' === $paramB) {
  151.                 return -1;
  152.             }
  153.             if ('(/.+?)?' === $paramA && '/.+?' === $paramB) {
  154.                 return 1;
  155.             }
  156.         }
  157.         $countA = \count(explode('/'$pathA));
  158.         $countB = \count(explode('/'$pathB));
  159.         if ($countA $countB) {
  160.             return -1;
  161.         }
  162.         if ($countB $countA) {
  163.             return 1;
  164.         }
  165.         return strnatcasecmp($pathA$pathB);
  166.     }
  167.     protected function convertLanguagesForSorting(array $languages): array
  168.     {
  169.         $result = [];
  170.         foreach ($languages as $language) {
  171.             if (!$locales LocaleUtil::getFallbacks($language)) {
  172.                 continue;
  173.             }
  174.             $language array_pop($locales);
  175.             $result[] = $language;
  176.             foreach (array_reverse($locales) as $locale) {
  177.                 if (!\in_array($locale$resulttrue)) {
  178.                     $result[] = $locale;
  179.                 }
  180.             }
  181.         }
  182.         return array_flip($result);
  183.     }
  184.     private function getLocalePriority(array $locales, array $notIn, array $languagePriority): ?int
  185.     {
  186.         foreach (array_reverse($locales) as $locale) {
  187.             if (isset($languagePriority[$locale]) && !\in_array($locale$notIntrue)) {
  188.                 return $languagePriority[$locale];
  189.             }
  190.         }
  191.         return null;
  192.     }
  193. }