Laravel 4: Where to put bindings
If you make judicious use of IoC containers (and Dependency Injection), you'll likely end up with lots of Bindings, some of which may look like:
App::bind('Anvil', function()
{
return new Acme\Product\AnvilHeavy;
});
or
App::bind('Acme\Product\Anvil\AnvilInterface', 'Acme\Product\Anvil\AnvilHeavy');
Yes, I have lots of bindings!
Me too. Let's figure out what to do with them.
Let's say Acme is an accomplished business, and has quite a few products. Amongst them: Anvils and TNT. You likely have classes for all of these. Since I usually create an application library for business logic, my code structure might look like this:
Acme
app
lib
Acme
Product
Anvil
AnvilInterface.php
AnvilHeavy.php
AnvilLight.php
Tnt
TntInterface.php
TntHighyield.php
TntLowyield.php
commands
config
…etc…
filters.php
routes.php
This means you may have these bindings in your code somewhere. And that's the key - At this stage, very few articles explain where to put that code.
//Somewhere in your code
App::bind('Acme\Product\Anvil\AnvilInterface', 'Acme\Product\Anvil\AnvilHeavy');
App::bind('Tnt', function()
{
return new Acme\Product\TntHighyield;
});
These will work merrily. We can type-hint AnvilInterface in a controller and know it will get AnvilHeavy. Addtionally, we can resolve our Tnt using App::make('Tnt')
anywhere in our application.
As we add products in our contrived example here, we will end up with more and more bindings. These can add up. How can we organize our code to make these less obstrusive? Where is the "home" for these bindings?
A home for your bindings
The answer is to use Service Providers. These nifty classes provide a way to add bindings and use Laravel containers for your libraries.
To use these, I'll create a ServiceProvider for each Acme product:
Acme
app
lib
Acme
Product
Anvil
AnvilInterface.php
AnvilHeavy.php
AnvilLight.php
AnvilServiceProvider.php # New file <--
Tnt
TntInterface.php
TntHighyield.php
TntLowyield.php
TntServiceProvider.php # New file <--
The code to add the bindings for our classes are simple:
// app/lib/Acme/Product/Anvil/AnvilServiceProvider.php
<?php namespace Acme\Product\Anvil;
use Illuminate\Support\ServiceProvider;
class AnvilServiceProvider extends ServiceProvider {
/**
* Register the binding
*
* @return void
*/
public function register()
{
$app = $this->app;
$app->bind('Acme\Product\Anvil\AnvilInterface', 'Acme\Product\Anvil\AnvilHeavy');
}
}
// app/lib/Acme/Product/Tnt/TntServiceProdiver.php
<?php namespace Acme\Product\Tnt;
use Illuminate\Support\ServiceProvider;
class TntServiceProvider extends ServiceProvider {
/**
* Register the binding
*
* @return void
*/
public function register()
{
$app = $this->app;
$app['Tnt'] = function() {
return new TntHighyield;
};
}
}
Now, rather than having App::bind() calls littered somewhere in our code, we have them as Service Providers for our various Acme products.
The last step is to register these service providers so they are run as part of the bootstrap process.
Head to app/config/app.php
. In the 'providers' array, we'll register our Service Providers.
'providers' => array(
'Illuminate\Foundation\Providers\ArtisanServiceProvider',
…and so on…
'Acme\Product\Anvil\AnvilServiceProvider',
'Acme\Product\Tnt\TntServiceProvider',
)
Now, our bindings will be created for us on bootstrap, and we can go our merry way without cluttering up a config or route file with bindings.
Alternatively!
If I didn't want to create a ServiceProvider class for each product in this example, I could instead create one ProductServiceProvider and do all the binding there.You may find this way makes more sense to you:
// app/lib/Acme/Product/ProductServiceProvider.php
<?php namespace Acme\Product;
use Illuminate\Support\ServiceProvider;
class ProductServiceProvider extends ServiceProvider {
/**
* Register Acme Product bindings
*
* @return void
*/
public function register()
{
$app = $this->app;
// Handle Anvil binding
$app->bind('Acme\Product\Anvil\AnvilInterface', 'Acme\Product\Anvil\AnvilHeavy');
// Handle Tnt binding
$app['Tnt'] = function() {
return new TntHighyield;
};
}
}
What have we learned?
We learned that if you use IoC (and hopefully Dependency Injection) in Laravel, you'll likely be using container bindings.
- Judicious use of IoC will mean many
App::bind()
calls - Organize your container bindings into ServiceProvider classes
- You can use many Service Providers, but also consider using one ServiceProvider (perhaps per namespace) to limit the number of classes and (potentially) increase maintainability.