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

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($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.                 // 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/*, $uuid = null*/)
  253.     {
  254.         $uuid null;
  255.         if (\func_num_args() >= 5) {
  256.             $uuid \func_get_arg(4);
  257.         }
  258.         $routes $this->getWebspaceRouteNode($webspaceKey$languageCode$segmentKey);
  259.         if ($this->isUnique($routes$path$uuid)) {
  260.             // path is already unique
  261.             return $path;
  262.         } else {
  263.             // append -
  264.             $path .= '-';
  265.             // init counter
  266.             $i 1;
  267.             // while $path-$i is not unique raise counter
  268.             while (!$this->isUnique($routes$path $i$uuid)) {
  269.                 ++$i;
  270.             }
  271.             // result is unique
  272.             return $path $i;
  273.         }
  274.     }
  275.     public function deleteById($id$languageCode$segmentKey null)
  276.     {
  277.         $routeDocument $this->documentManager->find($id$languageCode);
  278.         $this->documentManager->remove($routeDocument);
  279.     }
  280.     public function getParentPath($uuid$webspaceKey$languageCode$segmentKey null)
  281.     {
  282.         $session $this->sessionManager->getSession();
  283.         $contentNode $session->getNodeByIdentifier($uuid);
  284.         $parentNode $contentNode->getParent();
  285.         try {
  286.             return $this->loadByContent($parentNode$webspaceKey$languageCode$segmentKey);
  287.         } catch (ResourceLocatorNotFoundException $ex) {
  288.             // parent node donĀ“t have a resource locator
  289.             return;
  290.         }
  291.     }
  292.     /**
  293.      * Check if path is unique from given $root node.
  294.      *
  295.      * @param NodeInterface $root route node
  296.      * @param string $path requested path
  297.      *
  298.      * @return bool path is unique
  299.      */
  300.     private function isUnique(NodeInterface $root$path$uuid null)
  301.     {
  302.         $path \ltrim($path'/');
  303.         if (!$root->hasNode($path)) {
  304.             return true;
  305.         }
  306.         if (!$uuid) {
  307.             return false;
  308.         }
  309.         $route $root->getNode($path);
  310.         return $route->hasProperty('sulu:content')
  311.             && $route->getPropertyValue('sulu:content')->getIdentifier() === $uuid;
  312.     }
  313.     /**
  314.      * Returns base node of routes from phpcr.
  315.      *
  316.      * @param string $webspaceKey current session
  317.      * @param string $languageCode
  318.      * @param string $segmentKey
  319.      *
  320.      * @return \PHPCR\NodeInterface base node of routes
  321.      */
  322.     private function getWebspaceRouteNode($webspaceKey$languageCode$segmentKey)
  323.     {
  324.         return $this->sessionManager->getRouteNode($webspaceKey$languageCode$segmentKey);
  325.     }
  326.     /**
  327.      * Returns base path of routes from phpcr.
  328.      *
  329.      * @param string $webspaceKey current session
  330.      * @param string $languageCode
  331.      * @param string $segmentKey
  332.      *
  333.      * @return string
  334.      */
  335.     private function getWebspaceRouteNodeBasePath($webspaceKey$languageCode$segmentKey)
  336.     {
  337.         return $this->sessionManager->getRoutePath($webspaceKey$languageCode$segmentKey);
  338.     }
  339.     /**
  340.      * Returns the abspath.
  341.      *
  342.      * @param string $relPath
  343.      * @param string $webspaceKey
  344.      * @param string $languageCode
  345.      * @param string $segmentKey
  346.      *
  347.      * @return string
  348.      */
  349.     private function getPath($relPath$webspaceKey$languageCode$segmentKey)
  350.     {
  351.         $basePath $this->getWebspaceRouteNodeBasePath($webspaceKey$languageCode$segmentKey);
  352.         return '/' \ltrim($basePath'/') . ('' !== $relPath '/' \ltrim($relPath'/') : '');
  353.     }
  354.     /**
  355.      * Returns resource-locator.
  356.      *
  357.      * @param string $path
  358.      * @param string $webspaceKey
  359.      * @param string $languageCode
  360.      * @param string $segmentKey
  361.      *
  362.      * @return string
  363.      */
  364.     private function getResourceLocator($path$webspaceKey$languageCode$segmentKey)
  365.     {
  366.         $basePath $this->getWebspaceRouteNodeBasePath($webspaceKey$languageCode$segmentKey);
  367.         if ($path === $basePath) {
  368.             return '/';
  369.         }
  370.         if (false !== \strpos($path$basePath '/')) {
  371.             $result \str_replace($basePath '/''/'$path);
  372.             if (=== \strpos($result'/')) {
  373.                 return $result;
  374.             }
  375.         }
  376.         return false;
  377.     }
  378. }