Creating The Login Form

In this video we're going to get started implementing a basic login form. Rather than worry about HTML or styling we're going to use Bootstrap’s sign in form template as the starting point.

This is a really simple process, we just need to do a little copy / paste.

I'm going to copy the form, and a little CSS from here, and create a new Twig template to place all of this inside:

<!-- /app/Resources/views/security/login.html.twig -->

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

{% block body %}

    <form class="form-signin" action="{{ path('login') }}" method="POST">

        <h2 class="form-signin-heading">Please sign in</h2>

        <label for="_username" class="sr-only">Username</label>
        <input type="text"

        <label for="_password" class="sr-only">Password</label>
        <input type="password"

        <button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button>

{% endblock %}

There's nothing particularly special about this template just yet. I've made a few small changes to the field id properties, but mostly it's copy / paste from the Bootstrap template.

You can, of course, swap this out for any other template you like, whether that be Zurb Foundation, or some other framework, or your own custom implementation. It's really not the important part of this video.

At this stage all we have is a Twig template that contains a HTML representation of the forms that we wish to display for log in.

We should also tidy up our existing HTML at this point, and extract the CSS out to its own file.

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

<!DOCTYPE html>
        <meta charset="UTF-8" />
        <title>{% block title %}Welcome!{% endblock %}</title>
        <!-- Latest compiled and minified CSS -->
        <link rel="stylesheet"
        <link rel="stylesheet" href="{{ asset('css/styles.css') }}" />
        {% block stylesheets %}{% endblock %}
        <link rel="icon" type="image/x-icon" href="{{ asset('favicon.ico') }}" />
        <div class="container">

            {% include('flash-messages.html.twig') %}

            {% block body %}{% endblock %}


        {% block javascripts %}{% endblock %}


Note here in inclusion of the line:

<link rel="stylesheet" href="{{ asset('css/styles.css') }}" />

We can use the provided asset function to make our application that little bit more portable. Another benefit of the asset function is cache busting on site update.

By passing in the path of css/styles.css, this implies that our actual stylesheet file should be at /web/css/styles.css.

We will therefore also need to create this standalone CSS file:

# /web/css/styles.css

.container {
    margin-top: 40px;
.form-signin {
    margin: 0 auto;
    max-width: 400px;

To be able to use this form we're going to need a new controller. Let's create one now:


// /src/AppBundle/Controller/SecurityController.php

namespace AppBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;

class SecurityController extends Controller
     * @Route("/login", name="login")
    public function loginAction()
        return $this->render('security/login.html.twig');

Ok, so the good news is that there's nothing here that we haven't seen before already in this series.

And because of the way that our project is set up, we should now be able to hit the /login route and see our new form in our browser. If you're unsure on any of this, please do watch the previous videos in this series where we cover controller actions and routing in further depth.

If we try and submit this form, however, then we might end up a little disappointed. We're not quite done just yet.

One thing that we learned about already is that if we are not passing in an explicit action as part of our call to $this->render, then implicitly, our Login form will be trying to POST back to this route. However, as we are not using the Symfony-provided form Twig function to render our form, we really ought to be more explicit here. We will get to this in the next video.

We won't be directly handling the login process. Instead, we are going to tell Symfony to watch for POST requests to our /login route and intercept them. Behind the scenes, the security component will handle all the complexities of ensuring our credentials are valid.

At this stage we have a simple, 'nicely' styled login form displaying. In the next video we will turn this into a useful, usable login form. It's easier than you might think.

Code For This Course

Get the code for this course.