Symfony2 Tutorial: The first static Page – Routing, Templates, Twig

This is the third part of the series Symfony2 Tutorial for Beginners which will describe the process of creating a static page.

We will create a controller that will define a route as well as the action for this route. Furthermore we will create a basic template in twig and the impressum page itself.

Creating the Route and the Controller

The first thing one has to do when adding a new page to a website is to define how the user can reach it and what part of the code should be executed in order to show it. Symfony2 names this process of mapping a url with a controller method routing.

Yaml, Xml and Annotation to define a Route in Symfony2

In Symfony2 there are three ways to define a new route: yaml, xml and annotation in the controller. The advantage of yaml is, that all defined routes can be seen in one place. Annotations on the other hand are defining the route right where it will be needed. The syntax of Xml does not rely on whitespace, but it is needlessly verbose and thereby unreadable, so I would not recommend using it. You have to decide which one you prefer, or you might want to mix them and choose one depending on the situation.

I will use annotations in this tutorial but touch on how it would look in yaml now and again.

Example of Annotation to define a Route and the Controller

So lets get right on with creating an impressum page (a page containing infos on the person legally responsible for the website): Create a controller:

// src/YourIdentifier/YourBundle/Controller/PageController.php
namespace YourIdentifier\YourBundle\Controller;

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

class PageController extends Controller {

     * @Route("/impressum", name="page_impressum")
     * @Template()

    public function impressumAction()
        return $this->render('YourIdentifierYourBundle:Page\static:impressum.html.twig');

If you are unsure what namespace, class, or extends means, you should go read up on php namespace and oop in php.

We are defining the url by which a user may reach the impressum with the @Route annotation. You can choose the path and name freely. The path is the first argument and it is necessary. The name argument is optional, although it is nice to have to call the route later on.

Note that the route must be activated in the configurations (see Dans comment below). So for every class, create an entry in your routing.yml file like this:

 resource: "@YourIdentifierYourBundle/Controller/PageController.php"
 type: annotation

Example of Yaml

The yaml alternative to the above annotation is:

page_impressum:                 # the identifier
    pattern:  /impressum        # the route
    defaults: { _controller:  YourIdentifierYourBundle:Page:impressum }  # the controller + action
        _method:  GET           # restraints

As can be seen, it is pretty similar to the annotation method, except the definition of the controller which is used. In the example, YourIdentifierYourBundle defines the bundle in which to look, Page stands for PageController.php and impressum stands for the impressumAction method.

Notes on the Controller example

Note that the function name itself must be post-fixed with the string “Action”.

The executed action itself is rather uninteresting: We are rendering a static page (we are not passing any arguments to the twig template). The template we are rendering is /src/YourIdentifier/YourBundle/Resources/views/Page/static/impressum.html.twig .

If you are interested in Routing and what else is possible with it, you might want to read the Symfony2 chapter on Routing.

Creating the View

Creating a base template

As some parts of the website will be the same on almost all pages, we will first define a base twig template which will be included by most of the pages we will create later.

<!-- /src/YourIdentifier/YourBundle/Resources/views/base.html.twig -->
<!DOCTYPE html>
<html xmlns="" dir="ltr" lang="en-US">
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
        <title>{% block title %}Default for variable title part {% endblock %} - constant title part</title>
        <!--[if lt IE 9]>
           <script src=""></script>
        {% block stylesheets %}
        {% stylesheets filter='yui_css' 'css/main.css' %}
        <link rel="stylesheet" href="{{ asset_url }}" type="text/css" media="screen" />
        {% endstylesheets %}
        {% endblock %}
        <link rel="shortcut icon" href="{{ asset('favicon.ico') }}" />
        <div id="wrap">
        <header id="header">
            <h1>Page Name</h1>
            {% block navigation %}
                <ul class="navigation">
                    <li><a href="{{ path('page_impressum') }}">Impressum</a></li>                    
            {% endblock %}

        <section class="main">
            {% block body %}{% endblock %}
        <aside class="sidebar-right">

            {% block sidebarright %}Right Sidebar{% endblock %}

        <aside class="sidebar-left">
            {% block sidebarleft %}{% endblock %}

        <div id="footer">
            {% block footer %}
            Maybe a link to your privacy settings, a link to Symfony2, whatever.
            {% endblock %}
        {% block javascripts %}{% endblock %}
I am not too good with css or design, but for those interested here is the css file so far:

    Document   : main
    Created on : Jun 23, 2012, 8:46:13 PM
    Author     : developer
        Styling of the main site, including positioning of main conten, sidebar and footer,
        general font-styling and styling of nav-bar.

/* style text for whole site*/
html {

font-family: Arial, Helvetica, Sans-serif;
color: #666666;

/* site width and center */
#wrap {
margin:0 auto;

/* style links for whole site*/
#wrap a {
    text-decoration: none;

/* positioning of main, sidebar and footer */
.main {
line-height: 18px;

.sidebar-right {
line-height: 18px;

#footer {

/* position navigation*/
#header .navigation ul {
#header .navigation li {

/* style naviagion */
#header .navigation { border-bottom: 1px solid #ccc; border-top: 1px solid #ccc; }
#header .navigation li a { display: inline-block; padding: 10px 15px; border-left: 1px solid #ccc; color: black}

Here you have your basic parts of a website. The head – containing title, stylesheets and the like – and the body – containing a header, a left and a right sidebar, a footer and the main content.

The parts of it inside { and } are twig-code. Generally, {% means “do something”, {{ means print something and {# is for comments.

In twig, a block may be defined in the base template and it may contain a default value there. Later, it can be overridden in a template that extends the base template.

It can be seen that we are already using the route we defined in the controller above to link in the navigation to the impressum we are creating. This is done by calling the path function with the identifying name we used in the controller (in this case “page_impressum”).

In the example above, I used Assetic for the management of the css files. I will not go into details about it, but you can read about in the symfony cookbook: How to Use Assetic for Asset Management. Assetic is quite useful and I would suggest using it, but you can always use the standard way of importing css as well by using:

<!-- /src/YourIdentifier/YourBundle/Resources/views/base.html.twig -->
{% block stylesheets %}
    <link href="{{ asset('css/main.css') }}" type="text/css" rel="stylesheet" />
{% endblock %}
instead of the stylesheets block used above. In that case, the main.css file must be located at /web/css/main.css

If you are using Assetic and placed your css files in src/YourIdentifier/YourBundle/Resources/public/css/ they must be included by calling
php app/console assetic:dump

Thoughts on Twig and alternatives

Twig is alright in most contexts, but you may choose to use PHP instead of Twig for Templates. The only real downside is that you have to remember to escape the output yourself as php does not protect against code open to xss, while twig does. In this tutorial I will continue to use twig as I find it goes nicer together with html than php does (but it is somewhat limiting out of the box).

Creating the Impressum Page

<!-- /src/YourIdentifier/YourBundle/Resources/views/Page/static/impressum.html.twig -->
{% extends 'YourBundle::base.html.twig' %}

{% block title %}
{% endblock %}

{% block body %}
<p>Responcible for this website (§ 5 TMG, § 55 RfStV):</p>
<p>Fake Fakerson</p>
<p>123 Fakestreet</p>
<p>Fakestown - USA</p>
{% endblock %}

Not much going on here: The title- and body-blocks are overwritten with the impressum content.

If you now visit http://localhost/your_project/web/app_dev.php/impressum you should see the impressum as well as the navigation (which at this point only contains a – working – link to the impressum).

That is it for the third part of this Symfony2 Tutorial. In the following parts of this series some more possibilities of twig and the controller will be introduced.

More on Twig can also be found on the official Symfony2 website.

The next part of this tutorial is: User Submissions – Controller, Forms, E-Mails
Overview over all parts: Symfony2 Tutorial for Beginners

21 thoughts on “Symfony2 Tutorial: The first static Page – Routing, Templates, Twig

  1. Hey there,

    nice Tutorial, but this Chapter doesn’t really work. You’ve got some mistakes in your Code.

    Chance this:

                    <a href="{{ path('page_home') }}" rel="nofollow">Home</a>
                    <a href="{{ path('page_impressum') }}" rel="nofollow">Impressum</a>

    into this

                   <a href="#" rel="nofollow">Home</a>
                   <a href="{{ path('page_impressum') }}" rel="nofollow">Impressum</a>

    In the ../views/Page/static/impressum.html.twig

    {% extends 'CowproxyMainBundle::base.html.twig' %}     is wrong
    {% extends 'YourBundle::base.html.twig' %}                     is right

    This {% block stylesheets %} – block-Element doesn’t work. Furthermore, you’ve forgotten about the css file. Where is it located? And the asset function {{ asset(‘favicon.ico’) }} and {{ asset_url }}, what does it mean?

    And somehow Symfony doesn’t like the name body in here, choose another name for the block
    {% block body %}{% endblock %}

    After those changes, the tutorial works on my system.


  2. Hey Sarah, thanks for your detailed comment.

    You are right, I forgot to write the page_home route in this tutorial so it will not work. I just removed it as it is not really necessary anyways. I also updated the impressum.html.twig (must have overlooked that one as well).

    I updated the section on the stylesheet block and hope now it is a little bit clearer.

    Thank you for the corrections and I’m glad you got it to run despite the errors in the tutorial.

  3. I’ve run through the tutorial up to this point twice and can’t get it to work. After creating the new controller, the view, and the related CSS, i attempt to view the route, and receive the error: No route found for “GET /impressum”

  4. @developer: i have the same problem with this tutorial as succinct.
    I’m guessing that, since the files for the bundle only reside in the ‘src’ directory, they somehow have to be “processed” to be included in the actual ‘app’ directory ?

  5. Never mind, cause was that I hadn’t first used the ‘generate:bundle’ command to pre-populate the Bundle…

  6. Pingback: Which PHP Framework | strayObject

  7. Nice article, but needs another fix: when implemented as is, it gives me error:

    “Unable to find template “MyBundle::base.html.twig”

    Fixed by changing

    {% extends ‘MyBundle::base.html.twig’ %}


    {% extends ‘::base.html.twig’ %}

    in impressum.html.twig

  8. Another problem:
    for the example to work, base.html.twig must be located in /app/Resources/views – not as shown in the article in /src/YourIdentifier/YourBundle/Resources/views

  9. I have to clarify that the above two problems I pointed to are appearing when Symfony version 2.2.1 is used. There might be no such problems with older Symfony versions.

  10. Didnt work for me:
    ERROR – Uncaught PHP Exception Symfony\Component\HttpKernel\Exception\NotFoundHttpException: “No route found for “GET /impressum”” at C:\xampp\htdocs\symfony-test\app\cache\dev\classes.php line 1871


  11. I found this tutorial very usefull but,
    Even for me some problems….
    I’m using symfony 2.3.2
    and I’ve try to fix the problems like the comments says but no result:

    Unable to find template “mybundle:Page\static:impressum.html.twig”.

    no result even with the moving of the base.html.twig file as suggested by Kris

  12. Very nice tutorial. Thanks.
    You have not explained that for using route annotations in the controller using :
    use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
    you must still ‘activate’ the route with something like the following:

    # app/config/routing.yml

    # import routes from a controller class
        resource: "@YourBundle/Controller/PageController.php"
        type:     annotation

    This would account for people getting “No route found for “GET /impressum”” errors.
    Further details:
    This would account for people getting “No route found for “GET /impressum”

  13. “Unable to find template “Gimba1SymfonyBundle::base.html.twig”

    Hi dear tutorial makers,
    this is a fine tutorial, but I have had the same problem as Kris on 2013/05/23 at 6:54 am UTC said:
    Fixed by changing
    {% extends ‘MyBundle::base.html.twig’ %}
    {% extends ‘::base.html.twig’ %}
    in impressum.html.twig
    Best regards
    Axel Arnold Bangert – Herzogenrath 2014

  14. Hi,
    if I use that {% extends ‘::base.html.twig’ %} then I naturally have no css included, but it works.
    If I use that {% extends ‘gimba1SymfonyBundle::base.html.twig’ %}, I got first the error “Unable to find template “MyBundle::base.html.twig”
    Then I added it in config.yml:
    debug: “%kernel.debug%”
    use_controller: false
    bundles: [gimba1SymfonyBundle ]
    and added the yui_css, which was missing too
    jar: “%kernel.root_dir%/Resources/java/yuicompressor-2.4.7.jar”
    then I get the error:
    CRITICAL – Uncaught PHP Exception Twig_Error_Runtime: “An exception has been thrown during the rendering of a template (“Unable to generate a URL for the named route “_assetic_b735f91_0″ as such route does not exist.”) in “gimba1SymfonyBundle::base.html.twig” at line 10.” at C:\Users\Administrator\Eigene Dateien\NetBeansProjects\gimbasymfony1\app\cache\dev\classes.php line 4460

    There is definitly something wrong and missing .
    Best regrads
    Axel Arnold Bangert – Herzogenrath 2014

  15. Hi,
    if I use css without assetic as described in the tutorial,
    {% block stylesheets %}

    {% endblock %}

    it’s working fine and is formatted equally to the main.css file.
    Best regards
    Axel Arnold Bangert – Herzogenrath 2014

  16. Hi,
    after I added
    and changed the assetic configuration in config.yml to:

    debug: “%kernel.debug%”
    use_controller: false
    bundles: [gimba1SymfonyBundle ]
    #java: /usr/bin/java
    cssrewrite: ~
    jar: “%kernel.root_dir%/Resources/java/compiler.jar”
    jar: “%kernel.root_dir%/Resources/java/yuicompressor-2.4.8.jar”

    it works very fine with assetic too.

  17. Hi,
    what I wonder is, that the command generate:Bundle inserts the entry:
    new gimba1\SymfonyBundle\gimba1SymfonyBundle(),
    in Kernel.php

    but not in:

    Is this perhaps a missing part in the command “generate:Bundle…” (bug?)?
    Best regards
    Axel Arnold Bangert – Herzogenrath 2014

Leave a Reply

Your email address will not be published.