Skip to content

Commit

Permalink
Refactored creation of sidebar menu by using menu builder
Browse files Browse the repository at this point in the history
  • Loading branch information
hason committed May 28, 2015
1 parent 31b8e11 commit 3f6874f
Show file tree
Hide file tree
Showing 13 changed files with 453 additions and 344 deletions.
10 changes: 10 additions & 0 deletions DependencyInjection/SonataAdminExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,16 @@ public function load(array $configs, ContainerBuilder $container)
$loader->load('validator.xml');
$loader->load('route.xml');
$loader->load('block.xml');
$loader->load('menu.xml');

// TODO: Go back on xml configuration when bumping requirements to SF 2.6+
$sidebarMenu = $container->getDefinition('sonata.admin.sidebar_menu');
if (method_exists($sidebarMenu, 'setFactory')) {
$sidebarMenu->setFactory(array(new Reference('sonata.admin.menu_builder'), 'createSidebarMenu'));
} else {
$sidebarMenu->setFactoryService('sonata.admin.menu_builder');
$sidebarMenu->setFactoryMethod('createSidebarMenu');
}

$configuration = new Configuration();
$processor = new Processor();
Expand Down
135 changes: 135 additions & 0 deletions Menu/MenuBuilder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
<?php
/*
* This file is part of the Sonata package.
*
* (c) Thomas Rabaix <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
*/

namespace Sonata\AdminBundle\Menu;

use Knp\Menu\FactoryInterface;
use Knp\Menu\ItemInterface;
use Knp\Menu\Provider\MenuProviderInterface;
use Sonata\AdminBundle\Admin\Pool;
use Symfony\Component\HttpFoundation\Request;

/**
* Sonata menu builder.
*
* @author Martin Hasoň <[email protected]>
*/
class MenuBuilder
{
private $pool;
private $factory;
private $provider;
private $request;

/**
* Constructor.
*
* @param Pool $pool
* @param FactoryInterface $factory
* @param MenuProviderInterface $provider
*/
public function __construct(Pool $pool, FactoryInterface $factory, MenuProviderInterface $provider)
{
$this->pool = $pool;
$this->factory = $factory;
$this->provider = $provider;
}

/**
* Builds sidebar menu.
*
* @return ItemInterface
*/
public function createSidebarMenu()
{
$menu = $this->factory->createItem('root', array(
'extras' => array(
'request' => $this->request,
),
));

foreach ($this->pool->getAdminGroups() as $name => $group) {
$attributes = array(
'icon' => $group['icon'],
'label_catalogue' => $group['label_catalogue'],
);

$extras = array(
'roles' => $group['roles'],
);

// Check if the menu group is built by a menu provider
if (isset($group['provider'])) {
$subMenu = $this->provider->get($group['provider']);

$menu
->addChild($subMenu)
->setExtras(array_merge($subMenu->getExtras(), $extras))
->setAttributes(array_merge($subMenu->getAttributes(), $attributes))
;

continue;
}

// The menu group is built by config
$menu->addChild($name, array(
'label' => $group['label'],
'attributes' => $attributes,
'extras' => $extras,
));

foreach ($group['items'] as $item) {
if (isset($item['admin']) && !empty($item['admin'])) {
$admin = $this->pool->getInstance($item['admin']);

// skip menu item if no `list` url is available or user doesn't have the LIST access rights
if (!$admin->hasRoute('list') || !$admin->isGranted('LIST')) {
continue;
}

$label = $admin->getLabel();
$options = $admin->generateMenuUrl('list');
$options['extras'] = array(
'translation_domain' =>$admin->getTranslationDomain(),
'admin' => $admin,
);
} else {
$label = $item['label'];
$options = array(
'route' => $item['route'],
'routeParameters' => $item['route_params'],
'extras' => array(
'translation_domain' => $group['label_catalogue'],
)
);
}

$menu[$name]->addChild($label, $options);
}

if (0 === count($menu[$name]->getChildren())) {
$menu->removeChild($name);
}
}

return $menu;
}

/**
* Sets the request the service
*
* @param Request $request
*/
public function setRequest(Request $request = null)
{
$this->request = $request;
}
}
19 changes: 19 additions & 0 deletions Resources/config/menu.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8" ?>

<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">

<services>
<service id="sonata.admin.menu_builder" class="Sonata\AdminBundle\Menu\MenuBuilder">
<argument type="service" id="sonata.admin.pool" />
<argument type="service" id="knp_menu.factory" />
<argument type="service" id="knp_menu.menu_provider" />
<call method="setRequest"><argument type="service" id="request" on-invalid="null" strict="false" /></call>
</service>

<service id="sonata.admin.sidebar_menu" class="Knp\Menu\MenuItem">
<tag name="knp_menu.menu" alias="sonata_admin_sidebar" />
</service>
</services>
</container>
2 changes: 0 additions & 2 deletions Resources/config/twig.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@
<tag name="twig.extension"/>

<argument type="service" id="sonata.admin.pool" />
<argument type="service" id="router" />
<argument type="service" id="knp_menu.helper" />
<argument type="service" id="logger" on-invalid="ignore" />
</service>
</services>
Expand Down
10 changes: 5 additions & 5 deletions Resources/doc/cookbook/recipe_knp_menu.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Create your controller:

.. code-block:: php
class BlogController
class BlogController
{
/**
* @Route("/blog", name="blog_home")
Expand Down Expand Up @@ -68,9 +68,9 @@ And voilà, now you have a menu group which contains a link to a sonata admin vi
Using a menu provider
---------------------

As seen above, the main way to declare your menu is by declaring items in your sonata admin config file. In some case you may have to create a more complexe menu depending on your business logic. This is possible by using a menu provider to populate a whole menu group. This is done with the ``provider`` config value.
As seen above, the main way to declare your menu is by declaring items in your sonata admin config file. In some case you may have to create a more complex menu depending on your business logic. This is possible by using a menu provider to populate a whole menu group. This is done with the ``provider`` config value.

Tthe following configuration uses a menu provider to populate the menu group ``my_group``:
The following configuration uses a menu provider to populate the menu group ``my_group``:

.. code-block:: yaml
Expand All @@ -81,7 +81,7 @@ Tthe following configuration uses a menu provider to populate the menu group ``m
provider: 'MyBundle:MyMenuProvider:getMyMenu'
icon: '<i class="fa fa-edit"></i>'
With KnpMenuBundle you can create a custom menu by using a builder class or by declaring it as a service. Please see the `Knp documentation <http://symfony.com/doc/current/bundles/KnpMenuBundle/index.html#create-your-first-menu>`_ for further information.
With KnpMenuBundle you can create a custom menu by using a builder class or by declaring it as a service. Please see the `Knp documentation <http://symfony.com/doc/current/bundles/KnpMenuBundle/index.html#create-your-first-menu>`_ for further information.

In sonata, whatever the implementation you choose, you only have to provide the menu alias to the provider config key:

Expand All @@ -93,4 +93,4 @@ In sonata, whatever the implementation you choose, you only have to provide the
<tag name="knp_menu.menu" alias="my_menu_alias" />
</service>
Please note that when using the provider option, you can't set the menu label via the configuration. It is done in your custom menu.
Please note that when using the provider option, you can't set the menu label via the configuration. It is done in your custom menu.
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ From the configuration file, you can add a new section named ``admin_services``
route_generator: sonata.admin.route.default_generator
validator: validator
security_handler: sonata.admin.security.handler
menu_factor: knp_menu.factory
menu_factory: knp_menu.factory
route_builder: sonata.admin.route.path_info
label_translator_strategy: sonata.admin.label.strategy.native
Expand All @@ -35,4 +35,4 @@ From the configuration file, you can add a new section named ``admin_services``
filter: [ 'MyTheme.twig.html', 'MySecondTheme.twig.html']
With these settings you will be able to change default services and templates used by the `id.of.admin.service`` admin instance.
With these settings you will be able to change default services and templates used by the `id.of.admin.service`` admin instance.
22 changes: 10 additions & 12 deletions Resources/views/Menu/sonata_menu.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,30 @@

{% block root %}
{%- set listAttributes = item.childrenAttributes|merge({'class': 'sidebar-menu'}) %}
{%- set request = item.getExtra('request') %}
{%- set request = item.extra('request') %}
{{ block('list') -}}
{% endblock %}

{% block item %}
{%- if item.displayed %}
{#- check role of the group #}
{%- set display = (item.getExtra('roles') is empty or is_granted('ROLE_SUPER_ADMIN') ) %}
{%- for role in item.getExtra('roles') if not display %}
{%- set display = (item.extra('roles') is empty or is_granted('ROLE_SUPER_ADMIN') ) %}
{%- for role in item.extra('roles') if not display %}
{%- set display = is_granted(role) %}
{%- endfor %}
{%- endif %}

{%- if item.displayed and display|default %}
{%- set active = false %}
{%- if item.getExtra('active') is not empty and item.getExtra('active') %}
{%- if item.extra('active') is not empty and item.extra('active') %}
{%- set active = true %}
{%- elseif item.getExtra('admin') is not empty and item.getExtra('admin').hasroute('list') and item.getExtra('admin').isGranted('LIST') and request.get('_sonata_admin') == item.getExtra('admin').code %}
{%- elseif item.extra('admin') is not empty and item.extra('admin').hasroute('list') and item.extra('admin').isGranted('LIST') and request.get('_sonata_admin') == item.extra('admin').code %}
{%- set active = true %}
{%- elseif item.route is defined and request.get('_route') == item.route %}
{%- set active = true %}
{%- else %}
{%- for child in item.children if not active %}
{%- if child.getExtra('admin') is not empty and child.getExtra('admin').hasroute('list') and child.getExtra('admin').isGranted('LIST') and request.get('_sonata_admin') == child.getExtra('admin').code %}
{%- if child.extra('admin') is not empty and child.extra('admin').hasroute('list') and child.extra('admin').isGranted('LIST') and request.get('_sonata_admin') == child.extra('admin').code %}
{%- set active = true %}
{%- elseif child.route is defined and request.get('_route') == child.route %}
{%- set active = true %}
Expand All @@ -48,24 +48,22 @@

{% block linkElement %}
{% spaceless %}
{% set translation_domain = item.getExtra('translationdomain', 'messages') %}
{% set icon = item.attribute('icon') ? item.attribute('icon') : (item.level > 1 ? '<i class="fa fa-angle-double-right"></i>' : '') %}
{% set translation_domain = item.extra('translation_domain', 'messages') %}
{% set icon = item.attribute('icon')|default(item.level > 1 ? '<i class="fa fa-angle-double-right"></i>' : '') %}
{% set is_link = true %}
{{ parent() }}
{% endspaceless %}
{% endblock %}

{% block spanElement %}
{% spaceless %}

<a href="#">
{% set translation_domain = item.attribute('label_catalogue') %}
{% set icon = item.attribute('icon')|default ? item.attribute('icon') : '' %}
{{ icon|default|raw }}
{% set icon = item.attribute('icon')|default('') %}
{{ parent() }}
<i class="fa pull-right fa-angle-left"></i>
</a>
{% endspaceless %}
{% endblock %}

{% block label %}{% if is_link is defined and is_link %}{{ icon|default|raw }}{% endif %}{% if options.allow_safe_labels and item.getExtra('safe_label', false) %}{{ item.label|raw }}{% else %}{{ item.label|trans({}, translation_domain|default('messages')) }}{% endif %}{% endblock %}
{% block label %}{% if is_link is defined and is_link %}{{ icon|default|raw }}{% endif %}{% if options.allow_safe_labels and item.extra('safe_label', false) %}{{ item.label|raw }}{% else %}{{ item.label|trans({}, translation_domain|default('messages')) }}{% endif %}{% endblock %}
2 changes: 1 addition & 1 deletion Resources/views/standard_layout.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ file that was distributed with this source code.
{% block side_bar_before_nav %} {% endblock %}
{% block side_bar_nav %}
{% if app.security.token and is_granted('ROLE_SONATA_ADMIN') %}
{{ knp_menu_render(sonata_knp_menu_build(app.request), {'template' : admin_pool.getTemplate('knp_menu_template')}) }}
{{ knp_menu_render('sonata_admin_sidebar', {template: admin_pool.getTemplate('knp_menu_template')}) }}
{% endif %}
{% endblock side_bar_nav %}
{% block side_bar_after_nav %}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -394,8 +394,8 @@ private function getContainer()
->register('knp_menu.factory')
->setClass('Knp\Menu\Silex\RouterAwareFactory');
$container
->register('knp_menu.helper')
->setClass('Knp\Menu\Twig\Helper');
->register('knp_menu.menu_provider')
->setClass('Knp\Menu\Provider\MenuProviderInterface');
$container
->register('event_dispatcher')
->setClass('Symfony\Component\EventDispatcher\EventDispatcherInterface');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -356,8 +356,11 @@ private function getContainer()
->register('validator')
->setClass('Symfony\Component\Validator\ValidatorInterface');
$container
->register('knp_menu.helper')
->setClass('Knp\Menu\Twig\Helper');
->register('knp_menu.factory')
->setClass('Knp\Menu\FactoryInterface');
$container
->register('knp_menu.menu_provider')
->setClass('Knp\Menu\Provider\MenuProviderInterface');

// Add admin definition's
$container
Expand Down
Loading

0 comments on commit 3f6874f

Please sign in to comment.