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

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