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
Instantly share code, notes, and snippets.
Updated:
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 <[email protected]> | |
| * @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; | |
| } | |
| } |