Skip to content

Instantly share code, notes, and snippets.

@nateevans
Created April 3, 2014 16:56
Show Gist options
  • Select an option

  • Save nateevans/9958390 to your computer and use it in GitHub Desktop.

Select an option

Save nateevans/9958390 to your computer and use it in GitHub Desktop.

Revisions

  1. nateevans created this gist Apr 3, 2014.
    45 changes: 45 additions & 0 deletions MenuBuilder.php
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,45 @@
    <?php

    namespace Acme\HelloBundle\Menu;

    use Knp\Menu\FactoryInterface;
    use Symfony\Component\DependencyInjection\ContainerAware;

    class MenuBuilder extends ContainerAware
    {
    public function mainMenu(FactoryInterface $factory, array $options)
    {
    $menu = $factory->createItem('root');
    $menu->setChildrenAttribute('class', 'nav navbar-nav');

    $menu->addChild('Projects', array('route' => 'acme_hello_projects'))
    ->setAttribute('icon', 'fa fa-list');

    $menu->addChild('Employees', array('route' => 'acme_hello_employees'))
    ->setAttribute('icon', 'fa fa-group');

    return $menu;
    }

    public function userMenu(FactoryInterface $factory, array $options)
    {
    $menu = $factory->createItem('root');
    $menu->setChildrenAttribute('class', 'nav navbar-nav navbar-right');

    /*
    You probably want to show user specific information such as the username here. That's possible! Use any of the below methods to do this.
    if($this->container->get('security.context')->isGranted(array('ROLE_ADMIN', 'ROLE_USER'))) {} // Check if the visitor has any authenticated roles
    $username = $this->container->get('security.context')->getToken()->getUser()->getUsername(); // Get username of the current logged in user
    */
    $menu->addChild('User', array('label' => 'Hi visitor'))
    ->setAttribute('dropdown', true)
    ->setAttribute('icon', 'fa fa-user');

    $menu['User']->addChild('Edit profile', array('route' => 'acme_hello_profile'))
    ->setAttribute('icon', 'fa fa-edit');

    return $menu;
    }
    }
    31 changes: 31 additions & 0 deletions RequestVoter.php
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,31 @@
    <?php

    namespace Acme\HelloBundle\Menu;

    use Knp\Menu\ItemInterface;
    use Knp\Menu\Matcher\Voter\VoterInterface;
    use Symfony\Component\DependencyInjection\ContainerInterface;

    class RequestVoter implements VoterInterface
    {

    private $container;

    public function __construct(ContainerInterface $container)
    {
    $this->container = $container;
    }

    public function matchItem(ItemInterface $item)
    {
    if ($item->getUri() === $this->container->get('request')->getRequestUri()) {
    // URL's completely match
    return true;
    } else if($item->getUri() !== $this->container->get('request')->getBaseUrl().'/' && (substr($this->container->get('request')->getRequestUri(), 0, strlen($item->getUri())) === $item->getUri())) {
    // URL isn't just "/" and the first part of the URL match
    return true;
    }
    return null;
    }

    }
    39 changes: 39 additions & 0 deletions index.html.twig
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,39 @@
    {% extends '::base.html.twig' %}

    {% block title %}My Awesome Page!{% endblock %}

    {% block stylesheets %}
    <link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css">
    <link rel="stylesheet" href="//netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.css">
    {% endblock %}

    {% block body %}
    <div class="navbar navbar-default navbar-fixed-top">
    <div class="container-fluid">
    {# Brand and toggle get grouped for better mobile display #}
    <div class="navbar-header">
    <button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#admin-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="#">Awesome!</a>
    </div>

    {# Collect the nav links, forms, and other content for toggling #}
    <div class="collapse navbar-collapse" id="admin-navbar-collapse-1">
    {{ knp_menu_render('AcmeHelloBundle:MenuBuilder:mainMenu', {'currentClass': 'active', 'template': 'AcmeHelloBundle:Menu:knp_menu.html.twig'}) }}
    {{ knp_menu_render('AcmeHelloBundle:MenuBuilder:userMenu', {'currentClass': 'active', 'template': 'AcmeHelloBundle:Menu:knp_menu.html.twig'}) }}
    </div>
    </div>
    </div>
    <div class="content" style="margin-top: 60px;">
    Content goes here!
    </div>
    {% endblock %}

    {% block javascripts %}
    <script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.1/jquery.min.js"></script>
    <script type="text/javascript" src="//netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js"></script>
    {% endblock %}
    108 changes: 108 additions & 0 deletions knp_menu.html.twig
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,108 @@
    {% 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 %}
    7 changes: 7 additions & 0 deletions services.yml
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,7 @@
    services:
    acme.hello.menu.voter.request:
    class: Acme\HelloBundle\Menu\RequestVoter
    arguments:
    - @service_container
    tags:
    - { name: knp_menu.voter }