vendor/ibexa/core/src/lib/Persistence/Cache/UrlAliasHandler.php line 237

Open in your IDE?
  1. <?php
  2. /**
  3.  * @copyright Copyright (C) Ibexa AS. All rights reserved.
  4.  * @license For full copyright and license information view LICENSE file distributed with this source code.
  5.  */
  6. namespace Ibexa\Core\Persistence\Cache;
  7. use Ibexa\Contracts\Core\Persistence\Content\UrlAlias;
  8. use Ibexa\Contracts\Core\Persistence\Content\UrlAlias\Handler as UrlAliasHandlerInterface;
  9. use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException as APINotFoundException;
  10. use Ibexa\Core\Base\Exceptions\NotFoundException;
  11. class UrlAliasHandler extends AbstractInMemoryPersistenceHandler implements UrlAliasHandlerInterface
  12. {
  13.     private const URL_ALIAS_LOCATION_IDENTIFIER 'url_alias_location';
  14.     private const URL_ALIAS_LOCATION_PATH_IDENTIFIER 'url_alias_location_path';
  15.     private const URL_ALIAS_NOT_FOUND_IDENTIFIER 'url_alias_not_found';
  16.     private const URL_ALIAS_IDENTIFIER 'url_alias';
  17.     private const URL_ALIAS_LOCATION_LIST_IDENTIFIER 'url_alias_location_list';
  18.     private const URL_ALIAS_LOCATION_LIST_CUSTOM_IDENTIFIER 'url_alias_location_list_custom';
  19.     private const URL_ALIAS_CUSTOM_IDENTIFIER 'url_alias_custom';
  20.     private const URL_ALIAS_URL_IDENTIFIER 'url_alias_url';
  21.     private const URL_ALIAS_WITH_HASH_IDENTIFIER 'url_alias_with_hash';
  22.     /**
  23.      * Constant used for storing not found results for lookup().
  24.      */
  25.     public const NOT_FOUND 0;
  26.     /**
  27.      * {@inheritdoc}
  28.      */
  29.     public function publishUrlAliasForLocation(
  30.         $locationId,
  31.         $parentLocationId,
  32.         $name,
  33.         $languageCode,
  34.         $alwaysAvailable false,
  35.         $updatePathIdentificationString false
  36.     ): string {
  37.         $this->logger->logCall(
  38.             __METHOD__,
  39.             [
  40.                 'location' => $locationId,
  41.                 'parent' => $parentLocationId,
  42.                 'name' => $name,
  43.                 'language' => $languageCode,
  44.                 'alwaysAvailable' => $alwaysAvailable,
  45.             ]
  46.         );
  47.         $urlAliasHandler $this->persistenceHandler->urlAliasHandler();
  48.         $urlAliasIdentity $urlAliasHandler->publishUrlAliasForLocation(
  49.             $locationId,
  50.             $parentLocationId,
  51.             $name,
  52.             $languageCode,
  53.             $alwaysAvailable,
  54.             $updatePathIdentificationString
  55.         );
  56.         $this->cache->invalidateTags([
  57.             $this->cacheIdentifierGenerator->generateTag(self::URL_ALIAS_LOCATION_IDENTIFIER, [$locationId]),
  58.             $this->cacheIdentifierGenerator->generateTag(self::URL_ALIAS_LOCATION_PATH_IDENTIFIER, [$locationId]),
  59.             $this->cacheIdentifierGenerator->generateTag(self::URL_ALIAS_IDENTIFIER, [$urlAliasIdentity]),
  60.             $this->cacheIdentifierGenerator->generateTag(self::URL_ALIAS_NOT_FOUND_IDENTIFIER),
  61.         ]);
  62.         return $urlAliasIdentity;
  63.     }
  64.     /**
  65.      * {@inheritdoc}
  66.      */
  67.     public function createCustomUrlAlias($locationId$path$forwarding false$languageCode null$alwaysAvailable false)
  68.     {
  69.         $this->logger->logCall(
  70.             __METHOD__,
  71.             [
  72.                 'location' => $locationId,
  73.                 '$path' => $path,
  74.                 '$forwarding' => $forwarding,
  75.                 'language' => $languageCode,
  76.                 'alwaysAvailable' => $alwaysAvailable,
  77.             ]
  78.         );
  79.         $urlAlias $this->persistenceHandler->urlAliasHandler()->createCustomUrlAlias(
  80.             $locationId,
  81.             $path,
  82.             $forwarding,
  83.             $languageCode,
  84.             $alwaysAvailable
  85.         );
  86.         $this->cache->invalidateTags([
  87.             $this->cacheIdentifierGenerator->generateTag(self::URL_ALIAS_LOCATION_IDENTIFIER, [$locationId]),
  88.             $this->cacheIdentifierGenerator->generateTag(self::URL_ALIAS_LOCATION_PATH_IDENTIFIER, [$locationId]),
  89.             $this->cacheIdentifierGenerator->generateTag(self::URL_ALIAS_NOT_FOUND_IDENTIFIER),
  90.             $this->cacheIdentifierGenerator->generateTag(self::URL_ALIAS_IDENTIFIER, [$urlAlias->id]),
  91.         ]);
  92.         return $urlAlias;
  93.     }
  94.     /**
  95.      * {@inheritdoc}
  96.      */
  97.     public function createGlobalUrlAlias($resource$path$forwarding false$languageCode null$alwaysAvailable false)
  98.     {
  99.         $this->logger->logCall(
  100.             __METHOD__,
  101.             [
  102.                 'resource' => $resource,
  103.                 'path' => $path,
  104.                 'forwarding' => $forwarding,
  105.                 'language' => $languageCode,
  106.                 'alwaysAvailable' => $alwaysAvailable,
  107.             ]
  108.         );
  109.         $urlAlias $this->persistenceHandler->urlAliasHandler()->createGlobalUrlAlias(
  110.             $resource,
  111.             $path,
  112.             $forwarding,
  113.             $languageCode,
  114.             $alwaysAvailable
  115.         );
  116.         $this->cache->invalidateTags([
  117.             $this->cacheIdentifierGenerator->generateTag(self::URL_ALIAS_NOT_FOUND_IDENTIFIER),
  118.         ]);
  119.         return $urlAlias;
  120.     }
  121.     /**
  122.      * {@inheritdoc}
  123.      */
  124.     public function listGlobalURLAliases($languageCode null$offset 0$limit = -1)
  125.     {
  126.         $this->logger->logCall(__METHOD__, ['language' => $languageCode'offset' => $offset'limit' => $limit]);
  127.         return $this->persistenceHandler->urlAliasHandler()->listGlobalURLAliases($languageCode$offset$limit);
  128.     }
  129.     /**
  130.      * {@inheritdoc}
  131.      */
  132.     public function listURLAliasesForLocation($locationId$custom false)
  133.     {
  134.         return $this->getListCacheValue(
  135.             ($custom) ?
  136.                 $this->cacheIdentifierGenerator->generateKey(self::URL_ALIAS_LOCATION_LIST_CUSTOM_IDENTIFIER, [$locationId], true) :
  137.                 $this->cacheIdentifierGenerator->generateKey(self::URL_ALIAS_LOCATION_LIST_IDENTIFIER, [$locationId], true),
  138.             function () use ($locationId$custom) {
  139.                 return $this->persistenceHandler->urlAliasHandler()->listURLAliasesForLocation($locationId$custom);
  140.             },
  141.             function (UrlAlias $alias) {
  142.                 $tags = [
  143.                     $this->cacheIdentifierGenerator->generateTag(self::URL_ALIAS_IDENTIFIER, [$alias->id]),
  144.                 ];
  145.                 if ($alias->type === UrlAlias::LOCATION) {
  146.                     $tags[] = $this->cacheIdentifierGenerator->generateTag(self::URL_ALIAS_LOCATION_IDENTIFIER, [$alias->destination]);
  147.                     $location $this->persistenceHandler->locationHandler()->load($alias->destination);
  148.                     $pathIds $this->locationPathConverter->convertToPathIds($location->pathString);
  149.                     foreach ($pathIds as $pathId) {
  150.                         $tags[] = $this->cacheIdentifierGenerator->generateTag(self::URL_ALIAS_LOCATION_PATH_IDENTIFIER, [$pathId]);
  151.                     }
  152.                 }
  153.                 return $tags;
  154.             },
  155.             static function () { return []; },
  156.             function () use ($locationId) {
  157.                 return [
  158.                     $this->cacheIdentifierGenerator->generateTag(self::URL_ALIAS_LOCATION_IDENTIFIER, [$locationId]),
  159.                 ];
  160.             },
  161.             ['location' => $locationId'custom' => $custom]
  162.         );
  163.     }
  164.     /**
  165.      * {@inheritdoc}
  166.      */
  167.     public function removeURLAliases(array $urlAliases)
  168.     {
  169.         $this->logger->logCall(__METHOD__, ['aliases' => $urlAliases]);
  170.         $return $this->persistenceHandler->urlAliasHandler()->removeURLAliases($urlAliases);
  171.         $cacheTags = [];
  172.         foreach ($urlAliases as $urlAlias) {
  173.             $cacheTags[] = $this->cacheIdentifierGenerator->generateTag(self::URL_ALIAS_IDENTIFIER, [$urlAlias->id]);
  174.             if ($urlAlias->type === UrlAlias::LOCATION) {
  175.                 $cacheTags[] = $this->cacheIdentifierGenerator->generateTag(self::URL_ALIAS_LOCATION_IDENTIFIER, [$urlAlias->destination]);
  176.                 $cacheTags[] = $this->cacheIdentifierGenerator->generateTag(self::URL_ALIAS_LOCATION_PATH_IDENTIFIER, [$urlAlias->destination]);
  177.             }
  178.             if ($urlAlias->isCustom) {
  179.                 $cacheTags[] = $this->cacheIdentifierGenerator->generateTag(self::URL_ALIAS_CUSTOM_IDENTIFIER, [$urlAlias->destination]);
  180.             }
  181.         }
  182.         $this->cache->invalidateTags($cacheTags);
  183.         return $return;
  184.     }
  185.     /**
  186.      * {@inheritdoc}
  187.      */
  188.     public function lookup($url)
  189.     {
  190.         $cacheItem $this->cache->getItem(
  191.             $this->cacheIdentifierGenerator->generateKey(
  192.                 self::URL_ALIAS_URL_IDENTIFIER,
  193.                 [$this->cacheIdentifierSanitizer->escapeForCacheKey($url)],
  194.                 true
  195.             )
  196.         );
  197.         if ($cacheItem->isHit()) {
  198.             $this->logger->logCacheHit(['url' => $url]);
  199.             if (($return $cacheItem->get()) === self::NOT_FOUND) {
  200.                 throw new NotFoundException('UrlAlias'$url);
  201.             }
  202.             return $return;
  203.         }
  204.         $this->logger->logCacheMiss(['url' => $url]);
  205.         try {
  206.             $urlAlias $this->persistenceHandler->urlAliasHandler()->lookup($url);
  207.         } catch (APINotFoundException $e) {
  208.             $cacheItem->set(self::NOT_FOUND)
  209.                 ->expiresAfter(30)
  210.                 ->tag([
  211.                     $this->cacheIdentifierGenerator->generateKey(self::URL_ALIAS_NOT_FOUND_IDENTIFIER),
  212.                 ]);
  213.             $this->cache->save($cacheItem);
  214.             throw $e;
  215.         }
  216.         $cacheItem->set($urlAlias);
  217.         $cacheItem->tag($this->getCacheTags($urlAlias));
  218.         $this->cache->save($cacheItem);
  219.         return $urlAlias;
  220.     }
  221.     /**
  222.      * {@inheritdoc}
  223.      */
  224.     public function loadUrlAlias($id)
  225.     {
  226.         $cacheItem $this->cache->getItem(
  227.             $this->cacheIdentifierGenerator->generateKey(self::URL_ALIAS_IDENTIFIER, [$id], true)
  228.         );
  229.         if ($cacheItem->isHit()) {
  230.             $this->logger->logCacheHit(['alias' => $id]);
  231.             return $cacheItem->get();
  232.         }
  233.         $this->logger->logCacheMiss(['alias' => $id]);
  234.         $urlAlias $this->persistenceHandler->urlAliasHandler()->loadUrlAlias($id);
  235.         $cacheItem->set($urlAlias);
  236.         $cacheItem->tag($this->getCacheTags($urlAlias));
  237.         $this->cache->save($cacheItem);
  238.         return $urlAlias;
  239.     }
  240.     /**
  241.      * {@inheritdoc}
  242.      */
  243.     public function locationMoved($locationId$oldParentId$newParentId)
  244.     {
  245.         $this->logger->logCall(
  246.             __METHOD__,
  247.             [
  248.                 'location' => $locationId,
  249.                 'oldParent' => $oldParentId,
  250.                 'newParent' => $newParentId,
  251.             ]
  252.         );
  253.         $return $this->persistenceHandler->urlAliasHandler()->locationMoved($locationId$oldParentId$newParentId);
  254.         if ($oldParentId !== $newParentId) {
  255.             $this->cache->invalidateTags([
  256.                 $this->cacheIdentifierGenerator->generateTag(self::URL_ALIAS_LOCATION_IDENTIFIER, [$locationId]),
  257.                 $this->cacheIdentifierGenerator->generateTag(self::URL_ALIAS_LOCATION_PATH_IDENTIFIER, [$locationId]),
  258.             ]);
  259.         }
  260.         return $return;
  261.     }
  262.     /**
  263.      * {@inheritdoc}
  264.      */
  265.     public function locationCopied($locationId$newLocationId$newParentId)
  266.     {
  267.         $this->logger->logCall(
  268.             __METHOD__,
  269.             [
  270.                 'oldLocation' => $locationId,
  271.                 'newLocation' => $newLocationId,
  272.                 'newParent' => $newParentId,
  273.             ]
  274.         );
  275.         $return $this->persistenceHandler->urlAliasHandler()->locationCopied(
  276.             $locationId,
  277.             $newLocationId,
  278.             $newParentId
  279.         );
  280.         $this->cache->invalidateTags([
  281.             $this->cacheIdentifierGenerator->generateTag(self::URL_ALIAS_LOCATION_IDENTIFIER, [$locationId]),
  282.             $this->cacheIdentifierGenerator->generateTag(self::URL_ALIAS_LOCATION_IDENTIFIER, [$newLocationId]),
  283.         ]);
  284.         return $return;
  285.     }
  286.     /**
  287.      * {@inheritdoc}
  288.      */
  289.     public function locationDeleted($locationId): array
  290.     {
  291.         $this->logger->logCall(__METHOD__, ['location' => $locationId]);
  292.         $childrenAliases $this->persistenceHandler->urlAliasHandler()
  293.             ->locationDeleted($locationId);
  294.         $tags = [
  295.             $this->cacheIdentifierGenerator->generateTag(self::URL_ALIAS_LOCATION_IDENTIFIER, [$locationId]),
  296.             $this->cacheIdentifierGenerator->generateTag(self::URL_ALIAS_LOCATION_PATH_IDENTIFIER, [$locationId]),
  297.         ];
  298.         foreach ($childrenAliases as $childAlias) {
  299.             $tags[] = $this->cacheIdentifierGenerator->generateTag(
  300.                 self::URL_ALIAS_WITH_HASH_IDENTIFIER,
  301.                 [$locationId$childAlias['text_md5']]
  302.             );
  303.         }
  304.         $this->cache->invalidateTags($tags);
  305.         return $childrenAliases;
  306.     }
  307.     /**
  308.      * {@inheritdoc}
  309.      */
  310.     public function locationSwapped($location1Id$location1ParentId$location2Id$location2ParentId)
  311.     {
  312.         $this->logger->logCall(
  313.             __METHOD__,
  314.             [
  315.                 'location1Id' => $location1Id,
  316.                 'location1ParentId' => $location1ParentId,
  317.                 'location2Id' => $location2Id,
  318.                 'location2ParentId' => $location2ParentId,
  319.             ]
  320.         );
  321.         $return $this->persistenceHandler->urlAliasHandler()->locationSwapped(
  322.             $location1Id,
  323.             $location1ParentId,
  324.             $location2Id,
  325.             $location2ParentId
  326.         );
  327.         $this->cache->invalidateTags(
  328.             [
  329.                 $this->cacheIdentifierGenerator->generateTag(self::URL_ALIAS_LOCATION_IDENTIFIER, [$location1Id]),
  330.                 $this->cacheIdentifierGenerator->generateTag(self::URL_ALIAS_LOCATION_PATH_IDENTIFIER, [$location1Id]),
  331.                 $this->cacheIdentifierGenerator->generateTag(self::URL_ALIAS_LOCATION_IDENTIFIER, [$location2Id]),
  332.                 $this->cacheIdentifierGenerator->generateTag(self::URL_ALIAS_LOCATION_PATH_IDENTIFIER, [$location2Id]),
  333.             ]
  334.         );
  335.         return $return;
  336.     }
  337.     /**
  338.      * {@inheritdoc}
  339.      */
  340.     public function translationRemoved(array $locationIds$languageCode)
  341.     {
  342.         $this->logger->logCall(
  343.             __METHOD__,
  344.             ['locations' => implode(','$locationIds), 'language' => $languageCode]
  345.         );
  346.         $this->persistenceHandler->urlAliasHandler()->translationRemoved($locationIds$languageCode);
  347.         $locationTags = [];
  348.         foreach ($locationIds as $locationId) {
  349.             $locationTags[] = $this->cacheIdentifierGenerator->generateTag(self::URL_ALIAS_LOCATION_IDENTIFIER, [$locationId]);
  350.             $locationTags[] = $this->cacheIdentifierGenerator->generateTag(self::URL_ALIAS_LOCATION_PATH_IDENTIFIER, [$locationId]);
  351.         }
  352.         $this->cache->invalidateTags($locationTags);
  353.     }
  354.     /**
  355.      * {@inheritdoc}
  356.      */
  357.     public function archiveUrlAliasesForDeletedTranslations($locationId$parentLocationId, array $languageCodes)
  358.     {
  359.         $this->logger->logCall(
  360.             __METHOD__,
  361.             [
  362.                 'locationId' => $locationId,
  363.                 'parentLocationId' => $parentLocationId,
  364.                 'languageCodes' => implode(','$languageCodes),
  365.             ]
  366.         );
  367.         $this->persistenceHandler->urlAliasHandler()->archiveUrlAliasesForDeletedTranslations(
  368.             $locationId,
  369.             $parentLocationId,
  370.             $languageCodes
  371.         );
  372.         $this->cache->invalidateTags([
  373.             $this->cacheIdentifierGenerator->generateTag(self::URL_ALIAS_LOCATION_IDENTIFIER, [$locationId]),
  374.             $this->cacheIdentifierGenerator->generateTag(self::URL_ALIAS_LOCATION_PATH_IDENTIFIER, [$locationId]),
  375.         ]);
  376.     }
  377.     /**
  378.      * Return relevant UrlAlias and optionally UrlAlias location tags so cache can be purged reliably.
  379.      *
  380.      * For use when generating cache, not on invalidation.
  381.      *
  382.      * @param \Ibexa\Contracts\Core\Persistence\Content\UrlAlias $urlAlias
  383.      * @param array $tags Optional, can be used to specify other tags.
  384.      *
  385.      * @return array
  386.      */
  387.     private function getCacheTags(UrlAlias $urlAlias, array $tags = [])
  388.     {
  389.         $tags[] = $this->cacheIdentifierGenerator->generateTag(self::URL_ALIAS_IDENTIFIER, [$urlAlias->id]);
  390.         if ($urlAlias->type === UrlAlias::LOCATION) {
  391.             $tags[] = $this->cacheIdentifierGenerator->generateTag(self::URL_ALIAS_LOCATION_IDENTIFIER, [$urlAlias->destination]);
  392.             $location $this->persistenceHandler->locationHandler()->load($urlAlias->destination);
  393.             $pathIds $this->locationPathConverter->convertToPathIds($location->pathString);
  394.             foreach ($pathIds as $pathId) {
  395.                 $tags[] = $this->cacheIdentifierGenerator->generateTag(self::URL_ALIAS_LOCATION_PATH_IDENTIFIER, [$pathId]);
  396.             }
  397.         }
  398.         return array_unique($tags);
  399.     }
  400.     /**
  401.      * Delete corrupted URL aliases (global, custom and system).
  402.      *
  403.      * @return int Number of deleted URL aliases
  404.      */
  405.     public function deleteCorruptedUrlAliases()
  406.     {
  407.         $this->logger->logCall(__METHOD__);
  408.         $deletedCount $this->persistenceHandler->urlAliasHandler()->deleteCorruptedUrlAliases();
  409.         if ($deletedCount) {
  410.             $this->cache->clear();//!TIMBER!: Deletes all cache
  411.         }
  412.         return $deletedCount;
  413.     }
  414.     /**
  415.      * Attempt repairing auto-generated URL aliases for the given Location (including history).
  416.      *
  417.      * Note: it is assumed that at this point original, working, URL Alias for Location is published.
  418.      *
  419.      * @param int $locationId
  420.      *
  421.      * @throws \Ibexa\Core\Base\Exceptions\BadStateException
  422.      * @throws \Psr\Cache\InvalidArgumentException
  423.      */
  424.     public function repairBrokenUrlAliasesForLocation(int $locationId)
  425.     {
  426.         $this->logger->logCall(__METHOD__, ['locationId' => $locationId]);
  427.         $this->persistenceHandler->urlAliasHandler()->repairBrokenUrlAliasesForLocation($locationId);
  428.         $this->cache->invalidateTags(
  429.             [
  430.                 $this->cacheIdentifierGenerator->generateTag(self::URL_ALIAS_LOCATION_IDENTIFIER, [$locationId]),
  431.                 $this->cacheIdentifierGenerator->generateTag(self::URL_ALIAS_LOCATION_PATH_IDENTIFIER, [$locationId]),
  432.             ]
  433.         );
  434.     }
  435. }
  436. class_alias(UrlAliasHandler::class, 'eZ\Publish\Core\Persistence\Cache\UrlAliasHandler');