First Experiences with Symfony 4 & the Symfony Community

First Experiences with Symfony 4 & the Symfony Community

Recently, I decided to learn the basics of the Symfony (4) framework, so that I could better understand one of my client’s applications, and provide better support to it. I never expected to use such a well-rounded framework. Nor did I expect to encounter such an engaged and supportive community. Here’s the story.

Why I Got Started With Symfony

To add a bit more background to this story, I’m involved in documentation and security at ownCloud, based out of my adopted hometown of Nuremberg. One of the apps that they have is written in a combination of Symfony and Vue.js.

I’ve intermittently had to create GitHub issues for the app, for a variety of things that needed fixing, implementing, or correcting. You know, the usual fare when you’re working with software.

Well, I was fine with this state of affairs, of just writing these up and assign them to the right person. However, as time went by, it became increasingly annoying. This is because, as a software developer of about 15 - 20 years, I found it frustrating not to be directly involved.

I wanted to be involved, to write code, to hack the project. The trouble was, I’d never used either Vue.js or Symfony. Given that, it’d likely take me longer to figure out what to do, than to just pass the ticket on. Well, that was the logic at first.

Also, to be fair, it also made sense, because I wasn’t directly tasked with working on the project, so it was out of scope my contract. To start getting involved might see me neglecting what I’d been hired to do.

For some, this would be perfectly fine, but it just didn’t sit well with me. This niggling annoyance persisted until early this week; I had the realisation that I did have grounds to learn the software better.

Why? Because part of my role is security. If I don’t have a solid grasp of the software that I’m responsible for helping secure, how can I do my job to the best of my abilities?

I pondered on it for some time (perhaps 30 mins) and felt that this mindset was logical and sensible. I decided to set half a day aside to get up to speed with the fundamentals of the core technologies behind the app, those being Vue.js and Symfony.

After writing a simple set of apps in Vue.js, I then turned to Symfony. Oh, the rabbit hole I didn’t know I was going down.

How I Got Started With Symfony

We all have different approaches to learning, mine is the Kinaesthetic (or hands-on) style. I can read and absorb some concepts through reading, but for them to stick I have to actively apply them. So, while reading through the Symfony documentation, I decided to create a somewhat simple, if rather basic, application.

No, I wasn’t going to learn everything there is to know about Symfony in an hour or two, but I was going to learn:

  • The basics of what’s on offer.
  • How to bootstrap a project.
  • How to perform basic configuration.
  • How the file and directory structure is laid out.
  • Common conventions; and
  • Some of the project’s idioms.

I started by getting an overview of the documentation — the first place you should go to learn about a software project (or service)! I feel that the theme could be optimised, just that bit more, for readability. That said, I was pleasantly surprised to find that the project’s documentation was:

  • Comprehensive in its coverage.
  • Easy to read.
  • Targeted at people aiming to use it.
  • Has an excellent taxonomy, and is
  • Written by people who use the project.

You can see from the documentation’s home page that while they provide comprehensive documentation, they also have a “Getting Started guide”, that is front-and-center, logically ordered, and easy to get to.

Creating the Core Application

So, I started with Chapter 1. Setup. I won’t go into intimate detail, instead skip to the essentials.

I first created a new project, using Composer, using the following command:

composer create-project symfony/website-skeleton hello-symfony

You can see an summary from the console output below as well, very informative!

Bootstrapping a Symfony app

If you’re not that familiar with Composer, this will create a new project, called “hello-symfony”, from the symfony/website-skeleton project. Effectively, it bootstraps a new Symfony application, ready for you to customise and build upon as you need to.

Following that, I cd’d to the project directory and installed the project’s dependencies, by running composer require server --dev, and then I started PHP’s built-in web server by running php bin/console server:run. With those two steps completed, I then navigated to the running project at http://localhost:8000/. Up until then, all was well.

Now I’d hit my first issue.

No default route in the Symfony application.

As you can see, while the core of the project was bootstrapped, no routes were available; so an exception was thrown when I attempted to dispatch one. This was a little unexpected, given that the documentation said that it should “just work”.

But, not to worry. This was an excellent opportunity to see better just how good the docs were. I went back to the documentation’s home page, and navigated to Chapter 4. Controllers.

After having a bit of a read through, it turned out to be quite easy to create a controller. Even better, it’s just as simple to map a route to that controller, once it’s created.

To skip along a bit, here’s the controller class that I ended up creating:


namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;

class HomeController extends Controller
    public function index()
        return $this->render('default/default.html.twig', [
            'name' => 'Matthew'

You can see that it’s a basic PHP class, which extends Symfony\Bundle\FrameworkBundle\Controller\Controller. It has one method, index, which renders a template and sets it as the body of the response to the request.

And here’s the routes configuration, which I added to config/routes.yaml, which enabled it:

    path: /
    controller: App\Controller\HomeController::index

You can see that it specifies the path which can be requested, /, and the controller which will be dispatched to upon requesting that route, App\Controller\HomeController::index. Note: I’m not delving into too much detail about either controllers or routes in this post. However, I encourage you to explore them further.

A Quick Look at Templates

Now it’d be unfair or me to gloss over templates if you’re just getting started, like I am. So here’s a quick overview of them. As you might be expecting, templates in Symfony, by default, aren’t that sophisticated — which isn’t to downplay the power that they have.

Here’s an example of my first template:

{% verbatim %}{# templates/default/default.html.twig #}
{% extends 'base.html.twig' %}

{% block body %}
<h1>Hello World (Symfony)</h1>
<p>Hi {{ name }}.</p>
{% endblock %}{% endverbatim %}

Most of this might make sense at first glance. However, there are a few things that might trip you up. So let’s step through it line-by-line. Line one is just a file comment, which was generated by default as part of the original Composer create-project command. Line two indicates that this template builds on another, called base.html.twig. The documentation sums it up best, so here’s what it has to say:

More often than not, templates in a project share common elements, like the header, footer, sidebar or more. In Symfony, this problem is thought about differently: a template can be decorated by another one. This works the same as PHP classes: template inheritance allows you to build a base “layout” template that contains all the common elements of your site defined as blocks (think “PHP class with base methods”). A child template can extend the base layout and override any of its blocks (think “PHP subclass that overrides certain methods of its parent class”).

This makes life far more efficient, as I didn’t have to redeclare anything in my template, that the base template contained. I only needed to build on it. I did that by including the content in my template, within {% verbatim %}{% block body %}{% endverbatim %} and {% verbatim %}{% endblock %}{% endverbatim %}. The only remaining line to cover is <p>Hi {{ name }}.</p>. This outputs the value of the template variable, called name, which was set to ‘Matthew’. A good name, no?

Easier Routing with Annotations

Getting back to the journey; that’s where it got even more interesting! It turns out that Symfony’s extraordinarily flexible and customisable.

I could create a route in the project’s core routing configuration file, config/routes.yaml which I mentioned just before, or I could use some class annotations instead. How? It takes just two additional lines, which you can see below. These are the Annotation\Route use statement, and the @Route annotation in the method’s docblock.


namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\Routing\Annotation\Route;

class HomeController extends Controller
     * @Route("/")
    public function index()
        return $this->render('default/default.html.twig', [
            'name' => 'Matthew'

For what it’s worth: if you’re taking over an existing project, I can see how this flexibility might also be a downside. Why? There’s no central location to store your routes configuration. Given that, developers may have to look in multiple places to find out what routes are set and what handles requests to them.

Or so I thought!

Introducing bin/console

While reading further through the routing documentation, I began to learn more about the console script. At first blush, it seems to be an amazingly well put together tool for helping you find out anything you want to know about your project.

Among other things, it lets you perform a host of administrative tasks, such as linting templates, clearing, and warming caches, running unit tests, and performing database migrations. To get a complete picture, run php bin/console and have a read through the commands and their short descriptions.

Here’s a short summary:

Symfony 4.0.6 (kernel: src, env: dev, debug: true)

  command [options] [arguments]

  -h, --help            Display this help message
  -q, --quiet           Do not output any message
  -V, --version         Display this application version
      --ansi            Force ANSI output
      --no-ansi         Disable ANSI output
  -n, --no-interaction  Do not ask any interactive question
  -e, --env=ENV         The Environment name. [default: "dev"]
      --no-debug        Switches off debug mode.
  -v|vv|vvv, --verbose  Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug

Available commands:
  about                                   Displays information about the current project
  help                                    Displays help for a command
  list                                    Lists commands
  assets:install                          Installs bundles web assets under a public directory
  cache:clear                             Clears the cache
  cache:pool:clear                        Clears cache pools
  cache:pool:prune                        Prune cache pools
  cache:warmup                            Warms up an empty cache
  config:dump-reference                   Dumps the default configuration for an extension
  debug:autowiring                        Lists classes/interfaces you can use for autowiring
  debug:config                            Dumps the current configuration for an extension
  debug:container                         Displays current services for an application
  debug:event-dispatcher                  Displays configured listeners for an application
  debug:form                              Displays form type information
  debug:router                            Displays current routes for an application
  debug:swiftmailer                       Displays current mailers for an application
  debug:translation                       Displays translation messages information
  debug:twig                              Shows a list of twig functions, filters, globals and tests
  lint:twig                               Lints a template and outputs encountered errors
  lint:xliff                              Lints a XLIFF file and outputs encountered errors
  lint:yaml                               Lints a file and outputs encountered errors
  router:match                            Helps debug routes by simulating a path info match
  server:log                              Starts a log server that displays logs in real time
  server:run                              Runs a local web server
  server:start                            Starts a local web server in the background
  server:status                           Outputs the status of the local web server for the given address
  server:stop                             Stops the local web server that was started with the server:start command

While perusing the default output, I saw the debug:router command. It seemed like an excellent complement to my concerns about non-centrally located routing configurations. And so it was. By running it, I saw an detailed table of all the current routes in the application, which you can see below.

 -------------------------- -------- -------- ------ -----------------------------------
  Name                       Method   Scheme   Host   Path
 -------------------------- -------- -------- ------ -----------------------------------
  app_home_index             ANY      ANY      ANY    /
  _twig_error_test           ANY      ANY      ANY    /_error/{code}.{_format}
  _wdt                       ANY      ANY      ANY    /_wdt/{token}
  _profiler_home             ANY      ANY      ANY    /_profiler/
  _profiler_search           ANY      ANY      ANY    /_profiler/search
  _profiler_search_bar       ANY      ANY      ANY    /_profiler/search_bar
  _profiler_phpinfo          ANY      ANY      ANY    /_profiler/phpinfo
  _profiler_search_results   ANY      ANY      ANY    /_profiler/{token}/search/results
  _profiler_open_file        ANY      ANY      ANY    /_profiler/open
  _profiler                  ANY      ANY      ANY    /_profiler/{token}
  _profiler_router           ANY      ANY      ANY    /_profiler/{token}/router
  _profiler_exception        ANY      ANY      ANY    /_profiler/{token}/exception
  _profiler_exception_css    ANY      ANY      ANY    /_profiler/{token}/exception.css
  logout                     ANY      ANY      ANY    /logout
 -------------------------- -------- -------- ------ -----------------------------------

Not only did it list the routes that I’d created, but it also showed me the method, scheme, and host that the route supported, along with a whole host of other routes — that I never knew about! Now, do you get why I used the term “rabbit hole” earlier?! The more I looked, the more I found. And the more I found, the more I wanted to learn.

So from there, I opened up the _profiler_home route and was gobsmacked! Without me having to do anything, here was a host of profile information about the routes that I’d requested so far.

By clicking on a route, I could get a host of information about it, such as the method, HTTP status, remote IP address, the request and response details, server parameters — and more. What a goldmine of information for debugging!

Like a little kid in a candy store, I started clicking on the left-hand side navigation links, learning all about what was in store. I looked at performance metrics, log messages, routing details, cache hits and misses, security information, and more. Everywhere I turned, there was more to see and do.

Honestly, if it weren’t for the fact that I have two (wonderful) kids, and a sense of discipline that this was not my main contract role, I’d likely have torn through the entire documentation, clicked every link, and run every command that bin/console has, regardless of how much time I was spending. It’s just so much fun as someone new to the project not to want to get lost in it.

What’s more, I’ve worked with other frameworks, across a variety of languages, and very few of them have ever offered such comprehensive functionality out of the box.

Anyway, I could go on and on at length about just how impressed I was with my experience. But I don’t think that that would serve anything other than to fan egos and to nerd out purely because I want to.

That said, I’m more than happy to give a conference or user group talk, where I do go into greater depth about my experience if any conference or user group organisers will have me! hint, hint.

Imagine My Surprise at Community Support and Engagement

What I do want to go into, however, is my sheer surprise at the level and depth of community support and engagement. This is because it’s important. Why?

Well, it doesn’t matter what the price point of a service or product is; it doesn’t matter how shiny and slick the marketing, advertising, and promotion is; it doesn’t matter how many promises and claims are made; if something goes wrong or if you just need a bit of help, you have to know that you can get it.

Now I didn’t need any, as I was able to address my own issues rather quickly. So you may be wondering just how I’m so confident about it being there. Have a look at the tweet below

When I began writing this, it had 32 retweets and 97 likes; as you can see, it’s now up to 53 retweets and 157 likes. I’d be stoked to see it hit 200 likes; perhaps, however, that’s my ego talking.

Anyway, if you look further into the metrics, it’s had 18,726 impressions with a 706 total engagements. Now, you’d be correct in asserting that both retweets and likes aren’t necessarily assurances of either quality or support.

But I’m neither a celebrity nor someone with any traction in the Symfony community. Given that, if I tweet something, I don’t expect that much engagement with it. What’s more, anyone can have a tweet that gains a bit of notoriety and superficial engagement for a short time, say half a day, or a few hours.

However, this tweet has been continuing to gain engagement for the last four days. I’d have thought it would just be a quick flash in the pan, rather like when I wrote a comparison post about Zend Expressive and Laravel. But it’s continued to be so passionate, with only a minor dip in interest.

What’s more, people have been chipping in, sharing further details about what Symfony has to offer, what I should look at next, the current conferences, as well as taking the time to welcome me to the community.

They’ve also told me how they left and came back; how working with Symfony’s changed their professional careers; and shared with me some of the key new features that I should get stuck into.

If they’re not a clear sign that it’s a great place to be, then I’m not sure what is. What’s more, if that’s not a clear sign of there being support if and when I need it, I don’t know what is.

I’ve worked with other communities, and while supportive, they’re nowhere near this. What’s also heartening about the engagement I’ve received so far, is that it doesn’t appear fanatical. It appears to be a good balance of enthusiasm, passion, and commitment.

That’s a Wrap

While it’s still very early days in my journey with Symfony — I don’t know exactly how far or where I’ll go with it — I’m feeling very rewarded to have begun engaging with a passionate and enthusiastic community.

I’m very heartened to know that as I take tentative steps back into software development, that I’ve had such a positive experience, one that’s left me with a positive idea about where to turn.

As time permits, I’m keen to continue learning more about Symfony, such as DataTransformers, tagged services, and compiler passes, which @Webjobsbiz recommended.

If you’re a long-time Symfony user, what else should I learn? Where should I turn my attention to next? If I was to look for a paid project using Symfony, where do you suggest I look?

If you’re a newcomer to Symfony, what were your experiences like? I’d love to know if our experiences, both with the framework and with the community match up.

The Symfony logo is copyright © and a trademark of SensioLabs.

You might also be interested in these tutorials too...

Becoming a Polyglot Developer (again)
Mon, Oct 10, 2016

Becoming a Polyglot Developer (again)

I’ve been developing software in PHP for a long time. But I recently decided to return to my polyglot roots, developing in multiple languages on a regular basis. The question is, which language, or languages, are the right ones to learn and use.

Why I Created the Free the Geek Website By Hand
Sun, Nov 8, 2015

Why I Created the Free the Geek Website By Hand

With so many existing solutions for creating websites, why would you create one by hand? This post looks at the reasons why I chose to create the site by hand, versus using an established platform, such as Sculpin or Wordpress

Want more tutorials like this?

If so, enter your email address in the field below and click subscribe.

You can unsubscribe at any time by clicking the link in the footer of the emails you'll receive. Here's my privacy policy, if you'd like to know more. I use Mailchimp to send emails. You can learn more about their privacy practices here.

Join the discussion

comments powered by Disqus