Skip to content
This repository has been archived by the owner on Sep 16, 2021. It is now read-only.

[POC] Provide a faster menu helper #241

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ cache:
- $HOME/.composer/cache/files

env:
global: SYMFONY_DEPRECATIONS_HELPER=533
global: SYMFONY_DEPRECATIONS_HELPER=548

matrix:
include:
Expand Down
10 changes: 9 additions & 1 deletion DependencyInjection/CmfMenuExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
4 changes: 4 additions & 0 deletions DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@

use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;
use Symfony\Cmf\Bundle\RoutingBundle\Routing\DynamicRouter;

class Configuration implements ConfigurationInterface
{
public function getConfigTreeBuilder()
{
$cmfRoutingAvailable = class_exists('Symfony\Cmf\Bundle\RoutingBundle\Routing\DynamicRouter');
$treeBuilder = new TreeBuilder();

$treeBuilder->root('cmf_menu')
Expand Down Expand Up @@ -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 ? DynamicRouter::CONTENT_KEY : '')->end()
->scalarNode('route_name_key')->defaultValue($cmfRoutingAvailable ? DynamicRouter::ROUTE_KEY : '')->end()

->arrayNode('voters')
->children()
Expand Down
14 changes: 14 additions & 0 deletions Resources/config/persistence-phpcr.xml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,20 @@
<tag name="doctrine_phpcr.initializer"/>
</service>

<service id="cmf_menu.templating.helper" class="Symfony\Cmf\Bundle\MenuBundle\Templating\MenuHelper" public="false">
<argument type="service" id="doctrine_phpcr"/>
<argument type="service" id="knp_menu.factory"/>
<argument>%cmf_menu.request_content_key%</argument>
<argument>%cmf_menu.request_route_name_key%</argument>
<call method="setManagerName"><argument>%cmf_menu.persistence.phpcr.manager_name%</argument></call>
<tag name="templating.helper" alias="cmf_menu"/>
</service>

<service id="cmf_menu.twig.extension" class="Symfony\Cmf\Bundle\MenuBundle\Twig\MenuExtension" public="false">
<argument type="service" id="cmf_menu.templating.helper"/>
<tag name="twig.extension"/>
</service>

</services>

</container>
2 changes: 2 additions & 0 deletions Resources/config/schema/menu-1.0.xsd
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

<xsd:attribute name="content-url-generator" type="xsd:string" />
<xsd:attribute name="allow-empty-items" type="xsd:boolean" />
<xsd:attribute name="content-key" type="xsd:string" />
<xsd:attribute name="route-name-key" type="xsd:string" />
</xsd:complexType>

<xsd:complexType name="persistence">
Expand Down
179 changes: 179 additions & 0 deletions Templating/MenuHelper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
<?php

/*
* This file is part of the Symfony CMF package.
*
* (c) 2011-2015 Symfony CMF
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Cmf\Bundle\MenuBundle\Templating;

use Knp\Menu\ItemInterface;
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\Bundle\RoutingBundle\Routing\DynamicRouter;
use Symfony\Cmf\Bundle\MenuBundle\Model\MenuNode;
use Symfony\Cmf\Bundle\MenuBundle\Model\MenuNodeReferrersInterface;

/**
* A templating helper providing faster solutions to
* the KnpMenu alternatives.
*
* @author Wouter de Jong <[email protected]>
*/
class MenuHelper extends Helper
{
/**
* @var ManagerRegistry
*/
private $managerRegistry;

/**
* @var FactoryInterface
*/
private $menuFactory;
private $managerName;
private $contentObjectKey;
private $routeNameKey;

/**
* @param ManagerRegistry $managerRegistry
* @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(ManagerRegistry $managerRegistry, FactoryInterface $menuFactory, $contentObjectKey = DynamicRouter::CONTENT_KEY, $routeNameKey = DynamicRouter::ROUTE_KEY)
{
$this->managerRegistry = $managerRegistry;
$this->menuFactory = $menuFactory;
$this->contentObjectKey = $contentObjectKey;
$this->routeNameKey = $routeNameKey;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing property declaration

}

/**
* 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.
*
* @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 getBreadcrumbsArray(NodeInterface $node, $includeMenuRoot = true)
{
$item = $this->menuFactory->createItem($node->getName(), $node->getOptions());

$breadcrumbs = array(
array(
'label' => $item->getLabel(),
'uri' => $item->getUri(),
'item' => $item,
),
);

$parent = $node->getParentObject();
if (!$parent instanceof MenuNode) {
// We assume the root of the menu is reached
return $includeMenuRoot ? $breadcrumbs : array();
}

return array_merge($this->getBreadcrumbsArray($parent, $includeMenuRoot), $breadcrumbs);
}

/**
* 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 ItemInterface|null
*/
public function getCurrentItem(Request $request)
{
$node = $this->getCurrentNode($request);

if (!$node instanceof NodeInterface) {
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:
*
* * 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
*/
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);

if ($content instanceof MenuNodeReferrersInterface) {
$node = $this->filterByLinkType(new ArrayCollection($content->getMenuNodes()), 'content');

if ($node) {
return $node;
}
}
}

if ($request->attributes->has($this->routeNameKey)) {
$route = $request->attributes->get($this->routeNameKey);

return $this->filterByLinkType($repository->findBy(array('route' => $route)), 'route');
}
}

private function filterByLinkType(\Traversable $nodes, $type)
{
if (1 === count($nodes)) {
return $nodes->first();
} else {
foreach ($nodes as $node) {
if ($type === $node->getLinkType()) {
return $node;
}
}
}
}

/**
* {@inheritdoc}
*/
public function getName()
{
return 'cmf_menu';
}
}
Loading