vendor/kunstmaan/node-bundle/Helper/NodeMenu.php line 16

Open in your IDE?
  1. <?php
  2. namespace Kunstmaan\NodeBundle\Helper;
  3. use Doctrine\ORM\EntityManagerInterface;
  4. use Kunstmaan\AdminBundle\Entity\BaseUser;
  5. use Kunstmaan\AdminBundle\Helper\DomainConfigurationInterface;
  6. use Kunstmaan\AdminBundle\Helper\Security\Acl\AclHelper;
  7. use Kunstmaan\AdminBundle\Helper\Security\Acl\Permission\PermissionMap;
  8. use Kunstmaan\NodeBundle\Entity\HasNodeInterface;
  9. use Kunstmaan\NodeBundle\Entity\Node;
  10. use Kunstmaan\NodeBundle\Entity\NodeTranslation;
  11. use Kunstmaan\NodeBundle\Repository\NodeRepository;
  12. use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
  13. class NodeMenu
  14. {
  15.     /**
  16.      * @var EntityManagerInterface
  17.      */
  18.     private $em;
  19.     /**
  20.      * @var TokenStorageInterface
  21.      */
  22.     private $tokenStorage;
  23.     /**
  24.      * @var AclHelper
  25.      */
  26.     private $aclHelper;
  27.     /**
  28.      * @var string
  29.      */
  30.     private $locale;
  31.     /**
  32.      * @var Node
  33.      */
  34.     private $currentNode;
  35.     /**
  36.      * @var string
  37.      */
  38.     private $permission PermissionMap::PERMISSION_VIEW;
  39.     /**
  40.      * @var bool
  41.      */
  42.     private $includeOffline false;
  43.     /**
  44.      * @var bool
  45.      */
  46.     private $includeHiddenFromNav false;
  47.     /**
  48.      * @var NodeMenuItem[]
  49.      */
  50.     private $topNodeMenuItems;
  51.     /**
  52.      * @var NodeMenuItem[]
  53.      */
  54.     private $breadCrumb;
  55.     /**
  56.      * @var Node[]
  57.      */
  58.     private $allNodes = [];
  59.     /**
  60.      * @var Node[]
  61.      */
  62.     private $childNodes = [];
  63.     /**
  64.      * @var Node[]
  65.      */
  66.     private $nodesByInternalName = [];
  67.     /**
  68.      * @var bool
  69.      */
  70.     private $initialized false;
  71.     /**
  72.      * @var NodeMenuItem
  73.      */
  74.     private $rootNodeMenuItem;
  75.     /**
  76.      * @var DomainConfigurationInterface
  77.      */
  78.     private $domainConfiguration;
  79.     /**
  80.      * @param EntityManagerInterface       $em                  The entity manager
  81.      * @param TokenStorageInterface        $tokenStorage        The security token storage
  82.      * @param AclHelper                    $aclHelper           The ACL helper pages
  83.      * @param DomainConfigurationInterface $domainConfiguration The current domain configuration
  84.      */
  85.     public function __construct(
  86.         EntityManagerInterface $em,
  87.         TokenStorageInterface $tokenStorage,
  88.         AclHelper $aclHelper,
  89.         DomainConfigurationInterface $domainConfiguration,
  90.     ) {
  91.         $this->em $em;
  92.         $this->tokenStorage $tokenStorage;
  93.         $this->aclHelper $aclHelper;
  94.         $this->domainConfiguration $domainConfiguration;
  95.     }
  96.     /**
  97.      * @param string $locale
  98.      */
  99.     public function setLocale($locale)
  100.     {
  101.         $this->locale $locale;
  102.     }
  103.     public function setCurrentNode(?Node $currentNode null)
  104.     {
  105.         $this->currentNode $currentNode;
  106.     }
  107.     /**
  108.      * @param string $permission
  109.      */
  110.     public function setPermission($permission)
  111.     {
  112.         if ($this->permission !== $permission) {
  113.             // For now reset initialized flag when cached data has to be reset ...
  114.             $this->initialized false;
  115.         }
  116.         $this->permission $permission;
  117.     }
  118.     /**
  119.      * @param bool $includeOffline
  120.      */
  121.     public function setIncludeOffline($includeOffline)
  122.     {
  123.         $this->includeOffline $includeOffline;
  124.     }
  125.     /**
  126.      * @param bool $includeHiddenFromNav
  127.      */
  128.     public function setIncludeHiddenFromNav($includeHiddenFromNav)
  129.     {
  130.         if ($this->includeHiddenFromNav !== $includeHiddenFromNav) {
  131.             // For now reset initialized flag when cached data has to be reset ...
  132.             $this->initialized false;
  133.         }
  134.         $this->includeHiddenFromNav $includeHiddenFromNav;
  135.     }
  136.     /**
  137.      * This method initializes the nodemenu only once, the method may be
  138.      * executed multiple times
  139.      */
  140.     private function init()
  141.     {
  142.         if ($this->initialized) {
  143.             return;
  144.         }
  145.         $this->allNodes = [];
  146.         $this->breadCrumb null;
  147.         $this->childNodes = [];
  148.         $this->topNodeMenuItems null;
  149.         $this->nodesByInternalName = [];
  150.         /* @var NodeRepository $repo */
  151.         $repo $this->em->getRepository(Node::class);
  152.         // Get all possible menu items in one query (also fetch offline nodes)
  153.         $nodes $repo->getChildNodes(
  154.             false,
  155.             $this->locale,
  156.             $this->permission,
  157.             $this->aclHelper,
  158.             $this->includeHiddenFromNav,
  159.             true,
  160.             $this->domainConfiguration->getRootNode()
  161.         );
  162.         foreach ($nodes as $node) {
  163.             $this->allNodes[$node->getId()] = $node;
  164.             if ($node->getParent()) {
  165.                 $this->childNodes[$node->getParent()->getId()][] = $node;
  166.             } else {
  167.                 $this->childNodes[0][] = $node;
  168.             }
  169.             $internalName $node->getInternalName();
  170.             if ($internalName) {
  171.                 $this->nodesByInternalName[$internalName][] = $node;
  172.             }
  173.         }
  174.         $this->initialized true;
  175.     }
  176.     /**
  177.      * @return NodeMenuItem[]
  178.      */
  179.     public function getTopNodes()
  180.     {
  181.         $this->init();
  182.         if (!\is_array($this->topNodeMenuItems)) {
  183.             $this->topNodeMenuItems = [];
  184.             // To be backwards compatible we need to create the top node MenuItems
  185.             if (\array_key_exists(0$this->childNodes)) {
  186.                 $topNodeMenuItems $this->getTopNodeMenuItems();
  187.                 $includeHiddenFromNav $this->includeHiddenFromNav;
  188.                 $this->topNodeMenuItems array_filter(
  189.                     $topNodeMenuItems,
  190.                     function (NodeMenuItem $entry) use ($includeHiddenFromNav) {
  191.                         if ($entry->getNode()->isHiddenFromNav() && !$includeHiddenFromNav) {
  192.                             return false;
  193.                         }
  194.                         return true;
  195.                     }
  196.                 );
  197.             }
  198.         }
  199.         return $this->topNodeMenuItems;
  200.     }
  201.     /**
  202.      * @return NodeMenuItem[]
  203.      */
  204.     public function getBreadCrumb()
  205.     {
  206.         if (\is_array($this->breadCrumb)) {
  207.             return $this->breadCrumb;
  208.         }
  209.         $this->breadCrumb = [];
  210.         /* @var NodeRepository $repo */
  211.         $repo $this->em->getRepository(Node::class);
  212.         // Generate breadcrumb MenuItems - fetch *all* languages so you can link translations if needed
  213.         $parentNodes $repo->getAllParents($this->currentNode);
  214.         $parentNodeMenuItem null;
  215.         /* @var Node $parentNode */
  216.         foreach ($parentNodes as $parentNode) {
  217.             $nodeTranslation $parentNode->getNodeTranslation(
  218.                 $this->locale,
  219.                 $this->includeOffline
  220.             );
  221.             if (!\is_null($nodeTranslation)) {
  222.                 $nodeMenuItem = new NodeMenuItem(
  223.                     $parentNode,
  224.                     $nodeTranslation,
  225.                     $parentNodeMenuItem,
  226.                     $this
  227.                 );
  228.                 $this->breadCrumb[] = $nodeMenuItem;
  229.                 $parentNodeMenuItem $nodeMenuItem;
  230.             }
  231.         }
  232.         return $this->breadCrumb;
  233.     }
  234.     /**
  235.      * @return NodeMenuItem|null
  236.      */
  237.     public function getCurrent()
  238.     {
  239.         $breadCrumb $this->getBreadCrumb();
  240.         if (\count($breadCrumb) > 0) {
  241.             return $breadCrumb[\count($breadCrumb) - 1];
  242.         }
  243.         return null;
  244.     }
  245.     /**
  246.      * @param int $depth
  247.      *
  248.      * @return NodeMenuItem|null
  249.      */
  250.     public function getActiveForDepth($depth)
  251.     {
  252.         $breadCrumb $this->getBreadCrumb();
  253.         if (\count($breadCrumb) >= $depth) {
  254.             return $breadCrumb[$depth 1];
  255.         }
  256.         return null;
  257.     }
  258.     /**
  259.      * @param bool $includeHiddenFromNav
  260.      *
  261.      * @return NodeMenuItem[]
  262.      */
  263.     public function getChildren(Node $node$includeHiddenFromNav true)
  264.     {
  265.         $this->init();
  266.         $children = [];
  267.         if (\array_key_exists($node->getId(), $this->childNodes)) {
  268.             $nodes $this->childNodes[$node->getId()];
  269.             /* @var Node $childNode */
  270.             foreach ($nodes as $childNode) {
  271.                 $nodeTranslation $childNode->getNodeTranslation(
  272.                     $this->locale,
  273.                     $this->includeOffline
  274.                 );
  275.                 if (!\is_null($nodeTranslation)) {
  276.                     $children[] = new NodeMenuItem(
  277.                         $childNode,
  278.                         $nodeTranslation,
  279.                         false,
  280.                         $this
  281.                     );
  282.                 }
  283.             }
  284.             $children array_filter(
  285.                 $children,
  286.                 function (NodeMenuItem $entry) use ($includeHiddenFromNav) {
  287.                     if ($entry->getNode()->isHiddenFromNav() && !$includeHiddenFromNav) {
  288.                         return false;
  289.                     }
  290.                     return true;
  291.                 }
  292.             );
  293.         }
  294.         return $children;
  295.     }
  296.     /**
  297.      * @param bool $includeHiddenFromNav
  298.      *
  299.      * @return array|\Kunstmaan\NodeBundle\Helper\NodeMenuItem[]
  300.      */
  301.     public function getSiblings(Node $node$includeHiddenFromNav true)
  302.     {
  303.         $this->init();
  304.         $siblings = [];
  305.         if (false !== $parent $this->getParent($node)) {
  306.             $siblings $this->getChildren($parent$includeHiddenFromNav);
  307.             foreach ($siblings as $index => $child) {
  308.                 if ($child === $node) {
  309.                     unset($siblings[$index]);
  310.                 }
  311.             }
  312.         }
  313.         return $siblings;
  314.     }
  315.     /**
  316.      * @param bool $includeHiddenFromNav
  317.      *
  318.      * @return bool|\Kunstmaan\NodeBundle\Helper\NodeMenuItem
  319.      */
  320.     public function getPreviousSibling(Node $node$includeHiddenFromNav true)
  321.     {
  322.         $this->init();
  323.         if (false !== $parent $this->getParent($node)) {
  324.             $siblings $this->getChildren($parent$includeHiddenFromNav);
  325.             foreach ($siblings as $index => $child) {
  326.                 if ($child->getNode() === $node && ($index >= 0)) {
  327.                     return $siblings[$index 1];
  328.                 }
  329.             }
  330.         }
  331.         return false;
  332.     }
  333.     /**
  334.      * @param bool $includeHiddenFromNav
  335.      *
  336.      * @return bool|\Kunstmaan\NodeBundle\Helper\NodeMenuItem
  337.      */
  338.     public function getNextSibling(Node $node$includeHiddenFromNav true)
  339.     {
  340.         $this->init();
  341.         if (false !== $parent $this->getParent($node)) {
  342.             $siblings $this->getChildren($parent$includeHiddenFromNav);
  343.             foreach ($siblings as $index => $child) {
  344.                 if ($child->getNode() === $node && (($index 1) < \count(
  345.                     $siblings
  346.                 ))
  347.                 ) {
  348.                     return $siblings[$index 1];
  349.                 }
  350.             }
  351.         }
  352.         return false;
  353.     }
  354.     /**
  355.      * @return NodeMenuItem
  356.      */
  357.     public function getParent(Node $node)
  358.     {
  359.         $this->init();
  360.         if ($node->getParent() && \array_key_exists(
  361.             $node->getParent()->getId(),
  362.             $this->allNodes
  363.         )
  364.         ) {
  365.             return $this->allNodes[$node->getParent()->getId()];
  366.         }
  367.         return false;
  368.     }
  369.     /**
  370.      * @param NodeTranslation $parentNode The parent node
  371.      * @param string          $slug       The slug
  372.      *
  373.      * @return NodeTranslation
  374.      */
  375.     public function getNodeBySlug(NodeTranslation $parentNode$slug)
  376.     {
  377.         return $this->em->getRepository(NodeTranslation::class)
  378.             ->getNodeTranslationForSlug($slug$parentNode);
  379.     }
  380.     /**
  381.      * @param string                                        $internalName   The
  382.      *                                                                      internal
  383.      *                                                                      name
  384.      * @param NodeTranslation|NodeMenuItem|HasNodeInterface $parent         The
  385.      *                                                                      parent
  386.      * @param bool                                          $includeOffline
  387.      *
  388.      * @return NodeMenuItem|null
  389.      */
  390.     public function getNodeByInternalName(
  391.         $internalName,
  392.         $parent null,
  393.         $includeOffline null,
  394.     ) {
  395.         $this->init();
  396.         $resultNode null;
  397.         if (\is_null($includeOffline)) {
  398.             $includeOffline $this->includeOffline;
  399.         }
  400.         if (\array_key_exists($internalName$this->nodesByInternalName)) {
  401.             $nodes $this->nodesByInternalName[$internalName];
  402.             $nodes array_filter(
  403.                 $nodes,
  404.                 function (Node $entry) use ($includeOffline) {
  405.                     if ($entry->isDeleted() && !$includeOffline) {
  406.                         return false;
  407.                     }
  408.                     return true;
  409.                 }
  410.             );
  411.             if (!\is_null($parent)) {
  412.                 $parentNode null;
  413.                 /** @var Node $parentNode */
  414.                 if ($parent instanceof NodeTranslation) {
  415.                     $parentNode $parent->getNode();
  416.                 } elseif ($parent instanceof NodeMenuItem) {
  417.                     $parentNode $parent->getNode();
  418.                 } elseif ($parent instanceof HasNodeInterface) {
  419.                     $repo $this->em->getRepository(
  420.                         Node::class
  421.                     );
  422.                     $parentNode $repo->getNodeFor($parent);
  423.                 }
  424.                 // Look for a node with the same parent id
  425.                 /** @var Node $node */
  426.                 foreach ($nodes as $node) {
  427.                     if ($parentNode && $node->getParent()->getId() == $parentNode->getId()) {
  428.                         $resultNode $node;
  429.                         break;
  430.                     }
  431.                 }
  432.                 // Look for a node that has an ancestor with the same parent id
  433.                 if (\is_null($resultNode)) {
  434.                     /* @var Node $n */
  435.                     foreach ($nodes as $node) {
  436.                         $tempNode $node;
  437.                         while (\is_null($resultNode) && !\is_null(
  438.                             $tempNode->getParent()
  439.                         )) {
  440.                             $tempParent $tempNode->getParent();
  441.                             if ($parentNode && $tempParent->getId() == $parentNode->getId()) {
  442.                                 $resultNode $node;
  443.                                 break;
  444.                             }
  445.                             $tempNode $tempParent;
  446.                         }
  447.                     }
  448.                 }
  449.             } elseif (\count($nodes) > 0) {
  450.                 $resultNode $nodes[0];
  451.             }
  452.         }
  453.         if ($resultNode) {
  454.             $nodeTranslation $resultNode->getNodeTranslation(
  455.                 $this->locale,
  456.                 $includeOffline
  457.             );
  458.             if (!\is_null($nodeTranslation)) {
  459.                 return new NodeMenuItem(
  460.                     $resultNode,
  461.                     $nodeTranslation,
  462.                     false,
  463.                     $this
  464.                 );
  465.             }
  466.         }
  467.         return null;
  468.     }
  469.     /**
  470.      * Returns the current root node menu item
  471.      */
  472.     public function getRootNodeMenuItem()
  473.     {
  474.         if (\is_null($this->rootNodeMenuItem)) {
  475.             $rootNode $this->domainConfiguration->getRootNode();
  476.             if (!\is_null($rootNode)) {
  477.                 $nodeTranslation $rootNode->getNodeTranslation(
  478.                     $this->locale,
  479.                     $this->includeOffline
  480.                 );
  481.                 $this->rootNodeMenuItem = new NodeMenuItem(
  482.                     $rootNode,
  483.                     $nodeTranslation,
  484.                     false,
  485.                     $this
  486.                 );
  487.             } else {
  488.                 $this->rootNodeMenuItem $this->breadCrumb[0];
  489.             }
  490.         }
  491.         return $this->rootNodeMenuItem;
  492.     }
  493.     /**
  494.      * @return bool
  495.      */
  496.     public function isIncludeOffline()
  497.     {
  498.         return $this->includeOffline;
  499.     }
  500.     /**
  501.      * @return string
  502.      */
  503.     public function getPermission()
  504.     {
  505.         return $this->permission;
  506.     }
  507.     /**
  508.      * @return BaseUser
  509.      */
  510.     public function getUser()
  511.     {
  512.         return $this->tokenStorage->getToken()->getUser();
  513.     }
  514.     /**
  515.      * @return EntityManagerInterface
  516.      */
  517.     public function getEntityManager()
  518.     {
  519.         return $this->em;
  520.     }
  521.     /**
  522.      * @return TokenStorageInterface
  523.      */
  524.     public function getTokenStorage()
  525.     {
  526.         return $this->tokenStorage;
  527.     }
  528.     /**
  529.      * @return AclHelper
  530.      */
  531.     public function getAclHelper()
  532.     {
  533.         return $this->aclHelper;
  534.     }
  535.     /**
  536.      * @return string
  537.      */
  538.     public function getLocale()
  539.     {
  540.         return $this->locale;
  541.     }
  542.     /**
  543.      * @return bool
  544.      */
  545.     public function isIncludeHiddenFromNav()
  546.     {
  547.         return $this->includeHiddenFromNav;
  548.     }
  549.     /**
  550.      * Check if provided slug is in active path
  551.      *
  552.      * @param string $slug
  553.      *
  554.      * @return bool
  555.      */
  556.     public function getActive($slug)
  557.     {
  558.         $bc $this->getBreadCrumb();
  559.         foreach ($bc as $bcItem) {
  560.             if ($bcItem->getSlug() == $slug) {
  561.                 return true;
  562.             }
  563.         }
  564.         return false;
  565.     }
  566.     /**
  567.      * @return bool
  568.      */
  569.     public function isInitialized()
  570.     {
  571.         return $this->initialized;
  572.     }
  573.     private function getTopNodeMenuItems(): array
  574.     {
  575.         $topNodeMenuItems = [];
  576.         $topNodes $this->childNodes[0];
  577.         /* @var Node $topNode */
  578.         foreach ($topNodes as $topNode) {
  579.             $nodeTranslation $topNode->getNodeTranslation(
  580.                 $this->locale,
  581.                 $this->includeOffline
  582.             );
  583.             if (!\is_null($nodeTranslation)) {
  584.                 $topNodeMenuItems[] = new NodeMenuItem(
  585.                     $topNode,
  586.                     $nodeTranslation,
  587.                     null,
  588.                     $this
  589.                 );
  590.             }
  591.         }
  592.         return $topNodeMenuItems;
  593.     }
  594. }