vendor/sulu/sulu/src/Sulu/Component/Content/Types/ResourceLocator/Mapper/PhpcrMapper.php line 265

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of Sulu.
  4.  *
  5.  * (c) Sulu GmbH
  6.  *
  7.  * This source file is subject to the MIT license that is bundled
  8.  * with this source code in the file LICENSE.
  9.  */
  10. namespace Sulu\Component\Content\Types\ResourceLocator\Mapper;
  11. use PHPCR\ItemExistsException;
  12. use PHPCR\NodeInterface;
  13. use PHPCR\PathNotFoundException;
  14. use PHPCR\PropertyInterface;
  15. use PHPCR\Util\PathHelper;
  16. use Sulu\Bundle\DocumentManagerBundle\Bridge\DocumentInspector;
  17. use Sulu\Component\Content\Document\Behavior\ResourceSegmentBehavior;
  18. use Sulu\Component\Content\Exception\ResourceLocatorAlreadyExistsException;
  19. use Sulu\Component\Content\Exception\ResourceLocatorMovedException;
  20. use Sulu\Component\Content\Exception\ResourceLocatorNotFoundException;
  21. use Sulu\Component\Content\Types\ResourceLocator\ResourceLocatorInformation;
  22. use Sulu\Component\DocumentManager\DocumentManagerInterface;
  23. use Sulu\Component\PHPCR\SessionManager\SessionManagerInterface;
  24. /**
  25.  * Manages resource-locators in phpcr.
  26.  */
  27. class PhpcrMapper implements ResourceLocatorMapperInterface
  28. {
  29.     /**
  30.      * @var SessionManagerInterface
  31.      */
  32.     private $sessionManager;
  33.     /**
  34.      * @var DocumentManagerInterface
  35.      */
  36.     private $documentManager;
  37.     /**
  38.      * @var DocumentInspector
  39.      */
  40.     private $documentInspector;
  41.     public function __construct(
  42.         SessionManagerInterface $sessionManager,
  43.         DocumentManagerInterface $documentManager,
  44.         DocumentInspector $documentInspector
  45.     ) {
  46.         $this->sessionManager $sessionManager;
  47.         $this->documentManager $documentManager;
  48.         $this->documentInspector $documentInspector;
  49.     }
  50.     public function save(ResourceSegmentBehavior $document)
  51.     {
  52.         $path $document->getResourceSegment();
  53.         $webspaceKey $this->documentInspector->getWebspace($document);
  54.         $locale $this->documentInspector->getOriginalLocale($document);
  55.         $segmentKey null;
  56.         $webspaceRouteRootPath $this->getWebspaceRouteNodeBasePath($webspaceKey$locale$segmentKey);
  57.         try {
  58.             $routeNodePath $this->loadByContent(
  59.                 $this->documentInspector->getNode($document),
  60.                 $webspaceKey,
  61.                 $locale,
  62.                 $segmentKey
  63.             );
  64.             $routeDocument $this->documentManager->find(
  65.                 $webspaceRouteRootPath $routeNodePath,
  66.                 $locale,
  67.                 ['rehydrate' => false]
  68.             );
  69.             $routeDocumentPath $webspaceRouteRootPath $routeNodePath;
  70.         } catch (ResourceLocatorNotFoundException $e) {
  71.             $routeDocument $this->documentManager->create('route');
  72.             $routeDocumentPath $webspaceRouteRootPath $path;
  73.         }
  74.         $routeDocument->setTargetDocument($document);
  75.         try {
  76.             $this->documentManager->persist(
  77.                 $routeDocument,
  78.                 $locale,
  79.                 [
  80.                     'path' => $routeDocumentPath,
  81.                     'auto_create' => true,
  82.                     'override' => true,
  83.                 ]
  84.             );
  85.             $this->documentManager->publish($routeDocument$locale);
  86.         } catch (ItemExistsException $e) {
  87.             throw new ResourceLocatorAlreadyExistsException($document->getResourceSegment(), $routeDocumentPath$e);
  88.         }
  89.     }
  90.     public function loadByContent(NodeInterface $contentNode$webspaceKey$languageCode$segmentKey null)
  91.     {
  92.         $result $this->iterateRouteNodes(
  93.             $contentNode,
  94.             function($resourceLocatorNodeInterface $node) {
  95.                 if (false === $node->getPropertyValue('sulu:history') && false !== $resourceLocator) {
  96.                     return $resourceLocator;
  97.                 }
  98.                 return false;
  99.             },
  100.             $webspaceKey,
  101.             $languageCode,
  102.             $segmentKey
  103.         );
  104.         if (null !== $result) {
  105.             return $result;
  106.         }
  107.         throw new ResourceLocatorNotFoundException();
  108.     }
  109.     /**
  110.      * Iterates over all route nodes assigned by the given node, and executes the callback on it.
  111.      *
  112.      * @param callable $callback will be called foreach route node (stops and return value if not false)
  113.      * @param string $webspaceKey
  114.      * @param string $languageCode
  115.      * @param string $segmentKey
  116.      *
  117.      * @return NodeInterface
  118.      */
  119.     private function iterateRouteNodes(
  120.         NodeInterface $node,
  121.         $callback,
  122.         $webspaceKey,
  123.         $languageCode,
  124.         $segmentKey null
  125.     ) {
  126.         if ($node->isNew()) {
  127.             return null;
  128.         }
  129.         $routePath $this->sessionManager->getRoutePath($webspaceKey$languageCode);
  130.         // search for references with name 'content'
  131.         foreach ($node->getReferences('sulu:content') as $ref) {
  132.             if ($ref instanceof PropertyInterface) {
  133.                 $routeNode $ref->getParent();
  134.                 if (!== \strpos($routeNode->getPath(), $routePath)) {
  135.                     continue;
  136.                 }
  137.                 $resourceLocator $this->getResourceLocator(
  138.                     $ref->getParent()->getPath(),
  139.                     $webspaceKey,
  140.                     $languageCode,
  141.                     $segmentKey
  142.                 );
  143.                 $result $callback($resourceLocator$routeNode);
  144.                 if (false !== $result) {
  145.                     return $result;
  146.                 }
  147.             }
  148.         }
  149.         return null;
  150.     }
  151.     public function loadByContentUuid($uuid$webspaceKey$languageCode$segmentKey null)
  152.     {
  153.         $session $this->sessionManager->getSession();
  154.         $contentNode $session->getNodeByIdentifier($uuid);
  155.         return $this->loadByContent($contentNode$webspaceKey$languageCode$segmentKey);
  156.     }
  157.     public function loadHistoryByContentUuid($uuid$webspaceKey$languageCode$segmentKey null)
  158.     {
  159.         // get content node
  160.         $session $this->sessionManager->getSession();
  161.         $contentNode $session->getNodeByIdentifier($uuid);
  162.         // get current path node
  163.         $pathNode $this->iterateRouteNodes(
  164.             $contentNode,
  165.             function($resourceLocatorNodeInterface $node) {
  166.                 if (false === $node->getPropertyValue('sulu:history') && false !== $resourceLocator) {
  167.                     return $node;
  168.                 } else {
  169.                     return false;
  170.                 }
  171.             },
  172.             $webspaceKey,
  173.             $languageCode,
  174.             $segmentKey
  175.         );
  176.         // iterate over history of path node
  177.         $result = [];
  178.         if (!$pathNode) {
  179.             return $result;
  180.         }
  181.         $this->iterateRouteNodes(
  182.             $pathNode,
  183.             function($resourceLocatorNodeInterface $node) use (&$result) {
  184.                 if (false !== $resourceLocator) {
  185.                     // add resourceLocator
  186.                     $result[] = new ResourceLocatorInformation(
  187.                         //backward compability
  188.                         $resourceLocator,
  189.                         $node->getPropertyValueWithDefault('sulu:created', new \DateTime()),
  190.                         $node->getIdentifier()
  191.                     );
  192.                 }
  193.                 return false;
  194.             },
  195.             $webspaceKey,
  196.             $languageCode,
  197.             $segmentKey
  198.         );
  199.         // sort history descending
  200.         \usort(
  201.             $result,
  202.             function(ResourceLocatorInformation $item1ResourceLocatorInformation $item2) {
  203.                 return $item1->getCreated() < $item2->getCreated();
  204.             }
  205.         );
  206.         return $result;
  207.     }
  208.     public function loadByResourceLocator($resourceLocator$webspaceKey$languageCode$segmentKey null)
  209.     {
  210.         $resourceLocator \ltrim($resourceLocator'/');
  211.         $path \sprintf(
  212.             '%s/%s',
  213.             $this->getWebspaceRouteNodeBasePath($webspaceKey$languageCode$segmentKey),
  214.             $resourceLocator
  215.         );
  216.         try {
  217.             if ('' !== $resourceLocator) {
  218.                 if (!PathHelper::assertValidAbsolutePath($pathfalsefalse)) {
  219.                     throw new ResourceLocatorNotFoundException(\sprintf('Path "%s" not found'$path));
  220.                 }
  221.                 // get requested resource locator route node
  222.                 $route $this->sessionManager->getSession()->getNode($path);
  223.             } else {
  224.                 // get home page route node
  225.                 $route $this->getWebspaceRouteNode($webspaceKey$languageCode$segmentKey);
  226.             }
  227.         } catch (PathNotFoundException $e) {
  228.             throw new ResourceLocatorNotFoundException(\sprintf('Path "%s" not found'$path), null$e);
  229.         }
  230.         if ($route->hasProperty('sulu:content') && $route->hasProperty('sulu:history')) {
  231.             if (!$route->getPropertyValue('sulu:history')) {
  232.                 /** @var NodeInterface $content */
  233.                 $content $route->getPropertyValue('sulu:content');
  234.                 return $content->getIdentifier();
  235.             } else {
  236.                 // get path from history node
  237.                 /** @var NodeInterface $realPath */
  238.                 $realPath $route->getPropertyValue('sulu:content');
  239.                 throw new ResourceLocatorMovedException(
  240.                     $this->getResourceLocator($realPath->getPath(), $webspaceKey$languageCode$segmentKey),
  241.                     $realPath->getIdentifier()
  242.                 );
  243.             }
  244.         } else {
  245.             throw new ResourceLocatorNotFoundException(\sprintf(
  246.                 'Route has "%s" does not have either the "sulu:content" or "sulu:history" properties',
  247.                 $route->getPath()
  248.             ));
  249.         }
  250.     }
  251.     public function unique($path$webspaceKey$languageCode$segmentKey null)
  252.     {
  253.         $routes $this->getWebspaceRouteNode($webspaceKey$languageCode$segmentKey);
  254.         return $this->isUnique($routes$path);
  255.     }
  256.     public function getUniquePath($path$webspaceKey$languageCode$segmentKey null/*, $uuid = null*/)
  257.     {
  258.         $uuid null;
  259.         if (\func_num_args() >= 5) {
  260.             $uuid \func_get_arg(4);
  261.         }
  262.         $routes $this->getWebspaceRouteNode($webspaceKey$languageCode$segmentKey);
  263.         if ($this->isUnique($routes$path$uuid)) {
  264.             // path is already unique
  265.             return $path;
  266.         } else {
  267.             // append -
  268.             $path .= '-';
  269.             // init counter
  270.             $i 1;
  271.             // while $path-$i is not unique raise counter
  272.             while (!$this->isUnique($routes$path $i$uuid)) {
  273.                 ++$i;
  274.             }
  275.             // result is unique
  276.             return $path $i;
  277.         }
  278.     }
  279.     public function deleteById($id$languageCode$segmentKey null)
  280.     {
  281.         $routeDocument $this->documentManager->find($id$languageCode);
  282.         $this->documentManager->remove($routeDocument);
  283.     }
  284.     public function getParentPath($uuid$webspaceKey$languageCode$segmentKey null)
  285.     {
  286.         $session $this->sessionManager->getSession();
  287.         $contentNode $session->getNodeByIdentifier($uuid);
  288.         $parentNode $contentNode->getParent();
  289.         try {
  290.             return $this->loadByContent($parentNode$webspaceKey$languageCode$segmentKey);
  291.         } catch (ResourceLocatorNotFoundException $ex) {
  292.             // parent node donĀ“t have a resource locator
  293.             return;
  294.         }
  295.     }
  296.     /**
  297.      * Check if path is unique from given $root node.
  298.      *
  299.      * @param NodeInterface $root route node
  300.      * @param string $path requested path
  301.      *
  302.      * @return bool path is unique
  303.      */
  304.     private function isUnique(NodeInterface $root$path$uuid null)
  305.     {
  306.         $path \ltrim($path'/');
  307.         if (!$root->hasNode($path)) {
  308.             return true;
  309.         }
  310.         if (!$uuid) {
  311.             return false;
  312.         }
  313.         $route $root->getNode($path);
  314.         return $route->hasProperty('sulu:content')
  315.             && $route->getPropertyValue('sulu:content')->getIdentifier() === $uuid;
  316.     }
  317.     /**
  318.      * Returns base node of routes from phpcr.
  319.      *
  320.      * @param string $webspaceKey current session
  321.      * @param string $languageCode
  322.      * @param string $segmentKey
  323.      *
  324.      * @return NodeInterface base node of routes
  325.      */
  326.     private function getWebspaceRouteNode($webspaceKey$languageCode$segmentKey)
  327.     {
  328.         return $this->sessionManager->getRouteNode($webspaceKey$languageCode$segmentKey);
  329.     }
  330.     /**
  331.      * Returns base path of routes from phpcr.
  332.      *
  333.      * @param string $webspaceKey current session
  334.      * @param string $languageCode
  335.      * @param string $segmentKey
  336.      *
  337.      * @return string
  338.      */
  339.     private function getWebspaceRouteNodeBasePath($webspaceKey$languageCode$segmentKey)
  340.     {
  341.         return $this->sessionManager->getRoutePath($webspaceKey$languageCode$segmentKey);
  342.     }
  343.     /**
  344.      * Returns the abspath.
  345.      *
  346.      * @param string $relPath
  347.      * @param string $webspaceKey
  348.      * @param string $languageCode
  349.      * @param string $segmentKey
  350.      *
  351.      * @return string
  352.      */
  353.     private function getPath($relPath$webspaceKey$languageCode$segmentKey)
  354.     {
  355.         $basePath $this->getWebspaceRouteNodeBasePath($webspaceKey$languageCode$segmentKey);
  356.         return '/' \ltrim($basePath'/') . ('' !== $relPath '/' \ltrim($relPath'/') : '');
  357.     }
  358.     /**
  359.      * Returns resource-locator.
  360.      *
  361.      * @param string $path
  362.      * @param string $webspaceKey
  363.      * @param string $languageCode
  364.      * @param string $segmentKey
  365.      *
  366.      * @return string
  367.      */
  368.     private function getResourceLocator($path$webspaceKey$languageCode$segmentKey)
  369.     {
  370.         $basePath $this->getWebspaceRouteNodeBasePath($webspaceKey$languageCode$segmentKey);
  371.         if ($path === $basePath) {
  372.             return '/';
  373.         }
  374.         if (false !== \strpos($path$basePath '/')) {
  375.             $result \str_replace($basePath '/''/'$path);
  376.             if (=== \strpos($result'/')) {
  377.                 return $result;
  378.             }
  379.         }
  380.         return false;
  381.     }
  382. }