Loïc Faugeron Technical Blog

Learn Symfony2 - part 3: Bundles 02/07/2014

Deprecated: This series has been re-written - see The Ultimate Developer Guide to Symfony

This is the third article of the series on learning the Symfony2 framework. Have a look at the two first ones:

In the previous articles we began to create an empty application with the following files:

.
├── app
│   ├── AppKernel.php
│   ├── cache
│   │   └── .gitkeep
│   ├── config
│   │   └── config.yml
│   └── logs
│       └── .gitkeep
├── composer.json
├── composer.lock
├── .gitignore
└── web
    └── app.php

Running composer install should create a vendor directory, which we ignored with git.

Here's the repository where you can find the actual code.

We'll now see what a bundle is.

Creating the application bundle

We'll need some use case in order for our code snippets to make sense. So here it is: the Knights Who Say 'Ni', demand a webservice! It shall say 'ni' if the user do not appease it. To do so, the user should post a shrubbery!

Let's create our application bundle, in order to have a place where we can put our code. To do so we need to create the directory:

mkdir -p src/Knight/ApplicationBundle

Then the class extending Symfony\Component\HttpKernel\Bundle\Bundle:

<?php
// File: src/Knight/ApplicationBundle/KnightApplicationBundle.php

namespace Knight\ApplicationBundle;

use Symfony\Component\HttpKernel\Bundle\Bundle;

class KnightApplicationBundle extends Bundle
{
}

Finally we register the bundle into our application:

<?php
// File: app/AppKernel.php

use Symfony\Component\HttpKernel\Kernel;
use Symfony\Component\Config\Loader\LoaderInterface;

class AppKernel extends Kernel
{
    public function registerBundles()
    {
        return array(
            new Symfony\Bundle\FrameworkBundle\FrameworkBundle(),
            new Knight\ApplicationBundle\KnightApplicationBundle(), // <-- Here!
        );
    }

    public function registerContainerConfiguration(LoaderInterface $loader)
    {
        $loader->load(__DIR__.'/config/config.yml');
    }
}

Let's commit our work:

git add -A
git commit -m 'Created Application bundle'

Bundles allow you to extend the application's DIC

The KnightApplicationBundle class extends the following one:

<?php

namespace Symfony\Component\HttpKernel\Bundle;

use Symfony\Component\DependencyInjection\ContainerAware;
use Symfony\Component\Console\Application;

abstract class Bundle extends ContainerAware implements BundleInterface
{
    public function getContainerExtension();
    public function registerCommands(Application $application);
}

Note: Only the part we're interested in is shown here.

Those two methods make the bundle capable of autodiscovering its commands and its Dependency Injection Container's (DIC) extension, if the following directory directory structure is used:

.
├── Command
│   └── *Command.php
├── DependencyInjection
│   └── KnightApplicationExtension.php
└── KnightApplicationBundle.php

Note: the only file required in a bundle is the KnightApplicationBundle.php one.

The name of a bundle (in our example KnightApplication) is composed of:

For your own sake, choose a small one-word name for you vendor name and for your bundle name (there's no hard rules but that's my advice).

The KnightApplicationExtension class allows you to manipulate the DIC (more often you'll load a configuration file which can be located in Resources/config/services.xml).

And that's precisely the purpose of bundles: registering services in the application's DIC.

Side note about DIC and services

Services and Dependency Injection isn't in the scope of this series. However if you want to discover what it is all about, have a look at these two articles:

Note: this is a kindly reminder about the nature of Symfony2 Components. Those are third party libraries which can be used on their own outside of the framework.

Side note about commands

The Symfony2 Console Component allows you to create CLI applications. This application can have one or many commands. To learn more about them, have a look at this article:

Note: commands aren't in the scope of this article, but they're worth mentioning.

Two kinds of bundles

There's two kinds of bundle:

Let's take the KnpLabs snappy library: it allows you to generate a PDF from a HTML page and can be used in any applications (non-symfony ones, and even framework-less ones).

The class allowing this generation is Knp\Bundle\SnappyBundle\Snappy\LoggableGenerator: its construction is a bit tiresome. To fix this, we can define its construction inside the DIC and fortunately there's already a bundle doing it for us: KnpSnappyBundle.

That's a good example of the first kind of bundles.

Now about the second kind: in our Symfony2 application, we'll need to integrate our own code to it, one day or another. We could go the long and painful way (writing a lot of boilerplate code and configurations), or we could use a bundle to do automatically the job for us!

Sometimes, we'll find applications which have many bundles in order to categorize them into modules. This isn't necessary and it's a bit tiresome if you ask me: we can simply create folders in a unique bundle to categorize our modules.

The creation of many bundles necessitates some extra manual steps. It also makes little sense as a bundle is supposed to be a decoupled unit: if we create a UserBundle, FrontendBundle, BlogBundle and ForumBundle, we'll find ourselves with bundles depending on one another, often with cyclic dependencies and we'll waste time wondering where to put new classes (which can rely on 3 bundles).

My advice: create a single bundle for your application. If later on you find that inside it you created a set of classes which makes sense in other projects (Symfony2 and non-Symfon2 ones alike), then maybe you can extract them to create a third party library. And then you might create a bundle to integrate it inside Symfony2 applications.

Conclusion

Bundles are a way to extend the Dependency Injection Container: they're the glue layer between your code and Symfony2 applications.

They follow conventions which aren't hard coded (you can override anything), allowing them to autodiscover some convenient classes.

Thanks for reading, in the next article, we'll create controllers!

Resources

Here's a good article about how reusable bundles should be created:

You don't like the conventions and you're ready to write a lot of boilerplate code and configuration? Here you go (I'd not advise you to do so, though):

I'm only putting these links because I like how they explain how Symfony2 works behind the hood, but I wouldn't apply them in a real world application as it makes too much fuss to no avail (that's my humble opinion anyway).