All you need to know about Laravel Service Container

All you need to know about Laravel Service Container

Laravel Service Container

This is how we will learn about the Laravel Service Container: we'll cover the necessary terminology and concepts, then examine the code to gain a complete understanding.

Concepts:-

  • Service container

  • Dependency injection

  • Registering dependency

  • Resolving dependency

What is a Service container?

Simple Answer:

Imagine having a magical box called the "Magic Box" that gives you anything you need. Instead of going to different shops, you ask the box for what you want, and it gives it to you. Service Container is like a smart helper for your web app, storing and managing everything it might need.

Technical Answer:

The service container is a powerful tool for managing class dependencies and performing dependency injection. Its sole responsibility is to manage the dependencies in your laravel project.

What is dependency injection?

Dependency injection is a design pattern in which components or services are externally provided to a class. It is typically achieved by providing the required dependencies to a class through constructor parameters, method arguments, or setter methods.

What is Registering dependency?

Registering dependencies in Laravel involves binding abstractions (interfaces or abstract classes) to their concrete implementations within the service container. This enables the framework to automatically resolve and inject the correct implementations when needed throughout the application, simplifying the process of managing and using dependencies.

What is Resolving dependency?

Resolving dependencies in Laravel is obtaining instances of registered classes from the service container. The framework automatically analyzes the required dependencies and fetches their concrete implementations from the container. This process ensures that the correct implementations are readily available for use in the application.

Ways to Register Dependencies

  1. Simple Bindings

  2. Binding A Singleton

  3. Binding Interfaces To Implementations

Dependencies are registered in the ServiceProvider register method.

<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     */
    public function register(): void
    {
        // here we will register the dependencies
    }
}

Simple Bindings

The Transistor class is registered in the service container via the bind method. Each time this dependency is resolved, a new instance of the class is provided:

use App\Services\Transistor;
use App\Services\PodcastParser;

$this->app->bind(Transistor::class, function ($app) {
    return new Transistor($app->make(PodcastParser::class));
});

Binding A Singleton

The singleton method binds a class or interface into the container that should only be resolved once. Once a singleton binding is resolved, the same object instance will be returned on subsequent calls into the container:

use App\Services\Transistor;
use App\Services\PodcastParser;

$this->app->singleton(Transistor::class, function ($app) {
    return new Transistor($app->make(PodcastParser::class));
});

Binding Interfaces To Implementations

A compelling feature of the service container is its ability to bind an interface to a given implementation. For example, let's assume we have an EventPusher interface and a RedisEventPusher implementation. Once we have coded our RedisEventPusher implementation of this interface, we can register it with the service container like so:

use App\Contracts\EventPusher;
use App\Services\RedisEventPusher;

$this->app->bind(EventPusher::class, RedisEventPusher::class);

meaning whenever the EventPusher resolved from the container the RedisEventPusher class implementation will be used.

Ways to Resolve Dependencies

  1. Automatic injection

  2. Property Injection

Automatic injection

// PostController.php:
public function store(StorePostRequest $request) {
    Post::create($request->validated());
    // ...
}

See that StorePostRequest as a type-hint in the method? It's a typical Form Request class. Notice that we don't initialize the instance of that class. Laravel does this for us if we type-hint the class.

In other words, we don't need to do this manually inside the method:

$request = new StoreUserRequest();

So this is done by Service Container; it "resolves" the instance of that class and auto-creates the object for us using the PHP Reflection Apis. Another example is through the constructor.

<?php

namespace App\Http\Controllers;

use App\Repositories\PostRepository;
use App\Models\Post;

class PostController extends Controller
{
    /**
     * Create a new controller instance.
     */
    public function __construct(
        protected PostRepository $posts,
    ) {}

    /**
     * Show the post with the given ID.
     */
    public function show(string $id): Post
    {
        return $this->posts->findOrFail($id); 
    }
}

Property Injection

In property injection, dependencies are directly assigned to class properties, allowing them to be accessed throughout the class.

Suppose we have an interface ShippingMethod and two concrete implementations StandardShipping and ExpressShipping.

interface ShippingMethod {
    public function calculateCost($weight);
}

class StandardShipping implements ShippingMethod {
    public function calculateCost($weight) {
        // Logic to calculate standard shipping cost based on weight
        return $weight * 5;
    }
}

class ExpressShipping implements ShippingMethod {
    public function calculateCost($weight) {
        // Logic to calculate express shipping cost based on weight
        return $weight * 10;
    }
}

Now, let's create a class OrderProcessor that uses property injection to handle shipping:

class OrderProcessor {
    protected $shippingMethod;

    public function setShippingMethod(ShippingMethod $method) {
        $this->shippingMethod = $method;
    }

    public function processOrder($weight) {        
        $shippingCost = $this->shippingMethod->calculateCost($weight);
        // Logic to process the order and calculate the total cost
        $totalCost = $shippingCost + /* additional costs */;
        return $totalCost;
    }
}

By using shippingMethod property injection, Laravel automatically assigns the appropriate implementation based on the service container's binding. This makes the OrderProcessor class more flexible and allows for easy switching between shipping methods without modifying the core logic.

Check out the complete documentation for the service container on Laravel official documentation.
https://laravel.com/docs/10.x/container

Want to hire me
- - - - - - -
Email: