In this lesson, you will understand what Dependency Injection is and how it functions within a Laravel application. We'll explore real-world examples to demonstrate how it plays a key role in creating dynamic and modular applications. Specifically, you'll learn how to inject dependencies into your services and controllers, promoting clean and manageable code.
Here is a concise example that expands on our previous lesson, where you will see an ExampleService dependent on BreadProvider, CheeseProvider, and GrillProvider, all injected into the ExampleService using Dependency Injection.
app/app/Services/ExampleService.php
<?php
namespace App\Services;
use App\Providers\BreadProvider;
use App\Providers\CheeseProvider;
use App\Providers\GrillProvider;
class ExampleService
{
protected $bread;
protected $cheese;
protected $grill;
public function __construct(BreadProvider $bread, CheeseProvider $cheese, GrillProvider $grill)
{
$this->bread = $bread;
$this->cheese = $cheese;
$this->grill = $grill;
}
public function makeSandwich()
{
$this->grill->turnOn();
$sandwich = "Sandwich with " . $this->bread->getName() . " and " . $this->cheese->getName();
$this->grill->turnOff();
return $sandwich;
}
}
Let's understand the code above:
- The
ExampleService class has three properties: $bread, $cheese, and $grill.
- The
__construct method injects the dependencies BreadProvider, CheeseProvider, and GrillProvider into the ExampleService.
- The
makeSandwich method uses the injected dependencies to create a sandwich with bread and cheese, turning the grill on and off in the process. Finally returning the sandwich.
Now let's see how we should define the BreadProvider, CheeseProvider with a common interface IngredientInterface and the GrillProvider class.
First, let's define the IngredientInterface interface. Interfaces are contracts that define the methods a class must implement. In this case, the IngredientInterface interface has a single method getName, which means any class that implements this interface must have a getName method.
app/app/Providers/IngredientInterface.php
<?php
namespace App\Interfaces;
interface IngredientInterface
{
public function getName();
}
Next, let's define the concrete classes BreadProvider, CheeseProvider that implement the IngredientInterface interface.
app/app/Providers/BreadProvider.php
<?php
namespace App\Providers;
use App\Interfaces\IngredientInterface;
class BreadProvider implements IngredientInterface
{
public function getName()
{
return "Slice of Bread";
}
}
app/app/Providers/CheeseProvider.php
<?php
namespace App\Providers;
use App\Interfaces\IngredientInterface;
class CheeseProvider implements IngredientInterface
{
public function getName()
{
return "Cheese";
}
}
Finally, let's define the GrillProvider class. Note that this class does not implement the IngredientInterface interface, as it is not an ingredient.
app/app/Providers/GrillProvider.php
<?php
namespace App\Providers;
class GrillProvider
{
public function turnOn()
{
echo 'Grill is turned on.'; // echo prints the message to the browser directly
}
public function turnOff()
{
echo 'Grill is turned off.';
}
}
In the code above, we have defined the IngredientInterface interface, which is implemented by the BreadProvider and CheeseProvider classes. The GrillProvider class does not implement the IngredientInterface interface, as it is not an ingredient. The ExampleService class injects the BreadProvider, CheeseProvider, and GrillProvider classes using Dependency Injection.
Now, it's time to see how we can use the ExampleService class in a controller:
app/app/Http/Controllers/ExampleController.php
<?php
namespace App\Http\Controllers;
use App\Services\ExampleService;
use Illuminate\Http\Request;
class ExampleController extends Controller
{
protected $exampleService;
public function __construct(ExampleService $exampleService)
{
$this->exampleService = $exampleService;
}
public function makeSandwich()
{
$sandwich = $this->exampleService->makeSandwich();
return view('sandwich', ['sandwich' => $sandwich]);
}
}
In the ExampleController class, the ExampleService class is injected into the controller using Dependency Injection. The makeSandwich method calls the makeSandwich method of the ExampleService class and returns the result to a view.
Finally, we'll add a route to the routes/web.php file to access the makeSandwich method of the ExampleController class, and display the sandwich in a view.
As a result, when you access the route, you will see the sandwich created by the ExampleService class using the injected dependencies, and the following message will be displayed in the browser:
Grill is turned on. Grill is turned off.
Sandwich with Slice of Bread and Cheese
Note, that the grill related messages are printed directly to the browser using the echo statement. In a real-world application, you would typically return the messages as a response to the client.
In this example, we have seen how Dependency Injection can be used to inject dependencies into classes, promoting clean and manageable code. By injecting dependencies instead of hardcoding them, you create flexible applications that are easier to debug and extend.