Service Containers in Laravel
The service containers concept in Laravel is one of the most misunderstood parts of the framework. It is rather complex and abstract but for the enlightened, it provides great extensibility to your code and a useful way of managing class dependencies.
Furthermore, the service container facilitates insulation between different classes, which can be advantageous for creating a more stable code.
Dependency Injection
The class dependencies are managed via class injection, which basically means you can tell a method which class you expect, whereafter it will create an instance of it if none is passed.
Technically speaking this is done by type-hinting the expected class name.
The code below shows an example class which loads a dependency via the constructor method. Note how the type-hinting is used to determine the class name for the instance to be created.
namespace App\Handlers\Commands; use App\Commands\OrderCommand; class OrderHandler { /** * Purchase a podcast. * * @param PurchasePodcastCommand $command * @return void */ public function handle(OrderCommand $command) { // Your handler code } }
Information
The service container concept is used all over Laravel, hence a good understanding of this concept is important to work with more complex coding in Laravel.
Example Insulation between Implementations
Managing larger code bases can be a pain when it comes to managing changes and dependencies. Therefore many layers of abstraction are often desired to insulate code parts from each other.
In a simple example, you use a database class for interacting with the database instead of reading and writing directly via the database driver.
That gives you the freedom to change the database driver anytime in the database class, which will automatically affect the entire code base, without any further changes needed.
This simple example illustrates the helpful purpose of this differentiation, however, service containers in Laravel can take this concept to a much broader perspective.
Consider an example where you have an order process which sends confirmation emails. The order handler can be centralized in an own class, to make the order handler reusable across your system.
Furthermore, the order handler makes uses of an external class to send confirmation emails.
/** * The mailer implementation. */ protected $email; /** * Create a new instance. * * @param Email $email * @return void */ public function __construct(Email $email) { $this->email = $email; }
In the example above the order, handler makes use of the email classes send
method directly, which makes the order handler vulnerable to changes in the send method. This is okay if the email class is an internal class you build yourself, but could impose problems if the class is externally maintained and suddenly changes.
Therefore you could consider writing an email class to insert between the order handler and the actual email class you use.
The advantage is that you get only a single place to change if the email class changes.
interface Email { /** * Send an email * * @param string $event * @param array $data * @return void */ public function send($event, array $data); }
From the above examples, you should get an insight to the importance of the service container concept in Laravel, and understand the practical application to create multiple layers of abstraction, thus making your code more robust.
Remember that robustness is not just about writing good and well-tested code, but also to break your code into digestible chunks of well-documented code, that is easy to adapt, and which works in ever-changing environments.