Skip to content

Instantly share code, notes, and snippets.

@FlorianKromer
Last active February 18, 2020 11:18
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save FlorianKromer/def2713587c71002a046 to your computer and use it in GitHub Desktop.
Save FlorianKromer/def2713587c71002a046 to your computer and use it in GitHub Desktop.
Bootstrap navbar with knp-menu-bundle for symfony2

Create a navbar menu with knp menu bundle in symfony 2

Why use a bundle to make a menu?

In order to split the dynamic text and the page static template moreover make a breadcrumb become easier. We can also translate the menu, dupplicate it, etc. without rewrite it many time. DRY <3

HOW?

Follow the steps!

knp-menu-bundle version
"require": {
...
"knplabs/knp-menu-bundle": "~2.0",
...
}
## Add knp-menu as service
cg_main.menu_builder:
class: CG\MainBundle\Menu\MenuBuilder
arguments: ["@knp_menu.factory"]
cg_main.menu.footer:
class: Knp\Menu\MenuItem # the service definition requires setting the class
factory_service: cg_main.menu_builder
factory_method: createFooterMenu
arguments: ["@request"]
scope: request # needed as we have the request as a dependency here
tags:
- { name: knp_menu.menu, alias: footer } # The alias is what is used to retrieve the menu
// Create a menu
<?php
namespace CG\MainBundle\Menu;
use Knp\Menu\FactoryInterface;
use Symfony\Component\DependencyInjection\ContainerAware;
use Symfony\Component\HttpFoundation\Request;
class MenuBuilder
{
private $factory;
/**
* @param FactoryInterface $factory
*/
public function __construct(FactoryInterface $factory)
{
$this->factory = $factory;
}
public function createFooterMenu(Request $request)
{
$menu = $this->factory->createItem('root');
$menu->setChildrenAttributes(array('class' => 'nav navbar-nav navbar-right'));
$menu->addChild('News', array('route' => 'cg_main_news'));
$menu->addChild('Informations')->setAttribute('dropdown', true);
$menu['Informations']->addChild('Services', array('route' => 'cg_main_services'));
$menu['Informations']->addChild('A propos', array('route' => 'cg_main_about'));
$menu->addChild('Mon compte', array('route' => 'cg_main_compte'));
$menu->addChild('Contact', array('route' => 'cg_main_contact'));
return $menu;
}
}
{# Template for navbar with dropdown #}
{% extends 'knp_menu.html.twig' %}
{% block item %}
{% import "knp_menu.html.twig" as macros %}
{% if item.displayed %}
{%- set attributes = item.attributes %}
{%- set is_dropdown = attributes.dropdown|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 }) %}
{%- 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 #}
<li{{ macros.attributes(attributes) }}>
{%- if is_dropdown %}
{{ block('dropdownElement') }}
{%- elseif item.uri is not empty and (not item.current or options.currentAsLink) %}
{{ block('linkElement') }}
{%- else %}
{{ block('spanElement') }}
{%- endif %}
{# render the list of children#}
{{ block('list') }}
</li>
{%- if divider_append %}
{{ block('dividerElement') }}
{%- endif %}
{% endif %}
{% endblock %}
{% block dividerElement %}
{% if item.level == 1 %}
<li class="divider-vertical"></li>
{% else %}
<li class="divider"></li>
{% endif %}
{% endblock %}
{% block linkElement %}
<a href="{{ item.uri }}"{{ macros.attributes(item.linkAttributes) }}>
{% if item.attribute('icon') is not empty %}
<i class="{{ item.attribute('icon') }}"></i>
{% endif %}
{{ block('label') }}
</a>
{% endblock %}
{% block spanElement %}
<span>{{ macros.attributes(item.labelAttributes) }}>
{% if item.attribute('icon') is not empty %}
<i class="{{ item.attribute('icon') }}"></i>
{% endif %}
{{ block('label') }}
</span>
{% endblock %}
{% block dropdownElement %}
{%- 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="#"{{ macros.attributes(attributes) }}>
{% if item.attribute('icon') is not empty %}
<i class="{{ item.attribute('icon') }}"></i>
{% endif %}
{{ block('label') }}
<b class="caret"></b>
</a>
{% endblock %}
{% block label %}{{ item.label|trans }}{% endblock %}
{# A random call in your code #}
{% extends '::base.html.twig' %}
{% block body %}
{% block navbar %}
<nav class="navbar navbar-inverse navbar-fixed-top" role="navigation">
<div class="container">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1">
<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="{{ path("cg_main_index") }}">CGdroid</a>
</div>
<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
{{ knp_menu_render('footer',{'currentClass': 'active','template': 'CGMainBundle:Menu:knp_menu.html.twig'}) }}
</div>
<!-- /.navbar-collapse -->
</div>
<!-- /.container -->
</nav>
{% endblock %}
{% block container %}
{% endblock %}
{% endblock body %}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment