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

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 DateTime;
  12. use PHPCR\ItemExistsException;
  13. use PHPCR\NodeInterface;
  14. use PHPCR\PathNotFoundException;
  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);
  87.         }
  88.     }
  89.     public function loadByContent(NodeInterface $contentNode$webspaceKey$languageCode$segmentKey null)
  90.     {
  91.         $result $this->iterateRouteNodes(
  92.             $contentNode,
  93.             function($resourceLocator, \PHPCR\NodeInterface $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($resourceLocator, \PHPCR\NodeInterface $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.                 // get requested resource locator route node
  218.                 $route $this->sessionManager->getSession()->getNode($path);
  219.             } else {
  220.                 // get home page route node
  221.                 $route $this->getWebspaceRouteNode($webspaceKey$languageCode$segmentKey);
  222.             }
  223.         } catch (PathNotFoundException $e) {
  224.             throw new ResourceLocatorNotFoundException(sprintf('Path "%s" not found'$path), null$e);
  225.         }
  226.         if ($route->hasProperty('sulu:content') && $route->hasProperty('sulu:history')) {
  227.             if (!$route->getPropertyValue('sulu:history')) {
  228.                 /** @var NodeInterface $content */
  229.                 $content $route->getPropertyValue('sulu:content');
  230.                 return $content->getIdentifier();
  231.             } else {
  232.                 // get path from history node
  233.                 /** @var NodeInterface $realPath */
  234.                 $realPath $route->getPropertyValue('sulu:content');
  235.                 throw new ResourceLocatorMovedException(
  236.                     $this->getResourceLocator($realPath->getPath(), $webspaceKey$languageCode$segmentKey),
  237.                     $realPath->getIdentifier()
  238.                 );
  239.             }
  240.         } else {
  241.             throw new ResourceLocatorNotFoundException(sprintf(
  242.                 'Route has "%s" does not have either the "sulu:content" or "sulu:history" properties',
  243.                 $route->getPath()
  244.             ));
  245.         }
  246.     }
  247.     public function unique($path$webspaceKey$languageCode$segmentKey null)
  248.     {
  249.         $routes $this->getWebspaceRouteNode($webspaceKey$languageCode$segmentKey);
  250.         return $this->isUnique($routes$path);
  251.     }
  252.     public function getUniquePath($path$webspaceKey$languageCode$segmentKey null)
  253.     {
  254.         $routes $this->getWebspaceRouteNode($webspaceKey$languageCode$segmentKey);
  255.         if ($this->isUnique($routes$path)) {
  256.             // path is already unique
  257.             return $path;
  258.         } else {
  259.             // append -
  260.             $path .= '-';
  261.             // init counter
  262.             $i 1;
  263.             // while $path-$i is not unique raise counter
  264.             while (!$this->isUnique($routes$path $i)) {
  265.                 ++$i;
  266.             }
  267.             // result is unique
  268.             return $path $i;
  269.         }
  270.     }
  271.     public function deleteById($id$languageCode$segmentKey null)
  272.     {
  273.         $routeDocument $this->documentManager->find($id$languageCode);
  274.         $this->documentManager->remove($routeDocument);
  275.     }
  276.     public function getParentPath($uuid$webspaceKey$languageCode$segmentKey null)
  277.     {
  278.         $session $this->sessionManager->getSession();
  279.         $contentNode $session->getNodeByIdentifier($uuid);
  280.         $parentNode $contentNode->getParent();
  281.         try {
  282.             return $this->loadByContent($parentNode$webspaceKey$languageCode$segmentKey);
  283.         } catch (ResourceLocatorNotFoundException $ex) {
  284.             // parent node donĀ“t have a resource locator
  285.             return;
  286.         }
  287.     }
  288.     /**
  289.      * Check if path is unique from given $root node.
  290.      *
  291.      * @param NodeInterface $root route node
  292.      * @param string $path requested path
  293.      *
  294.      * @return bool path is unique
  295.      */
  296.     private function isUnique(NodeInterface $root$path)
  297.     {
  298.         // check if root has node
  299.         return !$root->hasNode(ltrim($path'/'));
  300.     }
  301.     /**
  302.      * Returns base node of routes from phpcr.
  303.      *
  304.      * @param string $webspaceKey current session
  305.      * @param string $languageCode
  306.      * @param string $segmentKey
  307.      *
  308.      * @return \PHPCR\NodeInterface base node of routes
  309.      */
  310.     private function getWebspaceRouteNode($webspaceKey$languageCode$segmentKey)
  311.     {
  312.         return $this->sessionManager->getRouteNode($webspaceKey$languageCode$segmentKey);
  313.     }
  314.     /**
  315.      * Returns base path of routes from phpcr.
  316.      *
  317.      * @param string $webspaceKey current session
  318.      * @param string $languageCode
  319.      * @param string $segmentKey
  320.      *
  321.      * @return string
  322.      */
  323.     private function getWebspaceRouteNodeBasePath($webspaceKey$languageCode$segmentKey)
  324.     {
  325.         return $this->sessionManager->getRoutePath($webspaceKey$languageCode$segmentKey);
  326.     }
  327.     /**
  328.      * Returns the abspath.
  329.      *
  330.      * @param string $relPath
  331.      * @param string $webspaceKey
  332.      * @param string $languageCode
  333.      * @param string $segmentKey
  334.      *
  335.      * @return string
  336.      */
  337.     private function getPath($relPath$webspaceKey$languageCode$segmentKey)
  338.     {
  339.         $basePath $this->getWebspaceRouteNodeBasePath($webspaceKey$languageCode$segmentKey);
  340.         return '/' ltrim($basePath'/') . ('' !== $relPath '/' ltrim($relPath'/') : '');
  341.     }
  342.     /**
  343.      * Returns resource-locator.
  344.      *
  345.      * @param string $path
  346.      * @param string $webspaceKey
  347.      * @param string $languageCode
  348.      * @param string $segmentKey
  349.      *
  350.      * @return string
  351.      */
  352.     private function getResourceLocator($path$webspaceKey$languageCode$segmentKey)
  353.     {
  354.         $basePath $this->getWebspaceRouteNodeBasePath($webspaceKey$languageCode$segmentKey);
  355.         if ($path === $basePath) {
  356.             return '/';
  357.         }
  358.         if (false !== strpos($path$basePath '/')) {
  359.             $result str_replace($basePath '/''/'$path);
  360.             if (=== strpos($result'/')) {
  361.                 return $result;
  362.             }
  363.         }
  364.         return false;
  365.     }
  366. }