FOSUserBundle and the Bootstrap Navbar


Bootstrap is probably the most over widely used front end CSS framework on the web today. I use it here at Code Review Videos, and it does a great job of making light work of an otherwise pain-in-the-backside task.

With this in mind, I'm going to show you how to incorporate your new FOSUserBundle template content with some Bootstrap template stuff, in the form of the Bootstrap Navbar.

By the end of this video you will have learned how to create a Bootstrap Navbar that shows your FOSUserBundle logged in username, and a login or log out link, depending on your current status - that is, depending on whether you are logged in, or logged out :)

Of course, there are other front end frameworks available. Zurb's Foundation framework is really nice, although if you are a beginner then you will find more help and support, especially in terms of already answered questions, by using Bootstrap.

Creating a Navbar

We are going to get started by copying and pasting directly from the Bootstrap Starter Template.

The section we are interested in is:

    <nav class="navbar navbar-inverse navbar-fixed-top">
      <div class="container">
        <div class="navbar-header">
          <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
            <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="#">Project name</a>
        </div>
        <div id="navbar" class="collapse navbar-collapse">
          <ul class="nav navbar-nav">
            <li class="active"><a href="#">Home</a></li>
            <li><a href="#about">About</a></li>
            <li><a href="#contact">Contact</a></li>
          </ul>
        </div><!--/.nav-collapse -->
      </div>
    </nav>

Rather than paste this into our ::base.html.twig template, I am going to advise you put this into its own template file:

app/Resources/views/topnav.html.twig

There are a few reasons for doing this, but the biggest benefit for me is that I like to keep everything separated and as small as possible. That way, when I want to change the topnav / navbar, I can open up a dedicated file and see only the HTML related to that one specific thing.

You will also need to copy in the appropriate CSS from the Bootstrap template to stop the unwanted situation whereby your navbar overlays on top of your existing page content. You will more than likely want the navbar to sit inline with the page - explained better in the video (around the 2 minute mark).

I would advise you use a proper front end build system rather than rely on Assetic. I have covered this in the series How To Use Gulp. There are other systems - Grunt and similar.

However, for the purposes of this demo, I will add in a custom stylesheet:

/* web/css/my-style.css */
body {
  padding-top: 50px;
}
.starter-template { /* rename this bit to whatever your content div is using */
  padding: 40px 15px;
  text-align: center;
}

If unsure on that .starter-template bit, look at this link on line 56 which hopefully makes it a little clearer. If not, ask a question in the comments section below.

Then add this new stylesheet in:

<!-- /app/Resources/views/base.html.twig -->
<head>
    ...
    {% block stylesheets %}{% endblock %}
    {% stylesheets 
        "css/my-style.css"
    %}
        <link rel="stylesheets" href="{{ asset_url }}"
    {% endstylesheets %}
    .. 
</head>

This should be enough to get a basic Bootstrap navbar working.

Twig Tip

One cool tip when working with Twig is to use empty blocks to remove things from your sub pages.

We could put our topnav template inside a block in our ::base.html.twig template:

<!-- /app/Resources/views/base.html.twig -->
    .. 
</head>

<body>

{% block topnav %}
  {% include '::topnav.html.twig' %}
{% endblock %}

<div class="container">
    {% block content %}{% endblock %}
</div>

  ..

Then, when we are in a page that we don't want the topnav on, we could do something like:

<!-- /app/Resources/views/Subpage/some-template.html.twig -->

{% extends '::base.html.twig' %}

{% block topnav %}{% endblock %}

{% block content %}
  <p>Our content here</p>
{% endblock %}

Notice, by providing the topnav block without any content, this will override the topnav template content, and the end result will be that the navbar is removed for this page only.

Pretty handy.

FOSUserBundle-izing Things

So far, so Bootstrap.

But this is a course on FOSUserBundle, so let's bring this back on home.

What we want is to have our FOSUserBundle template 'stuff' - the 'Log In' link, the 'Logged in as {userName}' text, and so on, available to us and displaying as we might expect.

I'm not going to cover exactly why this doesn't work out of the box - that's already been covered in the video, so if unsure, then do watch it (this part starts at the 3:22 mark).

First, we need to pull the entire if block out of our overridden FOSUserBundle layout.html.twig:

<!-- app/Resources/FOSUserBundle/views/layout.html.twig -->

    <div>
        {% if is_granted("IS_AUTHENTICATED_REMEMBERED") %}
            {{ 'layout.logged_in_as'|trans({'%username%': app.user.username}, 'FOSUserBundle') }} |
            <a href="{{ path('fos_user_security_logout') }}">
                {{ 'layout.logout'|trans({}, 'FOSUserBundle') }}
            </a>
        {% else %}
            <a href="{{ path('fos_user_security_login') }}">{{ 'layout.login'|trans({}, 'FOSUserBundle') }}</a>
        {% endif %}
    </div>

This is the block we need to customise, wrapping the links in the Bootstrap specific tags / classes to style this properly:

<!-- app/Resources/FOSUserBundle/views/topnav.html.twig -->

<nav class="navbar navbar-inverse navbar-fixed-top">
    <div class="container">
        <div class="navbar-header">
            <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
                <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="#">FOSUserBundle Example</a>
        </div>
        <div id="navbar" class="collapse navbar-collapse">
            <ul class="nav navbar-nav navbar-right">
                {% if is_granted("IS_AUTHENTICATED_REMEMBERED") %}
                    <p class="navbar-text">
                    {{ 'layout.logged_in_as'|trans({'%username%': app.user.username}, 'FOSUserBundle') }}
                    </p>
                    <li>
                        <a href="{{ path('fos_user_security_logout') }}">
                            {{ 'layout.logout'|trans({}, 'FOSUserBundle') }}
                        </a>
                    </li>
                {% else %}
                    <li>
                        <a href="{{ path('fos_user_security_login') }}">{{ 'layout.login'|trans({}, 'FOSUserBundle') }}</a>
                    </li>
                {% endif %}
            </ul>
        </div><!--/.nav-collapse -->
    </div>
</nav>

Now you should have a topnav section that contains the FOSUserBundle bits, styled in a Bootstrap navbar.

This same technique would work for a Foundation navbar, or any other type of navbar. Use the right tags, and the right styles. The FOSUserBundle bits don't need to change.

Code For This Course

Get the code for this course.

Episodes