Welcome back to your Laravel learning journey! As we venture deeper into Laravel, you’ll now learn about a crucial concept known as Dependency Injection. Building on the idea of services from the previous lesson, Dependency Injection will help you understand how Laravel's design encourages clean and efficient coding. This lesson is designed to help you grasp this important concept and see how it can simplify the dependencies within your application.
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
php1<?php 2 3namespace App\Services; 4 5use App\Providers\BreadProvider; 6use App\Providers\CheeseProvider; 7use App\Providers\GrillProvider; 8 9class ExampleService 10{ 11 protected $bread; 12 protected $cheese; 13 protected $grill; 14 15 public function __construct(BreadProvider $bread, CheeseProvider $cheese, GrillProvider $grill) 16 { 17 $this->bread = $bread; 18 $this->cheese = $cheese; 19 $this->grill = $grill; 20 } 21 22 public function makeSandwich() 23 { 24 $this->grill->turnOn(); 25 $sandwich = "Sandwich with " . $this->bread->getName() . " and " . $this->cheese->getName(); 26 $this->grill->turnOff(); 27 return $sandwich; 28 } 29}
Let's understand the code above:
- The
ExampleService
class has three properties:$bread
,$cheese
, and$grill
. - The
__construct
method injects the dependenciesBreadProvider
,CheeseProvider
, andGrillProvider
into theExampleService
. - 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
php1<?php 2 3namespace App\Interfaces; 4 5interface IngredientInterface 6{ 7 public function getName(); 8}
Next, let's define the concrete classes BreadProvider
, CheeseProvider
that implement the IngredientInterface
interface.
app/app/Providers/BreadProvider.php
php1<?php 2namespace App\Providers; 3use App\Interfaces\IngredientInterface; 4 5class BreadProvider implements IngredientInterface 6{ 7 public function getName() 8 { 9 return "Slice of Bread"; 10 } 11}
app/app/Providers/CheeseProvider.php
php1<?php 2namespace App\Providers; 3use App\Interfaces\IngredientInterface; 4 5class CheeseProvider implements IngredientInterface 6{ 7 public function getName() 8 { 9 return "Cheese"; 10 } 11}
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
php1<?php 2 3namespace App\Providers; 4 5class GrillProvider 6{ 7 public function turnOn() 8 { 9 echo 'Grill is turned on.'; // echo prints the message to the browser directly 10 } 11 12 public function turnOff() 13 { 14 echo 'Grill is turned off.'; 15 } 16}
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
php1<?php 2 3namespace App\Http\Controllers; 4 5use App\Services\ExampleService; 6use Illuminate\Http\Request; 7 8class ExampleController extends Controller 9{ 10 protected $exampleService; 11 12 public function __construct(ExampleService $exampleService) 13 { 14 $this->exampleService = $exampleService; 15 } 16 17 public function makeSandwich() 18 { 19 $sandwich = $this->exampleService->makeSandwich(); 20 return view('sandwich', ['sandwich' => $sandwich]); 21 } 22}
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:
Plain text1Grill is turned on. Grill is turned off. 2Sandwich 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.
Dependency Injection is a cornerstone of modern software development and is particularly important when working with Laravel. It promotes loose coupling between classes, making your code easier to test and maintain. By injecting dependencies instead of hardcoding them, you create flexible applications that are easier to debug and extend.
Learning Dependency Injection ensures you can build more scalable and resilient applications. It helps you adhere to solid design principles that are critical in building applications that stand the test of time.
Are you eager to see how Dependency Injection will transform your approach to coding in Laravel? Let's move to the practice section to get hands-on experience with these concepts.