In this post we’re going to build a Symfony full-stack app without Symfony Standard.
Would you do this every day? Probably not, but it’s a great way to learn a bit more about Symfony.
End Goals
The end goal here is to have an application that will send a simple Hello World message. So we’re going to cover the core framework stuff, but save things like templating, database access, ORMs, and forms for later. The goal here to see how to scaffold a Symfony app to better understand why symfony standard does what it does and where to deviate. We’ll end up with an app that uses the Symfony 3 directory structure.
The Smallest Composer.json
At the time of writing, Symfony 2.7 is on its second beta. We’ll be using that here. 2.7 is a LTS release and the rest of this tutorial should be relevant for some time to come.
Our composer.json
is pretty tiny:
The Application Kernel
Symfony apps all start their life by creating an new kernel object and then passing it around or handling HTTP requests with it. This class is usually called AppKernel
which resides in the app
directory at the root of your project and extends Kernel from the HttpKernel
component.
There’s two methods that we have to implement on the kernel: registerBundles
and registerContainerConfiguration
. We’ll talk about both of those further down but leave them empty for now. We’re also going to do some customization here and point to var/log
and var/cache
for our log and cache directories.
Handling Web Requests
Now let’s setup our front controller — a single PHP file that will handle all web requests that don’t go to static assets.
This is actually pretty simple: require the autoloader, create an instance of AppKernel
, create a request from the super globals, then handle the request and send the created response.
Now let’s run the dev server with php -S localhost:8000 -t web/
and see what happens!
Fatal error: Uncaught exception ‘Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException’ with message ‘You have requested a non-existent service “http_kernel”.’
Well! Things are broken but we’re close.
What’s in a Bundle?
Think of a bundle as a distinct unit of functionality that can be be plugged into a Symfony kernel. A bundle begins its life as an implementation of BundleInterface and a special directory structure (that can be overridden of course). Bundles are loaded in the registerBundles
method in AppKernel
seen above. From there a bundle can load services into the container, add controllers and console commands, and do just about anything else.
What I think is the coolest part about symfony is that there is no real core. There are components that may depend on each other, but Symfony uses its own bundle system to glue together those components into the Symfony full stack framework.
A big chunk of that glue is FrameworkBundle. It contains all the services necessary to actually handle web requests, include the missing http_kernel
service in the error message above.
Let’s add it and try our web request again.
If you fire up the dev server again you’ll going to get another error message about a missing service.
Stepping into Configuration
The error from above is because we have not done any configuration yet. This is where the registerContainerConfiguration
comes in. It takes a configuration loader as its argument and you use that to load up configuration from Yaml, XML, or PHP files. We’ll use Yaml here.
When you create an instance of AppKernel
, you do so with an environment. In terms of the Symfony components and bundles, the environment does very little. You as a user have the choice of using it. In fact, there’s already a bit of its use in getCacheDir
above.
Configuration is another popular spot to use environments. Generally folks will have a config_{environment}.yml
for each environment used. Those files import the main config.yml
file. All configuration resides in app/config
by convention. We’re only going to use one environment here, dev, so we’ll only create that file and a main configuration. We’re also going to create an empty (for now) routing.yml
file.
Our main configuration is pretty simple. Notice the stuff wrapped in percentage signs. Those are parameters and will be replaced with values a parameters file or the environment. I’m a big fan putting config in the environment rather than using a parameters file.
The dev configuration imports the main config and declares a parameters
section to make our development a bit easier (no environment variables). This works for the purposes of this example, but some things are better off in parameter files for development (like a set of database creds).
Now we need to load our configuration in AppKernel
.
Now when we run the dev server we can exciting error:
Symfony\Component\HttpKernel\Exception\NotFoundHttpException: No route found for “GET /”
We’re close! All that’s left is to add some routes.
AppBundle
AppBundle is the container for all of your application’s integration with Symfony. It’s a place where you put controllers, forms, etc. Anything that’s not core business logic goes in AppBundle.
We’re going to autoload our AppBundle from src
and register it in AppKernel by creating a class that extends Bundle and instantiating it in registerBundles
.
Hello, World
We are finally ready to add a controller in the AppBundle.
To register the controller with our router, we’ll edit the empty routing.yml
file from above.
Finally we can spin up the dev server and see a beautiful Hello, World.
Code for This Tutorial
All the code for this tutorial can be found in this GitHub repo. The commits are in the order of the tutorial so you can step through things and watch the application grow.