Laravel Dependency Injection and Service Container

Lesson 43 of 50

86%
1. What Is the Laravel Service Container?

The Laravel Service Container is a tool that manages class dependencies and helps Laravel create objects when they are needed.

When one class requires another class to perform its work, the service container can resolve and provide that dependency.

Example:

Suppose a student controller needs a service to generate student reports. Instead of creating that service manually inside the controller, Laravel can inject it automatically.

Benefits:

  • Reduces unnecessary object creation in controllers.
  • Makes classes easier to test.
  • Helps organize application dependencies.
  • Supports interfaces and interchangeable implementations.
  • Improves maintainability in larger projects.
2. Understand Dependency Injection

Dependency Injection (DI) means providing a class with the objects it needs instead of making the class create those objects itself.

Without dependency injection:

<?php

class StudentReport
{
    public function generate(): string
    {
        return 'Student report generated.';
    }
}

class StudentController
{
    public function showReport(): string
    {
        $report = new StudentReport();

        return $report->generate();
    }
}

Here, the controller creates the StudentReport object directly.

With dependency injection:

<?php

class StudentReport
{
    public function generate(): string
    {
        return 'Student report generated.';
    }
}

class StudentController
{
    public function __construct(
        private StudentReport $report
    ) {}

    public function showReport(): string
    {
        return $this->report->generate();
    }
}

Laravel can resolve the concrete StudentReport class and inject it into the controller constructor.

3. Use Constructor Injection in Laravel

Create a service class:

app/Services/StudentReportService.php

Example service code:

<?php

namespace App\Services;

class StudentReportService
{
    public function generate(): string
    {
        return 'Student report generated successfully.';
    }
}

Now create a controller:

php artisan make:controller StudentReportController

Open the controller:

<?php

namespace App\Http\Controllers;

use App\Services\StudentReportService;

class StudentReportController extends Controller
{
    public function __construct(
        private StudentReportService $reportService
    ) {}

    public function index()
    {
        $message = $this->reportService->generate();

        return response()->json([
            'message' => $message,
        ]);
    }
}

Laravel automatically resolves this concrete service class when constructing the controller, provided its dependencies can also be resolved.

Explicit container bindings are not normally required for simple concrete classes like this one.

4. Use Method Injection

Laravel can also inject dependencies directly into controller methods.

<?php

namespace App\Http\Controllers;

use App\Services\StudentReportService;

class StudentReportController extends Controller
{
    public function index(
        StudentReportService $reportService
    ) {
        return response()->json([
            'message' => $reportService->generate(),
        ]);
    }
}

Laravel resolves the service and passes it to the controller method when handling the request.

Register a route in routes/web.php:

use App\Http\Controllers\StudentReportController;
use Illuminate\Support\Facades\Route;

Route::get(
    '/student-report',
    [StudentReportController::class, 'index']
);

Visit:

http://127.0.0.1:8000/student-report

Example JSON response:

{
    "message": "Student report generated successfully."
}
5. Bind a Class in the Service Container

Sometimes a class depends on an interface rather than a specific implementation. In that situation, you can register a binding that tells Laravel which class to use.

Create an interface:

app/Contracts/ReportGenerator.php
<?php

namespace App\Contracts;

interface ReportGenerator
{
    public function generate(): string;
}

Create an implementation:

app/Services/StudentReportService.php
<?php

namespace App\Services;

use App\Contracts\ReportGenerator;

class StudentReportService implements ReportGenerator
{
    public function generate(): string
    {
        return 'Student report generated successfully.';
    }
}

Register the interface-to-class binding in app/Providers/AppServiceProvider.php.

use App\Contracts\ReportGenerator;
use App\Services\StudentReportService;

public function register(): void
{
    $this->app->bind(
        ReportGenerator::class,
        StudentReportService::class
    );
}

Keep the existing provider class and methods. Add the imports at the top and place the binding inside its register() method.

Now Laravel knows which implementation to provide whenever a class requests the ReportGenerator interface.

6. Inject an Interface into a Controller

Once the interface binding is registered, you can inject the interface instead of the concrete service.

<?php

namespace App\Http\Controllers;

use App\Contracts\ReportGenerator;

class StudentReportController extends Controller
{
    public function index(ReportGenerator $reportGenerator)
    {
        return response()->json([
            'message' => $reportGenerator->generate(),
        ]);
    }
}

Why is this useful?

  • The controller depends on an interface, not a specific implementation.
  • You can replace the report generator without rewriting the controller.
  • You can provide a fake implementation during testing.
  • The design becomes easier to maintain as the application grows.

Laravel uses the binding registered in the service provider to resolve the interface.

7. Resolve Services Manually

You can resolve a dependency from the service container when you need an object outside normal controller dependency injection.

Using the app() helper:

use App\Services\StudentReportService;

$reportService = app(StudentReportService::class);

$message = $reportService->generate();

echo $message;

You can also use the container facade:

use Illuminate\Support\Facades\App;
use App\Services\StudentReportService;

$reportService = App::make(
    StudentReportService::class
);

echo $reportService->generate();

Both examples ask Laravel's container to resolve the service class and its resolvable dependencies.

Prefer constructor or method injection for ordinary application code. Manual resolution is useful when dependencies must be obtained dynamically or outside Laravel's normal injection points.

8. Service Container Best Practices
  • Use dependency injection instead of repeatedly creating dependencies inside controllers.
  • Keep business logic in appropriate service classes.
  • Use interfaces when interchangeable implementations are useful.
  • Register interface bindings in a service provider.
  • Keep controllers focused on handling requests and responses.
  • Use constructor injection for required dependencies.
  • Write tests using fake or substitute implementations when appropriate.
  • Avoid unnecessary bindings for simple concrete classes Laravel can resolve automatically.

Remember:

Dependency injection is a programming technique. The Laravel service container is the mechanism that helps resolve and supply dependencies in a Laravel application.

9. Practice Assignment

Build a small report-generation system.

  1. Create a ReportGenerator interface.
  2. Create a StudentReportService implementation.
  3. Register the interface binding in AppServiceProvider.
  4. Inject the interface into a controller.
  5. Create a route that returns the generated report message.
  6. Test the endpoint in your browser or Postman.
  7. Replace the implementation with another service and observe how the controller remains unchanged.

Bonus Task:

Create a FeeReportGenerator interface with separate implementations for monthly and yearly fee reports. Use container bindings to choose the implementation.

Quick Quiz

1. What is the main purpose of Laravel's service container?




2. What does dependency injection mean?




3. Where can you register an interface binding in Laravel?