Skip to content

Instantly share code, notes, and snippets.

@matthbull
Last active February 12, 2017 08:22
Show Gist options
  • Save matthbull/7871bdb565ea33222dbeae3aa553776a to your computer and use it in GitHub Desktop.
Save matthbull/7871bdb565ea33222dbeae3aa553776a to your computer and use it in GitHub Desktop.
KnpMenuBundle - override knp_menu.html.twig to use bootstrap 3 and fontawesome.

Updated:

  • Twig 2
  • Symfony 3
  • Nav headers
  • corrections in spaceless shorttags
  • some extra logged in/out functionality

Thanks to Nate Evans's original extension of Niels Mouthaan's work. https://gist.github.com/nateevans/9958390

# add this to your main config file to globally use the template located in app/Resources/views/menu/menu.html.twig
knp_menu:
twig:
template: :menu:menu_template.html.twig
{% block nav %}
<div class="navbar navbar-default navbar-fixed-top">
<div class="container-fluid">
<div class="row">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar-collapse-1" aria-expanded="false">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">Brand</a>
</div>
<div class="collapse navbar-collapse" id="navbar-collapse-1">
{# only load main menu if logged in #}
{% if is_granted('IS_FULLY_AUTHENTICATED') %}
{{ knp_menu_render('AppBundle:MenuBuilder:mainMenu', {currentClass: 'active'}) }}
{% endif %}
{{ knp_menu_render('AppBundle:MenuBuilder:userMenu', {currentClass: 'active'}) }}
</div>
</div>
</div>
</div>
{% endblock %}
{%- extends 'knp_menu.html.twig' -%}
{%- macro attributes(attributes) -%}
{%- for name, value in attributes -%}
{%- if value is not none and value is not same as(false) and name != 'icon' -%}
{{- ' %s="%s"'|format(name, value is same as(true) ? name|e : value|e)|raw -}}
{%- endif -%}
{%- endfor -%}
{%- endmacro -%}
{%- block compressed_root -%}
{%- spaceless -%}
{{ block('root') }}
{%- endspaceless -%}
{%- endblock -%}
{%- block root -%}
{%- set listAttributes = item.childrenAttributes -%}
{{ block('list') -}}
{%- endblock -%}
{%- block list -%}
{%- if item.hasChildren and options.depth is not same as(0) and item.displayChildren -%}
{%- import _self as knp_menu -%}
<ul{{ knp_menu.attributes(listAttributes) }}>
{{ block('children') }}
</ul>
{%- endif -%}
{%- endblock -%}
{%- block children -%}
{# save current variables #}
{%- set currentOptions = options -%}
{%- set currentItem = item -%}
{# update the depth for children #}
{%- if options.depth is not none -%}
{%- set options = options|merge({'depth': currentOptions.depth - 1}) -%}
{%- endif -%}
{# update the matchingDepth for children #}
{%- if options.matchingDepth is not none and options.matchingDepth > 0 -%}
{%- set options = options|merge({'matchingDepth': currentOptions.matchingDepth - 1}) -%}
{%- endif -%}
{%- for item in currentItem.children -%}
{{ block('item') }}
{%- endfor -%}
{# restore current variables #}
{%- set item = currentItem -%}
{%- set options = currentOptions -%}
{%- endblock -%}
{%- block item -%}
{%- import _self as knp_menu -%}
{%- if item.displayed -%}
{%- set attributes = item.attributes -%}
{%- set is_dropdown = attributes.dropdown|default(false) -%}
{%- set is_header = attributes.header|default(false) -%}
{%- set divider_prepend = attributes.divider_prepend|default(false) -%}
{%- set divider_append = attributes.divider_append|default(false) -%}
{# unset bootstrap specific attributes #}
{%- set attributes = attributes|merge({'dropdown': null, 'divider_prepend': null, 'divider_append': null, 'header': null }) -%}
{%- if divider_prepend -%}
{{ block('dividerElement') }}
{%- endif -%}
{# building the class of the item #}
{%- 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 -%}
{# building the class of the children #}
{%- set childrenClasses = item.childrenAttribute('class') is not empty ? [item.childrenAttribute('class')] : [] -%}
{%- set childrenClasses = childrenClasses|merge(['menu_level_' ~ item.level]) -%}
{# adding classes for dropdown #}
{%- if is_dropdown -%}
{%- set classes = classes|merge(['dropdown']) -%}
{%- set childrenClasses = childrenClasses|merge(['dropdown-menu']) -%}
{%- endif -%}
{# putting classes together #}
{%- if classes is not empty -%}
{%- set attributes = attributes|merge({'class': classes|join(' ')}) -%}
{%- endif -%}
{%- set listAttributes = item.childrenAttributes|merge({'class': childrenClasses|join(' ') }) -%}
{# displaying the item #}
{% if not divider_prepend and not divider_append %}
<li{{ knp_menu.attributes(attributes) }}>
{%- if is_dropdown -%}
{{ block('dropdownElement') }}
{%- elseif item.uri is not empty and (not item.current or options.currentAsLink) -%}
{{ block('linkElement') }}
{%- elseif is_header -%}
{{ block('headerElement') }}
{%- else -%}
{{ block('spanElement') }}
{%- endif -%}
{# render the list of children#}
{{ block('list') }}
</li>
{% endif %}
{%- if divider_append -%}
{{ block('dividerElement') }}
{%- endif -%}
{%- endif -%}
{%- endblock -%}
{# elements #}
{%- block linkElement -%}
{%- import _self as knp_menu -%}
<a href="{{ item.uri }}"{{ knp_menu.attributes(item.linkAttributes) }}>
{%- if item.attribute('icon') is not empty -%}
<i class="{{ item.attribute('icon') }}" aria-hidden="true"></i>
{%- endif -%}
{{ block('label') }}
</a>
{%- endblock -%}
{%- block dividerElement -%}
{%- if item.level == 1 -%}
<li class="divider-vertical"></li>
{%- else -%}
<li class="divider"></li>
{%- endif -%}
{%- endblock -%}
{%- block headerElement -%}
<li class="dropdown-header">{{ block('label') }}</li>
{%- endblock -%}
{%- block spanElement -%}
{%- import _self as knp_menu -%}
<span>{{ knp_menu.attributes(item.labelAttributes) }}>
{%- if item.attribute('icon') is not empty -%}
<i class="{{ item.attribute('icon') }}" aria-hidden="true"></i>
{%- endif -%}
{{ block('label') }}
</span>
{%- endblock -%}
{%- block dropdownElement -%}
{%- import _self as knp_menu -%}
{%- set classes = item.linkAttribute('class') is not empty ? [item.linkAttribute('class')] : [] -%}
{%- set classes = classes|merge(['dropdown-toggle']) -%}
{%- set attributes = item.linkAttributes -%}
{%- set attributes = attributes|merge({'class': classes|join(' ')}) -%}
{%- set attributes = attributes|merge({'data-toggle': 'dropdown'}) -%}
<a href="#"{{ knp_menu.attributes(attributes) }}>
{%- if item.attribute('icon') is not empty -%}
<i class="{{ item.attribute('icon') }}" aria-hidden="true"></i>
{%- endif -%}
{{ block('label') }}
<b class="caret"></b>
</a>
{%- endblock -%}
<?php
namespace AppBundle\Menu;
use Knp\Menu\FactoryInterface;
use Symfony\Component\DependencyInjection\Container;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerAwareTrait;
/**
* Class MenuBuilder
*
* @Author Matt Holbrook-Bull <matt@ampisoft.com>
* @package AppBundle\Menu
*/
class MenuBuilder implements ContainerAwareInterface
{
use ContainerAwareTrait;
/** @var Container */
protected $container;
/**
* just to allow easy setup, all the routes are 'homepage', change them as you like
*/
/**
* @param FactoryInterface $factory
* @param array $options
* @return \Knp\Menu\ItemInterface
*/
public function mainMenu( FactoryInterface $factory, array $options )
{
$menu = $factory->createItem( 'root' );
$menu->setChildrenAttribute( 'class', 'nav navbar-nav' );
$menu->addChild( 'Bananas', [ 'route' => 'homepage' ] )
->setAttribute( 'icon', 'fa fa-list' );
$menu->addChild( 'Strawberries', [ 'route' => 'homepage' ] )
->setAttribute( 'icon', 'fa fa-group' );
return $menu;
}
/**
* @param FactoryInterface $factory
* @param array $options
* @return \Knp\Menu\ItemInterface
* @throws \InvalidArgumentException
*/
public function userMenu( FactoryInterface $factory, array $options )
{
$token = $this->container->get( 'security.token_storage' )
->getToken();
$auth = $this->container->get( 'security.authorization_checker' );
$menu = $factory->createItem( 'root' );
$menu->setChildrenAttribute( 'class', 'nav navbar-nav navbar-right' );
if ( $auth->isGranted( 'IS_AUTHENTICATED_FULLY' ) ) {
$username = $token->getUser()
->getUsername(); // Get username of the current logged in user
} else {
$username = 'guest';
}
$menu->addChild( 'User', [
'label' => $username,
] )
->setAttribute( 'dropdown', true );
if ( $auth->isGranted( 'IS_AUTHENTICATED_FULLY' ) ) {
$menu[ 'User' ]->addChild( 'Edit profile', [
'label' => 'Edit profile',
'route' => 'homepage',
] )
->setAttribute( 'icon', 'fa fa-edit' );
$menu[ 'User' ]->addChild( 'Messages', [
'label' => 'Messages',
'route' => 'homepage',
] )
->setAttribute( 'icon', 'fa fa-envelope-o' );
$menu->addChild( 'Logout', [
'label' => 'Logout',
'route' => 'homepage',
] )
->setAttribute( 'icon', 'fa fa-sign-out' );
} else {
$menu['User']->addChild('header', ['label' => 'section1'])
->setAttribute('header', true);
$menu['User']->addChild('Register', [
'label' => 'Register',
'route' => 'homepage'
]);
}
return $menu;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment