Laravel — P48: Dependency Injection Concept (High-Level PHP Concept)

Elevate your Laravel code with cleaner, modular dependencies

You’re going to hear the term “dependency injection” many times throughout your Laravel career, so you might as well get acquainted with it. Before we get into Laravel, let’s look at Dependency Injection as a concept in PHP (or really any programming language).

https://medium.com/geekculture/laravel-p47-final-touchups-cmp-5b2764c43565

We should know that we can pass objects as arguments to methods. We can also type hint with the inherited class, which is an overview of polymorphism in PHP. We can pass the object as an argument to the constructor. The constructor will then set the property inside the class. This is called dependency injection.

In my PHP tutorial series, we looked at dependency injection. We can use that code to refresh ourselves. To begin, let’s create a couple of classes: Lamborghini and Ferrari. Each one of those classes will require the Car class.

<?php
require_once("Car.php");

class Lamborghini extends Car {

}
<?php
require_once("Ferrari.php");

class Ferrari extends Car {

}

We can now create our test class and instantiate those objects.

<?php
// Object Arguments
require_once("Lamborghini.php");
require_once("Ferrari.php");

$lamborghini_diablo = new Lamborghini(1999, "Lamborghini", "Diablo");
$ferrari_f355 = new Ferrari(1996, "Ferrari", "F355");

Do you think the above code will work? We haven’t added any constructor to our Lamborghini and Ferrari classes, but we’re passing arguments to the constructor. Believe it or not, it actually will work. Why? Because we’ve defined our constructor arguments within our Car parent class. If there’s no constructor within our child class, it will automatically pass arguments to our parent class. Let’s take a quick look at our Car class constructor.

<?php
require_once('Vehicle.php');
require_once('Engine.php');
require_once('Transmission.php');

class Car extends Vehicle
{
    use Transmission, Engine {
        Transmission::check_oil_level insteadof Engine;
        Engine::check_oil_level as check_engine_oil_level;
    }

    const HAS_HEADLIGHTS = true;
    const HAS_TAIL_LIGHTS = true;
    const HAS_TURN_SIGNALS = true;

    private int $fuel_type;
    private int $hp;
    private int $tq;
    private string $transmission;
    private string $vehicle_type;
    private bool $car_on;

    public function __construct(int $year,
                                string $make,
                                string $model,
                                string $color = "",
                                int $fuel_type = 93,
                                int $hp = -1,
                                int $tq = -1,
                                string $transmission = "5 Speed Manual",
                                string $vehicle_type = "",
                                float $exterior_height = -1,
                                float $exterior_width = -1,
                                float $exterior_length = -1,
                                string $exterior_um = "in",
                                float $weight = -1,
                                string $weight_um = "lbs",
                                bool $car_on = false
    )
    {
        $this->year = $year;
        $this->make = $make;
        $this->model = $model;
        $this->color = $color;
        $this->fuel_type = $fuel_type;
        $this->hp = $hp;
        $this->tq = $tq;
        $this->transmission = $transmission;
        $this->vehicle_type = $vehicle_type;
        $this->exterior_height = $exterior_height;
        $this->exterior_width = $exterior_width;
        $this->exterior_length = $exterior_length;
        $this->exterior_um = $exterior_um;
        $this->weight = $weight;
        $this->weight_um = $weight_um;
        $this->car_on = $car_on;
    }
    
    //...
}

What can we do now? We have access to all of the properties and methods in our parent class (as long as they’re not private). So we can call those properties and methods.

<?php
// Object Arguments
require_once("Lamborghini.php");
require_once("Ferrari.php");

$lamborghini_diablo = new Lamborghini(1999, "Lamborghini", "Diablo");
echo $lamborghini_diablo->check_engine_oil_level();

$ferrari_f355 = new Ferrari(1996, "Ferrari", "F355");
echo $ferrari_f355->check_oil_level();

Great. We still haven’t done any passing of objects as arguments.

What we’re going to do is create a Driver class. The first method inside of our Driver class will be the drive() method since the Driver should be able to drive() a car. We can pass the Car object to it as an argument and then we can use various different Car methods to help us with driving the car. The two methods that we’ll focus on from our Car class is the turnOn() method and the drive() method.

<?php

class Driver {

    public function drive(Car $car) {
        echo $car->turnOn();
        echo $car->drive();
    }
}

We can now go back to our test class and instantiate the object.

<?php
// Object Arguments
require_once("Lamborghini.php");
require_once("Ferrari.php");

$lamborghini_diablo = new Lamborghini(1999, "Lamborghini", "Diablo");
$ferrari_f355 = new Ferrari(1996, "Ferrari", "F355");

$dino = new Driver();
$dino->drive( $lamborghini_diablo );

What just happened? I thought we specified that we have to pass a Car type object as an argument to the drive() method. It’s in the drive() declaration.

public function drive( Car $car )

And here’s your lesson on Polymorphism. Since both our Lamborghini and Ferrari classes extend the Car class, technically they are Cars! Guess what? Since the Car extends the Vehicle class, technically the Lamborghini and Ferrari classes are Vehicles as well! You’re wondering if we can modify our declaration to say the following:

public function drive( Vehicle $vehicle )

The answer is yes! You absolutely can. And you would still pass the $lamborghini_diablo or the $ferrari_f355 as an argument to the drive() method. If we pass the $ferrari_f355 to our drive() method, we get the following result: Ferrari F355 has been turned on. I’m driving.

Let’s do this a little differently now. We created class properties of intstring, etc. type? Just look at the Car class and you’ll see all of the various different properties. How did we initialize those properties? From the constructor. When instantiating the object, we would pass arguments to the constructor and then the constructor would initialize those properties with the values that were passed through as arguments. Same concept with what we’ll do here in our Driver class. We’ll specify that we’re going to have a Car based property and we’re going to pass a Car argument through our constructor and assign it to our Car property. Remember, a Car is just a data type like int or string.

<?php

class Driver {

    private Car $car;

    public function __construct(Car $car)
    {
        $this->car = $car;
    }
}

I don’t want to lose you here now. Remember that once the $car property has been initialized through our constructor, we’re going to have access to all of our Car properties and methods, such as turnOn() and drive(). Since we know that we must pass a Car type of object to our constructor and it must be assigned to our $car property, we can create methods in anticipation that we’re going to have access to those Car methods. So, let’s recreate our drive() method inside of our Driver class.

<?php

class Driver {

    private Car $car;

    public function __construct(Car $car)
    {
        $this->car = $car;
    }

    public function drive() {
        echo $this->car->turnOn();
        echo $this->car->drive();
        echo $this->car->turnOff();
    }
}

Do you see how this works? Let’s run an example in our test file and then walk through the code.

<?php
// Object Arguments
require_once("Lamborghini.php");
require_once("Ferrari.php");
require_once("Driver.php");

$lamborghini_diablo = new Lamborghini(1999, "Lamborghini", "Diablo");
$ferrari_f355 = new Ferrari(1996, "Ferrari", "F355");

$dino = new Driver( $ferrari_f355 );
$dino->drive();

$harrison = new Driver( $lamborghini_diablo );
$harrison->drive();

Time for a thorough code walk through.

  • PHP enters the test class and requires the LamborghiniFerrari, and Driver classes.
  • A new Lamborghini object is instantiated, which is of type Car.
  • Since the Lamborghini class does not contain a constructor, the parent constructor is called and the three arguments are passed to the parent constructor: 1999LamborghiniDiablo.
  • A new Ferrari object is instantiated, which is also of type Car.
  • Since the Ferrari class does not contain a constructor, the parent constructor is called and the three arguments are passed to the parent constructor: 1996FerrariF355.
  • A new Driver is created and the $ferrari_f355 object is passed to the constructor as an argument.
  • The Driver class initialized the $car property inside itself with the $ferrari_f355 object that was just passed to it. The $car property now has access to all of the methods inside of $ferrari_f355.
  • The drive() method is invoked.
  • PHP enters the drive() method. It sees that it’s calling 3 different methods all located within the $car property, which is technically the $ferrari_355 object.
  • PHP executes the turnOn()drive(), and turnOff() methods.
  • A new Driver is created inside of the test file and the $lamborghini_diablo object is passed to the constructor as an argument.
  • The Driver class initialized the $car property inside itself with the $lamborghini_diablo object that was just passed to it. The $car property now has access to all of the methods inside of $lamborghini_diablo.
  • The drive() method is invoked.
  • PHP enters the drive() method. It sees that it’s calling 3 different methods all located within the $car property, which is technically the $lamborghini_diablo object.
  • PHP executes the turnOn()drive(), and turnOff() methods.

This passing of an argument through the constructor and assigning it to a property is what’s called Dependency Injection.

As long as you visualize the flow of how the argument moves through the code, I’m confident that you’ll understand dependency injection. Complicated name for a simple topic.

We’ll tackle Laravel’s use of dependenct injection in the next article.

https://github.com/dinocajic/youtube-laravel

 

Laravel Series

Continue your Laravel Learning.

Laravel — P47: Final Touchups (CMP)

Give your Laravel project the perfect finishing touches

Laravel – P47: CMP – Final Touchups

In part 47 of our Laravel series, wrap up your application by applying crucial final adjustments. Learn best practices for code refactoring, caching strategies, QA checks, and polishing UI elements to ensure a secure, high-performing, and fully optimized Laravel project.

Laravel — P48: Dependency Injection Concept (High-Level PHP Concept)

Elevate your Laravel code with cleaner, modular dependencies

Laravel – P48: Dependency Injection Concept

In part 48 of our Laravel series, dive into the high-level PHP concept of Dependency Injection. Learn how to write cleaner, more testable code by decoupling class dependencies, improving maintainability, and promoting flexible, scalable design patterns within your Laravel applications.

Laravel — P49: Dependency Injection

Implement effective DI patterns for cleaner, testable Laravel code

Laravel – P49: Dependency Injection

In part 49 of our Laravel series, learn how to apply Dependency Injection in practice. Discover how to structure your project for clearer separation of concerns, enhance testability, and simplify code maintenance by fully leveraging Laravel’s built-in DI container.

Leave a Reply