Skip to content

Instantly share code, notes, and snippets.

@woutsanders
Last active February 15, 2020 08:46
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save woutsanders/79b3551f2c4f07ddaab3c74fa9aba132 to your computer and use it in GitHub Desktop.
Save woutsanders/79b3551f2c4f07ddaab3c74fa9aba132 to your computer and use it in GitHub Desktop.
Knp Menu Bundle with the Metronic Admin Theme 4 sidebar menu http://bit.ly/2bfd2JS Couldn't have done it without this earlier, successful, attempt for Twitter Bootstrap: https://gist.github.com/nateevans/9958390. Rendered example: http://imgur.com/a/9LfZq If you are going to use this, make sure you have a valid license for the template!
<div class="page-sidebar navbar-collapse collapse">
<!-- BEGIN SIDEBAR MENU -->
{{ knp_menu_render('main', {
'currentClass': 'active open',
'ancestorClass': 'active open'
}) }}
<!-- END SIDEBAR MENU -->
</div>
# KnpMenuBundle configuration parameters.
knp_menu:
# use "twig: false" to disable the Twig extension and the TwigRenderer
twig:
# Use custom template by default.
template: :Menu:knp_menu.html.twig
# if true, enables the helper for PHP templates
templating: false
# the renderer to use, list is also available by default
default_renderer: twig
{#
Metronic Admin Theme 4 sidebar menu template.
Many thanks to Niels Mouthaan for a version for generic twitter bootstrap
Thanks to Nate Evans for posting Niels' solution on GitHub.
Thanks to KnpMenuBundle team for their documentation.
URL: https://gist.github.com/nateevans/9958390
Niels' version altered by W. Sanders to make it compatible with Metronic template.
#}
{% extends 'knp_menu.html.twig' %}
{% block item %}
{# Make some functionality of bundle available within this block. #}
{% import "knp_menu.html.twig" as macros %}
{% if item.displayed %}
{# Fetch all attributes of the item. #}
{%- set attributes = item.attributes %}
{# Determine if item is a dropdown one with a sub menu. #}
{%- set is_dropdown = item.attribute('dropdown')|default(false) %}
{# Determine if item is a small, intermediate, heading to separate multiple menu sections. #}
{%- set is_heading = item.attribute('heading')|default(false) %}
{# Unset the checkmarks we've used above. #}
{%- set attributes = attributes|merge({'dropdown': null, 'heading': null }) %}
{# Set custom classes and/or automatic ones from the bundle (like first and last) #}
{%- set classes = item.attribute('class') is not empty ? [item.attribute('class')] : [] %}
{%- if matcher.isCurrent(item) %}
{%- set classes = classes|merge([options.currentClass]) %}
{%- elseif matcher.isAncestor(item, options.depth) %}
{%- set classes = classes|merge([options.ancestorClass]) %}
{%- endif %}
{%- if item.actsLikeFirst %}
{%- set classes = classes|merge([options.firstClass]) %}
{%- endif %}
{%- if item.actsLikeLast %}
{%- set classes = classes|merge([options.lastClass]) %}
{%- endif %}
{# Build classes of the child <ul></ul> element #}
{%- set childrenClasses = item.childrenAttribute('class') is not empty ? [item.childrenAttribute('class')] : [] %}
{%- set childrenClasses = childrenClasses|merge(['menu_level_' ~ item.level, 'sub-menu']) %}
{# Add one (or more) specific class(es) for correctly rendering a dropdown menu. #}
{%- if is_dropdown %}
{%- set classes = classes|merge(['nav-item']) %}
{%- endif %}
{# Add one (or more) specific class(es) for correctly rendering an intermediate menu heading. #}
{%- if is_heading %}
{%- set classes = classes|merge(['heading']) %}
{% endif %}
{# Melt all the classes together. #}
{%- if classes is not empty %}
{%- set attributes = attributes|merge({'class': classes|join(' ')}) %}
{%- endif %}
{%- set listAttributes = item.childrenAttributes|merge({'class': childrenClasses|join(' ') }) %}
{# Put the item on display. #}
<li{{ macros.attributes(attributes) }}>
{%- if is_dropdown %}
{# Render dropdown item. #}
{{ block('dropdownElement') }}
{%- elseif item.uri is not empty and (not item.current or options.currentAsLink) %}
{# Render <a></a> link item. #}
{{ block('linkElement') }}
{%- elseif is_heading is not empty %}
{# Render an intermediate heading for specifying sections. #}
{{ block('smallHeading') }}
{%- else %}
{# Render a piece of text (no link). #}
{{ block('spanElement') }}
{%- endif %}
{# Render the list of children #}
{{ block('list') }}
</li>
{% endif %}
{% endblock %}
{# Renders an item with a clickable link. #}
{% block linkElement %}
<a href="{{ item.uri }}"{{ macros.attributes(item.linkAttributes) }}>
{% if item.attribute('icon') is not empty %}
<i class="{{ item.attribute('icon') }}"></i>
{% endif %}
<span class="title">{{ block('label') }}</span>
</a>
{% endblock %}
{# Renders an item with text only (no link). #}
{% block spanElement %}
<span {{ macros.attributes(item.labelAttributes) }}>
{% if item.attribute('icon') is not empty %}
<i class="{{ item.attribute('icon') }}"></i>
{% endif %}
<span class="title">{{ block('label') }}</span>
</span>
{% endblock %}
{# Renders the dropdown item (which has children). #}
{% block dropdownElement %}
{# Set a break condition to exit the loop ASAP. #}
{%- set break = 0 %}
{# Set a variable which will determine the position of the arrow icon. #}
{%- set unfoldedIcon = false %}
{# Loop as long as the break is 0. #}
{% for attrGroup in attributes if break == 0 %}
{# If we've found the open class... #}
{% if 'open' in attrGroup %}
{%- set unfoldedIcon = true %}
{%- set break = 1 %}
{% endif %}
{% endfor %}
{# Add custom classes as perhaps defined within the built menu item. #}
{%- set classes = item.linkAttribute('class') is not empty ? [item.linkAttribute('class')] : [] %}
{# Make sure these two mandatory classes are molded in there as well. #}
{%- set classes = classes|merge(['nav-link','nav-toggle']) %}
{# Reset the attributes variable to the current item's link attributes (<a> tag). #}
{%- set attributes = item.linkAttributes %}
{# Mold all the classes in between the other attributes (separated by a space). #}
{%- set attributes = attributes|merge({'class': classes|join(' ')}) %}
{# Now, build the damn thing! #}
<a href="javascript:;"{{ macros.attributes(attributes) }}>
{% if item.attribute('icon') is not empty %}
<i class="{{ item.attribute('icon') }}"></i>
{% endif %}
<span class="title">{{ block('label') }}</span>
<span class="arrow nav-toggle {% if unfoldedIcon %}open{% endif %}"></span>
</a>
{% endblock %}
{# Renders the contents of the small menu sub-heading. #}
{% block smallHeading %}
<h3 class="uppercase">{{ block('label') }}</h3>
{% endblock %}
{# Enable translation on menu items. #}
{% block label %}{{ item.label|trans }}{% endblock %}
<?php
namespace AppBundle\Menu;
use Knp\Menu\FactoryInterface;
/* Please note, this is a test with made up items */
class MenuBuilder {
private $factory;
/**
* @param FactoryInterface $factory
*
* Add any other dependency you need
*/
public function __construct(FactoryInterface $factory) {
$this->factory = $factory;
}
/**
* Builds the main menu.
*
* @param array $options
*
* @return \Knp\Menu\ItemInterface The complete menu.
*/
public function createMainMenu(array $options) {
// Create root <ul> node and add a few attributes to that.
$menu = $this->factory->createItem('root', array(
'childrenAttributes' => array(
'class' => 'page-sidebar-menu',
'data-keep-expanded' => 'false',
'data-auto-scroll' => 'false',
'data-slide-speed' => '100',
),
));
$menu->addChild('Dashboard', array(
'route' => 'app_dashboard',
'attributes' => array(
'icon' => 'icon-home',
)
));
$menu->addChild('Demo heading', array(
'attributes' => array(
'heading' => true,
),
));
// create another menu item
$menu->addChild('About Me', array(
'attributes' => array(
'dropdown' => true,
'icon' => 'icon-user',
),
));
// you can also add sub level's to your menu's as follows
$menu['About Me']->addChild('Edit profile', array(
'uri' => 'http://edit.me',
'attributes' => [
'icon' => 'fa fa-car',
'dropdown' => true,
]
));
$menu['About Me']->addChild('Set Password', array(
'uri' => 'http://pass.me',
));
# $menu['About Me']['Edit profile']->setCurrent(true);
# $menu['Dashboard']->setCurrent(false);
$menu['About Me']['Edit profile']->addChild('Stairs 2', [
'attributes' => [
'dropdown' => true,
],
]);
// access services from the container!
//$em = $this->container->get('doctrine')->getManager();
// findMostRecent and Blog are just imaginary examples
//$blog = $em->getRepository('AppBundle:Blog')->findMostRecent();
//$menu->addChild('Latest Blog Post', array(
// 'route' => 'blog_show',
// 'routeParameters' => array('id' => $blog->getId())
//));
// ... add more children
return $menu;
}
}
<?xml version="1.0" ?>
<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="app.menu_builder" class="AppBundle\Menu\MenuBuilder">
<tag name="knp_menu.menu_builder" method="createMainMenu" alias="main"/>
<argument type="service" id="knp_menu.factory"/>
</service>
</services>
</container>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment