People often refer to bundles as modules or re-usable code for Symfony applications. When a developer has experience with Symfony1 or another framework with the module concept, it might seem logical that this is what a bundle represents in Symfony.
So what is a bundle? When do you need one and what can it do? What's the difference between an AppBundle and a vendor Bundle?
With the release of Symfony 2.0, it was often presumed useful to split code logically in bundles. One could for example build a UserBundle, InvoicingBundle, ContractBundle, LoginBundle and so on. This however, creates a spaghetti of dependencies and this was not necessarily in the code.
Twig is the main templating engine used in Symfony. So when using this bundle setup, templates were usually located in
src/SomeBundle/Resources/views/ or a similar structure. Moreover, it's quite common to use template inheritance. This
means that instead of including blocks of templates everywhere, you extend a base template. The question is, where do
you put this base template? Often in a website, you have similar layouts for all page, this means they share a common
parent, often called
layout.html.twig. Either you put this in your application directly:
in a bundle that's shared between them.
This already creates a tight coupling between the bundles (and application), but is further strengthened by service definitions and routes; for example my Invoice links to a Contract, this means that my InvoicingBundle has a hard dependency on the ContractBundle as the routes are now known.
Introducing the AppBundle solved the dependency problem. This means that instead of splitting bundles by
domain, the AppBundle contains the categorisation by domain and recommends resources to be added to
instead of having them in the bundle. This solved the problem of having too many bundles in your application, but what
about vendor bundles?
But What is a Bundle Exactly?
When looking back to what they are used for, we can determine that it seems to be pretty much an application. It has our Controllers, Entities, Commands and probably a bunch of classes related to domain logic. So what is a bundle?
A bundle is literally nothing more than an extension point. All the features listed above are determined based on the bundle. A bundle contains at least a bundle class and allows things like:
@Bundlenotation to alias everything referring to a bundle resource
- Automatically finding entities when using the DoctrineBundle
- Automatically registering commands
Core Features of a Bundle
As mentioned, the bundle provides an extension point. Other bundles for example, can hook in on your bundle because it contains some logic to expose information such as the directory of the bundle. This means that you can easily write a bundle that scans all available bundles for an Entity directory and try to register entities found inside, for example the DoctrineBundle.
The Vendor Bundles
The main purpose of a bundle however, is to provide an extension point for the Dependency Injection Container. When talking about this extension point, it revolves around adding, changing or removing service definitions. Often when you have a library and you want to register certain classes as service, you write a bundle for it, simply to register them in your kernel without having to do this in your AppBundle. Now we're talking about a re-usable vendor bundle. You can compare a bundle to glue. You literally glue a library or component into your symfony application.
Often you want those services to be configured; for example a connection of some sorts often requires a host, username and password. You don't want to rely on parameters that might or might not have be configured in the config.yml file, so you want to actually add a Configuration tree which will force the right values to be defined the way you want it to. This configuration will be processed by an Extension class and here you can modify the container.
Note that the Extension class only knows about your configuration and scopes the container, that means you can't modify services defined outside of your bundle. This brings us another feature: Compiler Passes. This feature can register compiler passes, which can be used to manipulate the container in specific build phases by a form of priority. A compiler pass runs on a more complete container and runs after the Extension initialization of the bundles.
As I have explained, there's a difference between an application bundle and a re-usable vendor bundle. Do you need multiple bundles in your application directly? Most likely not. You're better off writing an AppBundle to prevent a spaghetti of dependencies. You can simply follow the best practices and it will work fine.
When building re-usable bundles, make them infrastructural. Elnur has written a blog post about infrastructural bundles.
Infrastructural bundles are the ones that affect the infrastructure of your app but not its domain and database schema.
At the moment a bundle in your application is more of a convenience. It provides a few easy tricks to reduce the amount of knowledge to get a project started:
- Entity registration
- Command registration
- What ever other bundles automatically register
In theory you don't need an AppBundle: