From 2f48bfb88ea82d139c10f747eace6c2aba96e763 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Sat, 9 Jan 2016 11:45:18 +0100 Subject: [PATCH 1/7] Very early sketch of a faster menu helper --- Templating/MenuHelper.php | 95 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 Templating/MenuHelper.php diff --git a/Templating/MenuHelper.php b/Templating/MenuHelper.php new file mode 100644 index 00000000..cc756115 --- /dev/null +++ b/Templating/MenuHelper.php @@ -0,0 +1,95 @@ + + */ +class MenuHelper extends Helper +{ + /** + * @var DocumentManager + */ + private $documentManager; + private $contentObjectKey; + + /** + * @param DocumentManager $documentManager + * @param string $contentObjectKey The name of the request attribute holding + * the current content object + * @param string $routeNAmeKey The name of the request attribute holding + * the name of the current route + */ + public function __construct(DocumentManager $documentManager, $contentObjectKey = RouteObjectInterface::CONTENT_OBJECT, $routeNameKey = RouteObjectInterface::ROUTE_NAME) + { + $this->documentManager = $documentManager; + $this->contentObjectKey = $contentObjectKey; + $this->routeNameKey = $routeNameKey; + } + + /** + * Generates an array of breadcrumb items by traversing + * up the tree from the current item. + * + * @param ItemInterface $item The current menu item + * + * @return array An array with breadcrumb items (each item has the following keys: label, uri, item) + */ + public function getBreadcrumbArray(ItemInterface $item) + { + $breadcrumbs = array( + array( + 'label' => $item->getLabel(), + 'uri' => $item->getUri(), + 'item' => $item + ), + ); + + if (!$item instanceof MenuNode) { + // We assume the root of the menu is reached + return $breadcrumbs; + } + + return array_merge($breadcrumbs, $this->getBreadcrumbArray($item->getParentObject())); + } + + /** + * Tries to find the current item from the request. + * + * @param Request $request + * + * @return MenuNode|null + */ + public function getCurrentItem(Request $request) + { + $node = null; + if ($request->attributes->has($this->contentObjectKey)) { + $content = $request->attributes->get($this->contentObjectKey); + + $node = $this->documentManager->getRepository('CmfMenuBundle:MenuNode') + ->findOneBy(array('content' => $content)); + } elseif ($request->attributes->has($this->routeNameKey)) { + $route = $request->attributes->get($this->routeNameKey); + + $node = $this->documentManager->getRepository('CmfMenuBundle:MenuNode') + ->findOneBy(array('route' => $rotue)); + } + + return $node; + } + + public function getName() + { + return 'cmf_menu'; + } +} From a1130b8201cba6877a09f6b9b29c14790176c0fe Mon Sep 17 00:00:00 2001 From: WouterJ Date: Sat, 9 Jan 2016 16:00:12 +0100 Subject: [PATCH 2/7] Make code working --- Templating/MenuHelper.php | 65 ++++++++--- .../Functional/Templating/MenuHelperTest.php | 109 ++++++++++++++++++ Twig/MenuExtension.php | 31 +++++ 3 files changed, 190 insertions(+), 15 deletions(-) create mode 100644 Tests/Functional/Templating/MenuHelperTest.php create mode 100644 Twig/MenuExtension.php diff --git a/Templating/MenuHelper.php b/Templating/MenuHelper.php index cc756115..44d17d6a 100644 --- a/Templating/MenuHelper.php +++ b/Templating/MenuHelper.php @@ -3,6 +3,8 @@ namespace Symfony\Cmf\Bundle\MenuBundle\Templating; use Knp\Menu\ItemInterface; +use Knp\Menu\NodeInterface; +use Knp\Menu\FactoryInterface; use Doctrine\ODM\PHPCR\DocumentManager; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Templating\Helper\Helper; @@ -21,46 +23,58 @@ class MenuHelper extends Helper * @var DocumentManager */ private $documentManager; + + /** + * @var FactoryInterface + */ + private $menuFactory; private $contentObjectKey; + private $routeNameKey; /** - * @param DocumentManager $documentManager - * @param string $contentObjectKey The name of the request attribute holding - * the current content object - * @param string $routeNAmeKey The name of the request attribute holding - * the name of the current route + * @param DocumentManager $documentManager + * @param FactoryInterface $menuFactory + * @param string $contentObjectKey The name of the request attribute holding + * the current content object + * @param string $routeNameKey The name of the request attribute holding + * the name of the current route */ - public function __construct(DocumentManager $documentManager, $contentObjectKey = RouteObjectInterface::CONTENT_OBJECT, $routeNameKey = RouteObjectInterface::ROUTE_NAME) + public function __construct(DocumentManager $documentManager, FactoryInterface $menuFactory, $contentObjectKey = RouteObjectInterface::CONTENT_OBJECT, $routeNameKey = RouteObjectInterface::ROUTE_NAME) { $this->documentManager = $documentManager; + $this->menuFactory = $menuFactory; $this->contentObjectKey = $contentObjectKey; $this->routeNameKey = $routeNameKey; } /** * Generates an array of breadcrumb items by traversing - * up the tree from the current item. + * up the tree from the current node. * - * @param ItemInterface $item The current menu item + * @param NodeInterface $node The current menu node (use {@link getCurrentNode} to get it) + * @param bool $includeMenuRoot Whether to include the menu root as breadcrumb item * * @return array An array with breadcrumb items (each item has the following keys: label, uri, item) */ - public function getBreadcrumbArray(ItemInterface $item) + public function getBreadcrumbArray(NodeInterface $node, $includeMenuRoot = true) { + $item = $this->menuFactory->createItem($node->getName(), $node->getOptions()); + $breadcrumbs = array( array( 'label' => $item->getLabel(), 'uri' => $item->getUri(), - 'item' => $item + 'item' => $item, ), ); - if (!$item instanceof MenuNode) { + $parent = $node->getParentObject(); + if (!$parent instanceof MenuNode) { // We assume the root of the menu is reached - return $breadcrumbs; + return $includeMenuRoot ? $breadcrumbs : array(); } - return array_merge($breadcrumbs, $this->getBreadcrumbArray($item->getParentObject())); + return array_merge($this->getBreadcrumbArray($parent, $includeMenuRoot), $breadcrumbs); } /** @@ -71,6 +85,17 @@ public function getBreadcrumbArray(ItemInterface $item) * @return MenuNode|null */ public function getCurrentItem(Request $request) + { + $node = $this->getCurrentNode($request); + + if (!$node instanceof NodeInterface) { + return null; + } + + return $this->menuFactory->createItem($node->getName(), $node->getOptions()); + } + + public function getCurrentNode(Request $request) { $node = null; if ($request->attributes->has($this->contentObjectKey)) { @@ -81,8 +106,18 @@ public function getCurrentItem(Request $request) } elseif ($request->attributes->has($this->routeNameKey)) { $route = $request->attributes->get($this->routeNameKey); - $node = $this->documentManager->getRepository('CmfMenuBundle:MenuNode') - ->findOneBy(array('route' => $rotue)); + $nodes = $this->documentManager->getRepository('CmfMenuBundle:MenuNode') + ->findBy(array('route' => $route)); + if (1 === count($nodes)) { + $node = $nodes->first(); + } else { + foreach ($nodes as $n) { + if ('route' === $n->getLinkType()) { + $node = $n; + break; + } + } + } } return $node; diff --git a/Tests/Functional/Templating/MenuHelperTest.php b/Tests/Functional/Templating/MenuHelperTest.php new file mode 100644 index 00000000..b254b08a --- /dev/null +++ b/Tests/Functional/Templating/MenuHelperTest.php @@ -0,0 +1,109 @@ +db('PHPCR')->loadFixtures(array( + 'Symfony\Cmf\Bundle\MenuBundle\Tests\Resources\DataFixtures\PHPCR\LoadMenuData', + )); + + $this->helper = new MenuHelper($this->db('PHPCR')->getOm(), $this->getContainer()->get('knp_menu.factory')); + } + + /** + * @dataProvider provideGetBreadcrumbArrayData + */ + public function testGetBreadcrumbArray($includeMenuRoot) + { + $currentNode = $this->db('PHPCR')->getOm()->find(null, '/test/menus/test-menu/item-2/sub-item-2'); + + $breadcrumbs = $this->helper->getBreadcrumbArray($currentNode, $includeMenuRoot); + + // simplify the returned breadcrumb array + $breadcrumbs = array_map(function ($breadcrumb) { + return array('uri' => $breadcrumb['uri'], 'item_name' => $breadcrumb['item']->getName()); + }, $breadcrumbs); + + $expectedBreadcrumbs = array_merge( + $includeMenuRoot ? array(array('uri' => null, 'item_name' => 'test-menu')) : array(), + array( + array('uri' => 'http://www.example.com', 'item_name' => 'item-2'), + array('uri' => '/link_test_route', 'item_name' => 'sub-item-2'), + ) + ); + + $this->assertEquals($expectedBreadcrumbs, $breadcrumbs); + } + + public function provideGetBreadcrumbArrayData() + { + return array('menu root included' => array(true), 'menu route excluded' => array(false)); + } + + /** + * @dataProvider provideGetCurrentNodeWithRouteData + */ + public function testGetCurrentNodeWithRoute($routeName, $nodeName) + { + $attributes = $this->prophesize('Symfony\Component\HttpFoundation\ParameterBag'); + $attributes->has(RouteObjectInterface::CONTENT_OBJECT)->willReturn(false); + $attributes->has(RouteObjectInterface::ROUTE_NAME)->willReturn(true); + $attributes->get(RouteObjectInterface::ROUTE_NAME)->willReturn($routeName); + + $request = $this->prophesize('Symfony\Component\HttpFoundation\Request'); + $request->attributes = $attributes->reveal(); + + $node = $this->helper->getCurrentNode($request->reveal()); + $this->assertInstanceOf('Knp\Menu\NodeInterface', $node); + $this->assertEquals($nodeName, $node->getName()); + } + + public function provideGetCurrentNodeWithRouteData() + { + return array( + 'simple route refering node' => array('link_test_route_with_params', 'sub-item-3'), + 'multiple matching nodes' => array('link_test_route', 'item-1'), + ); + } + + public function testGetCurrentNodeWithContent() + { + $this->markTestIncomplete('Matching by content is not yet supported'); + } + + public function testGetCurrentNodeWithoutMatch() + { + $attributes = $this->prophesize('Symfony\Component\HttpFoundation\ParameterBag'); + $attributes->has(RouteObjectInterface::CONTENT_OBJECT)->willReturn(false); + $attributes->has(RouteObjectInterface::ROUTE_NAME)->willReturn(false); + + $request = $this->prophesize('Symfony\Component\HttpFoundation\Request'); + $request->attributes = $attributes->reveal(); + + $this->assertEquals(null, $this->helper->getCurrentNode($request->reveal())); + } + + public function testGetCurrentItemWithMatch() + { + $attributes = $this->prophesize('Symfony\Component\HttpFoundation\ParameterBag'); + $attributes->has(RouteObjectInterface::CONTENT_OBJECT)->willReturn(false); + $attributes->has(RouteObjectInterface::ROUTE_NAME)->willReturn(true); + $attributes->get(RouteObjectInterface::ROUTE_NAME)->willReturn('link_test_route_with_params'); + + $request = $this->prophesize('Symfony\Component\HttpFoundation\Request'); + $request->attributes = $attributes->reveal(); + + $item = $this->helper->getCurrentItem($request->reveal()); + $this->assertInstanceOf('Knp\Menu\ItemInterface', $item); + $this->assertEquals('sub-item-3', $item->getName()); + } +} diff --git a/Twig/MenuExtension.php b/Twig/MenuExtension.php new file mode 100644 index 00000000..fe74bac8 --- /dev/null +++ b/Twig/MenuExtension.php @@ -0,0 +1,31 @@ +helper = $helper; + } + + public function getFunctions() + { + return array( + new \Twig_SimpleFunction('cmf_menu_get_breadcrumbs_array', array($this->helper, 'getBreadcrumbsArray')), + new \Twig_SimpleFunction('cmf_menu_get_current_item', array($this->helper, 'getCurrentItem')), + ); + } + + public function getName() + { + return 'cmf_menu'; + } +} From 46e1479b038876c8e0e81133bf5e9abf065967b6 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Sat, 9 Jan 2016 16:22:07 +0100 Subject: [PATCH 3/7] Register as services --- DependencyInjection/CmfMenuExtension.php | 10 ++- DependencyInjection/Configuration.php | 4 + Resources/config/persistence-phpcr.xml | 14 ++++ Resources/config/schema/menu-1.0.xsd | 2 + Templating/MenuHelper.php | 76 ++++++++++++++----- .../Functional/Templating/MenuHelperTest.php | 12 ++- Tests/Resources/Fixtures/config/config2.xml | 4 +- Twig/MenuExtension.php | 15 ++++ 8 files changed, 113 insertions(+), 24 deletions(-) diff --git a/DependencyInjection/CmfMenuExtension.php b/DependencyInjection/CmfMenuExtension.php index 9be7d370..bd6011c7 100644 --- a/DependencyInjection/CmfMenuExtension.php +++ b/DependencyInjection/CmfMenuExtension.php @@ -32,7 +32,15 @@ public function load(array $configs, ContainerBuilder $container) $loader->load('menu.xml'); $container->setAlias('cmf_menu.content_router', $config['content_url_generator']); - $container->setParameter($this->getAlias().'.allow_empty_items', $config['allow_empty_items']); + + $settingToParameterMap = array( + 'allow_empty_items' => 'allow_empty_items', + 'content_key' => 'request_content_key', + 'route_name_key' => 'request_route_name_key', + ); + foreach ($settingToParameterMap as $setting => $parameter) { + $container->setParameter('cmf_menu.'.$parameter , $config[$setting]); + } $this->loadVoters($config, $loader, $container); diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index 789fc63e..caf176d9 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -13,11 +13,13 @@ use Symfony\Component\Config\Definition\Builder\TreeBuilder; use Symfony\Component\Config\Definition\ConfigurationInterface; +use Symfony\Cmf\Component\Routing\RouteObjectInterface; class Configuration implements ConfigurationInterface { public function getConfigTreeBuilder() { + $cmfRoutingAvailable = interface_exists('Symfony\Cmf\Component\Routing\RouteObjectInterface'); $treeBuilder = new TreeBuilder(); $treeBuilder->root('cmf_menu') @@ -51,6 +53,8 @@ public function getConfigTreeBuilder() ->scalarNode('content_url_generator')->defaultValue('router')->end() ->booleanNode('allow_empty_items')->defaultFalse()->end() + ->scalarNode('content_key')->defaultValue($cmfRoutingAvailable ? RouteObjectInterface::CONTENT_OBJECT : '')->end() + ->scalarNode('route_name_key')->defaultValue($cmfRoutingAvailable ? RouteObjectInterface::ROUTE_NAME : '')->end() ->arrayNode('voters') ->children() diff --git a/Resources/config/persistence-phpcr.xml b/Resources/config/persistence-phpcr.xml index 1d3d93f4..8ce64b61 100644 --- a/Resources/config/persistence-phpcr.xml +++ b/Resources/config/persistence-phpcr.xml @@ -28,6 +28,20 @@ + + + + %cmf_menu.request_content_key% + %cmf_menu.request_route_name_key% + %cmf_menu.persistence.phpcr.manager_name% + + + + + + + + diff --git a/Resources/config/schema/menu-1.0.xsd b/Resources/config/schema/menu-1.0.xsd index 82ff01d3..e7a1d605 100644 --- a/Resources/config/schema/menu-1.0.xsd +++ b/Resources/config/schema/menu-1.0.xsd @@ -16,6 +16,8 @@ + + diff --git a/Templating/MenuHelper.php b/Templating/MenuHelper.php index 44d17d6a..0f9985ca 100644 --- a/Templating/MenuHelper.php +++ b/Templating/MenuHelper.php @@ -1,11 +1,20 @@ documentManager = $documentManager; + $this->managerRegistry = $managerRegistry; $this->menuFactory = $menuFactory; $this->contentObjectKey = $contentObjectKey; $this->routeNameKey = $routeNameKey; } + /** + * Set the object manager name to use for this loader. If not set, the + * default manager as decided by the manager registry will be used. + * + * @param string|null $managerName + */ + public function setManagerName($managerName) + { + $this->managerName = $managerName; + } + /** * Generates an array of breadcrumb items by traversing * up the tree from the current node. @@ -80,49 +101,62 @@ public function getBreadcrumbArray(NodeInterface $node, $includeMenuRoot = true) /** * Tries to find the current item from the request. * + * The returned item does *not* include the parent and children, + * in order to minimalize the overhead. + * * @param Request $request * - * @return MenuNode|null + * @return ItemInterface|null */ public function getCurrentItem(Request $request) { $node = $this->getCurrentNode($request); if (!$node instanceof NodeInterface) { - return null; + return; } return $this->menuFactory->createItem($node->getName(), $node->getOptions()); } + /** + * Retrieves the current node based on a Request. + * + * It uses some special Request attributes that are managed by + * the CmfRoutingBundle: + * + * * RouteObjectInterface::CONTENT_OBJECT to match a menu node by the refering content + * * RouteObjectInterface::ROUTE_NAME to match a menu node by the refering route name + * + * @return NodeInterface|null + */ public function getCurrentNode(Request $request) { - $node = null; if ($request->attributes->has($this->contentObjectKey)) { $content = $request->attributes->get($this->contentObjectKey); - $node = $this->documentManager->getRepository('CmfMenuBundle:MenuNode') - ->findOneBy(array('content' => $content)); - } elseif ($request->attributes->has($this->routeNameKey)) { + return $this->managerRegistry->getManager($this->managerName)->getRepository('CmfMenuBundle:MenuNode')->findOneBy(array('content' => $content)); + } + + if ($request->attributes->has($this->routeNameKey)) { $route = $request->attributes->get($this->routeNameKey); - $nodes = $this->documentManager->getRepository('CmfMenuBundle:MenuNode') - ->findBy(array('route' => $route)); + $nodes = $this->managerRegistry->getManager($this->managerName)->getRepository('CmfMenuBundle:MenuNode')->findBy(array('route' => $route)); if (1 === count($nodes)) { - $node = $nodes->first(); + return $nodes->first(); } else { - foreach ($nodes as $n) { - if ('route' === $n->getLinkType()) { - $node = $n; - break; + foreach ($nodes as $node) { + if ('route' === $node->getLinkType()) { + return $node; } } } } - - return $node; } + /** + * {@inheritdoc} + */ public function getName() { return 'cmf_menu'; diff --git a/Tests/Functional/Templating/MenuHelperTest.php b/Tests/Functional/Templating/MenuHelperTest.php index b254b08a..d7dd9b67 100644 --- a/Tests/Functional/Templating/MenuHelperTest.php +++ b/Tests/Functional/Templating/MenuHelperTest.php @@ -1,5 +1,14 @@ helper = new MenuHelper($this->db('PHPCR')->getOm(), $this->getContainer()->get('knp_menu.factory')); + $container = $this->getContainer(); + $this->helper = new MenuHelper($container->get('doctrine_phpcr'), $container->get('knp_menu.factory')); } /** diff --git a/Tests/Resources/Fixtures/config/config2.xml b/Tests/Resources/Fixtures/config/config2.xml index d387187a..fa5dac0b 100644 --- a/Tests/Resources/Fixtures/config/config2.xml +++ b/Tests/Resources/Fixtures/config/config2.xml @@ -1,6 +1,8 @@ + allow-empty-items="false" + content-key="_custom_content" + route-name-key="_custom_route_name"> helper = $helper; } + /** + * {@inheritdoc} + */ public function getFunctions() { return array( @@ -24,6 +36,9 @@ public function getFunctions() ); } + /** + * {@inheritdoc} + */ public function getName() { return 'cmf_menu'; From ab949abfa44ba158692cdbcbe139d1c64eb04847 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Sat, 9 Jan 2016 17:18:59 +0100 Subject: [PATCH 4/7] Fix tests --- .travis.yml | 2 +- Templating/MenuHelper.php | 25 ++++++++++++++++--------- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/.travis.yml b/.travis.yml index af5f89cf..dbe7001b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,7 +15,7 @@ cache: - $HOME/.composer/cache/files env: - global: SYMFONY_DEPRECATIONS_HELPER=533 + global: SYMFONY_DEPRECATIONS_HELPER=548 matrix: include: diff --git a/Templating/MenuHelper.php b/Templating/MenuHelper.php index 0f9985ca..0575f167 100644 --- a/Templating/MenuHelper.php +++ b/Templating/MenuHelper.php @@ -132,23 +132,30 @@ public function getCurrentItem(Request $request) */ public function getCurrentNode(Request $request) { + $repository = $this->managerRegistry->getManager($this->managerName) + ->getRepository('Symfony\Cmf\Bundle\MenuBundle\Doctrine\Phpcr\MenuNode'); + if ($request->attributes->has($this->contentObjectKey)) { $content = $request->attributes->get($this->contentObjectKey); - return $this->managerRegistry->getManager($this->managerName)->getRepository('CmfMenuBundle:MenuNode')->findOneBy(array('content' => $content)); + return $this->filterByLinkType($repository->findBy(array('content' => $content)), 'content'); } if ($request->attributes->has($this->routeNameKey)) { $route = $request->attributes->get($this->routeNameKey); - $nodes = $this->managerRegistry->getManager($this->managerName)->getRepository('CmfMenuBundle:MenuNode')->findBy(array('route' => $route)); - if (1 === count($nodes)) { - return $nodes->first(); - } else { - foreach ($nodes as $node) { - if ('route' === $node->getLinkType()) { - return $node; - } + return $this->filterByLinkType($repository->findBy(array('route' => $route)), 'route'); + } + } + + private function filterByLinkType($nodes, $type) + { + if (1 === count($nodes)) { + return $nodes->first(); + } else { + foreach ($nodes as $node) { + if ($type === $node->getLinkType()) { + return $node; } } } From 5f8c272e63ade30905e4ee33aa6d80f19a618f58 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Fri, 15 Jan 2016 19:59:01 +0100 Subject: [PATCH 5/7] CI fixes --- DependencyInjection/CmfMenuExtension.php | 2 +- Templating/MenuHelper.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DependencyInjection/CmfMenuExtension.php b/DependencyInjection/CmfMenuExtension.php index bd6011c7..12dc0ed2 100644 --- a/DependencyInjection/CmfMenuExtension.php +++ b/DependencyInjection/CmfMenuExtension.php @@ -39,7 +39,7 @@ public function load(array $configs, ContainerBuilder $container) 'route_name_key' => 'request_route_name_key', ); foreach ($settingToParameterMap as $setting => $parameter) { - $container->setParameter('cmf_menu.'.$parameter , $config[$setting]); + $container->setParameter('cmf_menu.'.$parameter, $config[$setting]); } $this->loadVoters($config, $loader, $container); diff --git a/Templating/MenuHelper.php b/Templating/MenuHelper.php index 0575f167..6287be76 100644 --- a/Templating/MenuHelper.php +++ b/Templating/MenuHelper.php @@ -140,7 +140,7 @@ public function getCurrentNode(Request $request) return $this->filterByLinkType($repository->findBy(array('content' => $content)), 'content'); } - + if ($request->attributes->has($this->routeNameKey)) { $route = $request->attributes->get($this->routeNameKey); From 6b0d289b5555ad1fa3b4aaa7d1c5c5afbfdf0203 Mon Sep 17 00:00:00 2001 From: WouterJ Date: Fri, 15 Jan 2016 20:11:41 +0100 Subject: [PATCH 6/7] Add content menu referrer support --- Templating/MenuHelper.php | 8 +++- .../Functional/Templating/MenuHelperTest.php | 37 ++++++++++++++++++- 2 files changed, 42 insertions(+), 3 deletions(-) diff --git a/Templating/MenuHelper.php b/Templating/MenuHelper.php index 6287be76..962a7b72 100644 --- a/Templating/MenuHelper.php +++ b/Templating/MenuHelper.php @@ -15,10 +15,12 @@ use Knp\Menu\NodeInterface; use Knp\Menu\FactoryInterface; use Doctrine\Common\Persistence\ManagerRegistry; +use Doctrine\Common\Collections\ArrayCollection; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Templating\Helper\Helper; use Symfony\Cmf\Component\Routing\RouteObjectInterface; use Symfony\Cmf\Bundle\MenuBundle\Model\MenuNode; +use Symfony\Cmf\Bundle\MenuBundle\Model\MenuNodeReferrersInterface; /** * A templating helper providing faster solutions to @@ -138,7 +140,9 @@ public function getCurrentNode(Request $request) if ($request->attributes->has($this->contentObjectKey)) { $content = $request->attributes->get($this->contentObjectKey); - return $this->filterByLinkType($repository->findBy(array('content' => $content)), 'content'); + if ($content instanceof MenuNodeReferrersInterface) { + return $this->filterByLinkType(new ArrayCollection($content->getMenuNodes()), 'content'); + } } if ($request->attributes->has($this->routeNameKey)) { @@ -148,7 +152,7 @@ public function getCurrentNode(Request $request) } } - private function filterByLinkType($nodes, $type) + private function filterByLinkType(\Traversable $nodes, $type) { if (1 === count($nodes)) { return $nodes->first(); diff --git a/Tests/Functional/Templating/MenuHelperTest.php b/Tests/Functional/Templating/MenuHelperTest.php index d7dd9b67..632316e5 100644 --- a/Tests/Functional/Templating/MenuHelperTest.php +++ b/Tests/Functional/Templating/MenuHelperTest.php @@ -11,9 +11,11 @@ namespace Symfony\Cmf\Bundle\MenuBundle\Tests\Functional\Templating; +use Symfony\Cmf\Bundle\MenuBundle\Model\MenuNodeReferrersInterface; use Symfony\Cmf\Bundle\MenuBundle\Templating\MenuHelper; use Symfony\Cmf\Component\Routing\RouteObjectInterface; use Symfony\Cmf\Component\Testing\Functional\BaseTestCase; +use Knp\Menu\NodeInterface; class MenuHelperTest extends BaseTestCase { @@ -87,7 +89,20 @@ public function provideGetCurrentNodeWithRouteData() public function testGetCurrentNodeWithContent() { - $this->markTestIncomplete('Matching by content is not yet supported'); + $content = new MenuHelperTest_NodeReferrer(); + $content->addMenuNode($this->db('PHPCR')->getOm()->find(null, '/test/menus/test-menu/item-1')); + + $attributes = $this->prophesize('Symfony\Component\HttpFoundation\ParameterBag'); + $attributes->has(RouteObjectInterface::CONTENT_OBJECT)->willReturn(true); + $attributes->has(RouteObjectInterface::ROUTE_NAME)->willReturn(true); + $attributes->get(RouteObjectInterface::CONTENT_OBJECT)->willReturn($content); + + $request = $this->prophesize('Symfony\Component\HttpFoundation\Request'); + $request->attributes = $attributes->reveal(); + + $node = $this->helper->getCurrentNode($request->reveal()); + $this->assertInstanceOf('Knp\Menu\NodeInterface', $node); + $this->assertEquals('item-1', $node->getName()); } public function testGetCurrentNodeWithoutMatch() @@ -117,3 +132,23 @@ public function testGetCurrentItemWithMatch() $this->assertEquals('sub-item-3', $item->getName()); } } + +class MenuHelperTest_NodeReferrer implements MenuNodeReferrersInterface +{ + private $nodes = array(); + + public function getMenuNodes() + { + return $this->nodes; + } + + public function addMenuNode(NodeInterface $menu) + { + $this->nodes[] = $menu; + } + + public function removeMenuNode(NodeInterface $menu) + { + // dummy + } +} From 9065ce3cf23dc4c4481192ffba45b7ec5e08d47e Mon Sep 17 00:00:00 2001 From: WouterJ Date: Sat, 23 Jan 2016 00:16:39 +0100 Subject: [PATCH 7/7] Fixed some bugs --- DependencyInjection/Configuration.php | 8 ++--- Templating/MenuHelper.php | 18 +++++++---- .../Functional/Templating/MenuHelperTest.php | 32 +++++++++---------- Twig/MenuExtension.php | 1 + 4 files changed, 32 insertions(+), 27 deletions(-) diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index caf176d9..6ddc87db 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -13,13 +13,13 @@ use Symfony\Component\Config\Definition\Builder\TreeBuilder; use Symfony\Component\Config\Definition\ConfigurationInterface; -use Symfony\Cmf\Component\Routing\RouteObjectInterface; +use Symfony\Cmf\Bundle\RoutingBundle\Routing\DynamicRouter; class Configuration implements ConfigurationInterface { public function getConfigTreeBuilder() { - $cmfRoutingAvailable = interface_exists('Symfony\Cmf\Component\Routing\RouteObjectInterface'); + $cmfRoutingAvailable = class_exists('Symfony\Cmf\Bundle\RoutingBundle\Routing\DynamicRouter'); $treeBuilder = new TreeBuilder(); $treeBuilder->root('cmf_menu') @@ -53,8 +53,8 @@ public function getConfigTreeBuilder() ->scalarNode('content_url_generator')->defaultValue('router')->end() ->booleanNode('allow_empty_items')->defaultFalse()->end() - ->scalarNode('content_key')->defaultValue($cmfRoutingAvailable ? RouteObjectInterface::CONTENT_OBJECT : '')->end() - ->scalarNode('route_name_key')->defaultValue($cmfRoutingAvailable ? RouteObjectInterface::ROUTE_NAME : '')->end() + ->scalarNode('content_key')->defaultValue($cmfRoutingAvailable ? DynamicRouter::CONTENT_KEY : '')->end() + ->scalarNode('route_name_key')->defaultValue($cmfRoutingAvailable ? DynamicRouter::ROUTE_KEY : '')->end() ->arrayNode('voters') ->children() diff --git a/Templating/MenuHelper.php b/Templating/MenuHelper.php index 962a7b72..5a68d979 100644 --- a/Templating/MenuHelper.php +++ b/Templating/MenuHelper.php @@ -18,7 +18,7 @@ use Doctrine\Common\Collections\ArrayCollection; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Templating\Helper\Helper; -use Symfony\Cmf\Component\Routing\RouteObjectInterface; +use Symfony\Cmf\Bundle\RoutingBundle\Routing\DynamicRouter; use Symfony\Cmf\Bundle\MenuBundle\Model\MenuNode; use Symfony\Cmf\Bundle\MenuBundle\Model\MenuNodeReferrersInterface; @@ -51,7 +51,7 @@ class MenuHelper extends Helper * @param string $routeNameKey The name of the request attribute holding * the name of the current route */ - public function __construct(ManagerRegistry $managerRegistry, FactoryInterface $menuFactory, $contentObjectKey = RouteObjectInterface::CONTENT_OBJECT, $routeNameKey = RouteObjectInterface::ROUTE_NAME) + public function __construct(ManagerRegistry $managerRegistry, FactoryInterface $menuFactory, $contentObjectKey = DynamicRouter::CONTENT_KEY, $routeNameKey = DynamicRouter::ROUTE_KEY) { $this->managerRegistry = $managerRegistry; $this->menuFactory = $menuFactory; @@ -79,7 +79,7 @@ public function setManagerName($managerName) * * @return array An array with breadcrumb items (each item has the following keys: label, uri, item) */ - public function getBreadcrumbArray(NodeInterface $node, $includeMenuRoot = true) + public function getBreadcrumbsArray(NodeInterface $node, $includeMenuRoot = true) { $item = $this->menuFactory->createItem($node->getName(), $node->getOptions()); @@ -97,7 +97,7 @@ public function getBreadcrumbArray(NodeInterface $node, $includeMenuRoot = true) return $includeMenuRoot ? $breadcrumbs : array(); } - return array_merge($this->getBreadcrumbArray($parent, $includeMenuRoot), $breadcrumbs); + return array_merge($this->getBreadcrumbsArray($parent, $includeMenuRoot), $breadcrumbs); } /** @@ -127,8 +127,8 @@ public function getCurrentItem(Request $request) * It uses some special Request attributes that are managed by * the CmfRoutingBundle: * - * * RouteObjectInterface::CONTENT_OBJECT to match a menu node by the refering content - * * RouteObjectInterface::ROUTE_NAME to match a menu node by the refering route name + * * DynamicRouter::CONTENT_KEY to match a menu node by the refering content + * * DynamicRouter::ROUTE_KEY to match a menu node by the refering route name * * @return NodeInterface|null */ @@ -141,7 +141,11 @@ public function getCurrentNode(Request $request) $content = $request->attributes->get($this->contentObjectKey); if ($content instanceof MenuNodeReferrersInterface) { - return $this->filterByLinkType(new ArrayCollection($content->getMenuNodes()), 'content'); + $node = $this->filterByLinkType(new ArrayCollection($content->getMenuNodes()), 'content'); + + if ($node) { + return $node; + } } } diff --git a/Tests/Functional/Templating/MenuHelperTest.php b/Tests/Functional/Templating/MenuHelperTest.php index 632316e5..2029cb4c 100644 --- a/Tests/Functional/Templating/MenuHelperTest.php +++ b/Tests/Functional/Templating/MenuHelperTest.php @@ -13,7 +13,7 @@ use Symfony\Cmf\Bundle\MenuBundle\Model\MenuNodeReferrersInterface; use Symfony\Cmf\Bundle\MenuBundle\Templating\MenuHelper; -use Symfony\Cmf\Component\Routing\RouteObjectInterface; +use Symfony\Cmf\Bundle\RoutingBundle\Routing\DynamicRouter; use Symfony\Cmf\Component\Testing\Functional\BaseTestCase; use Knp\Menu\NodeInterface; @@ -32,13 +32,13 @@ protected function setUp() } /** - * @dataProvider provideGetBreadcrumbArrayData + * @dataProvider provideGetBreadcrumbsArrayData */ - public function testGetBreadcrumbArray($includeMenuRoot) + public function testGetBreadcrumbsArray($includeMenuRoot) { $currentNode = $this->db('PHPCR')->getOm()->find(null, '/test/menus/test-menu/item-2/sub-item-2'); - $breadcrumbs = $this->helper->getBreadcrumbArray($currentNode, $includeMenuRoot); + $breadcrumbs = $this->helper->getBreadcrumbsArray($currentNode, $includeMenuRoot); // simplify the returned breadcrumb array $breadcrumbs = array_map(function ($breadcrumb) { @@ -56,7 +56,7 @@ public function testGetBreadcrumbArray($includeMenuRoot) $this->assertEquals($expectedBreadcrumbs, $breadcrumbs); } - public function provideGetBreadcrumbArrayData() + public function provideGetBreadcrumbsArrayData() { return array('menu root included' => array(true), 'menu route excluded' => array(false)); } @@ -67,9 +67,9 @@ public function provideGetBreadcrumbArrayData() public function testGetCurrentNodeWithRoute($routeName, $nodeName) { $attributes = $this->prophesize('Symfony\Component\HttpFoundation\ParameterBag'); - $attributes->has(RouteObjectInterface::CONTENT_OBJECT)->willReturn(false); - $attributes->has(RouteObjectInterface::ROUTE_NAME)->willReturn(true); - $attributes->get(RouteObjectInterface::ROUTE_NAME)->willReturn($routeName); + $attributes->has(DynamicRouter::CONTENT_KEY)->willReturn(false); + $attributes->has(DynamicRouter::ROUTE_KEY)->willReturn(true); + $attributes->get(DynamicRouter::ROUTE_KEY)->willReturn($routeName); $request = $this->prophesize('Symfony\Component\HttpFoundation\Request'); $request->attributes = $attributes->reveal(); @@ -93,9 +93,9 @@ public function testGetCurrentNodeWithContent() $content->addMenuNode($this->db('PHPCR')->getOm()->find(null, '/test/menus/test-menu/item-1')); $attributes = $this->prophesize('Symfony\Component\HttpFoundation\ParameterBag'); - $attributes->has(RouteObjectInterface::CONTENT_OBJECT)->willReturn(true); - $attributes->has(RouteObjectInterface::ROUTE_NAME)->willReturn(true); - $attributes->get(RouteObjectInterface::CONTENT_OBJECT)->willReturn($content); + $attributes->has(DynamicRouter::CONTENT_KEY)->willReturn(true); + $attributes->has(DynamicRouter::ROUTE_KEY)->willReturn(true); + $attributes->get(DynamicRouter::CONTENT_KEY)->willReturn($content); $request = $this->prophesize('Symfony\Component\HttpFoundation\Request'); $request->attributes = $attributes->reveal(); @@ -108,8 +108,8 @@ public function testGetCurrentNodeWithContent() public function testGetCurrentNodeWithoutMatch() { $attributes = $this->prophesize('Symfony\Component\HttpFoundation\ParameterBag'); - $attributes->has(RouteObjectInterface::CONTENT_OBJECT)->willReturn(false); - $attributes->has(RouteObjectInterface::ROUTE_NAME)->willReturn(false); + $attributes->has(DynamicRouter::CONTENT_KEY)->willReturn(false); + $attributes->has(DynamicRouter::ROUTE_KEY)->willReturn(false); $request = $this->prophesize('Symfony\Component\HttpFoundation\Request'); $request->attributes = $attributes->reveal(); @@ -120,9 +120,9 @@ public function testGetCurrentNodeWithoutMatch() public function testGetCurrentItemWithMatch() { $attributes = $this->prophesize('Symfony\Component\HttpFoundation\ParameterBag'); - $attributes->has(RouteObjectInterface::CONTENT_OBJECT)->willReturn(false); - $attributes->has(RouteObjectInterface::ROUTE_NAME)->willReturn(true); - $attributes->get(RouteObjectInterface::ROUTE_NAME)->willReturn('link_test_route_with_params'); + $attributes->has(DynamicRouter::CONTENT_KEY)->willReturn(false); + $attributes->has(DynamicRouter::ROUTE_KEY)->willReturn(true); + $attributes->get(DynamicRouter::ROUTE_KEY)->willReturn('link_test_route_with_params'); $request = $this->prophesize('Symfony\Component\HttpFoundation\Request'); $request->attributes = $attributes->reveal(); diff --git a/Twig/MenuExtension.php b/Twig/MenuExtension.php index 85a9b5f1..919d1688 100644 --- a/Twig/MenuExtension.php +++ b/Twig/MenuExtension.php @@ -33,6 +33,7 @@ public function getFunctions() return array( new \Twig_SimpleFunction('cmf_menu_get_breadcrumbs_array', array($this->helper, 'getBreadcrumbsArray')), new \Twig_SimpleFunction('cmf_menu_get_current_item', array($this->helper, 'getCurrentItem')), + new \Twig_SimpleFunction('cmf_menu_get_current_node', array($this->helper, 'getCurrentNode')), ); }