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!
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.
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:
<?php
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:
default:
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.
<?php
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)
Usage:
command [options] [arguments]
Options:
-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
assets:install Installs bundles web assets under a public directory
cache
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
config:dump-reference Dumps the default configuration for an extension
debug
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
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
router:match Helps debug routes by simulating a path info match
server
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.
Join the discussion
comments powered by Disqus