Laravel — Dependency Injection and Inversion of Control

Uncoupling: That’s the Idea

Let’s walk through a simple example to understand how to inject and invert dependencies in Laravel. Laravel provides all the necessary configuration to do this in a clean and elegant way.

As an example, we’ll build an app that allows creating and deleting notes. First, using session variables, and later swapping the logic to use Eloquent and any database manager.

(Spanish) Hagamos un ejemplo sencillo para entender cómo inyectar e invertir dependencias. Laravel ya provee toda la configuración necesaria para hacerlo de forma simple.


🧩 Project Setup and Structure

Before we start, keep in mind that naming conventions for folders, files, and classes are essential for autoloading to work correctly.

Example path:

app/Repositories/NoteRepository.php

Example class:

<?php

namespace App\Repositories;

class NoteRepository {}

✨ Getting Started

Let’s create the folders and the controller:

php artisan make:controller NoteController

app/Http/Controllers/NoteController.php

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class NoteController extends Controller
{
    public function index()
    {
        $notes = [];
        return view('welcome', compact('notes'));
    }

    public function create(Request $request)
    {
        return redirect('/');
    }

    public function delete(int $id)
    {
        return redirect('/');
    }
}

routes/web.php

<?php

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

Route::get('/', [NoteController::class, 'index']);
Route::post('/', [NoteController::class, 'create']);
Route::delete('/{id}', [NoteController::class, 'delete']);

📁 Creating the Core Files

app/Core/Services/NoteService.php
app/Core/Repositories/Interfaces/NoteRepository.php
app/Core/Repositories/NoteRepositoryInMemory.php

🛠 Binding Interfaces to Implementations

We’ll tell Laravel how to resolve interfaces to concrete classes. You can do this either:

  • By creating a new Service Provider
  • Using the default AppServiceProvider

We’ll use AppServiceProvider for simplicity. More info: Laravel Providers

app/Providers/AppServiceProvider.php

<?php

namespace App\Providers;

use App\Core\Repositories\Interfaces\NoteRepository;
use App\Core\Repositories\NoteRepositoryInMemory;
use App\Core\Services\NoteService;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    public function register()
    {
        $this->app->singleton(NoteRepository::class, NoteRepositoryInMemory::class);

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

    public function boot()
    {
        //
    }
}

📦 Implementing the Interfaces

app/Core/Repositories/Interfaces/NoteRepository.php

<?php

namespace App\Core\Repositories\Interfaces;

interface NoteRepository
{
    public function create(string $title, string $description): array;
    public function getAll(): array;
    public function delete(int $id): void;
}

app/Core/Repositories/NoteRepositoryInMemory.php

<?php

namespace App\Core\Repositories;

use App\Core\Repositories\Interfaces\NoteRepository;

class NoteRepositoryInMemory implements NoteRepository
{
    public function __construct()
    {
        if (!session()->has('notes')) {
            session(['notes' => []]);
        }
    }

    public function getAll(): array
    {
        return session('notes');
    }

    public function create(string $name, string $description): array
    {
        $new = [
            "id" => count(session('notes')) + 1,
            "name" => $name,
            "content" => $description
        ];
        $notes = session('notes');
        array_unshift($notes, $new);
        session(['notes' => $notes]);
        return $new;
    }

    public function delete(int $id): void
    {
        $notes = array_filter(session('notes'), fn($note) => $note['id'] != $id);
        session(['notes' => $notes]);
    }
}

🔧 The Service Layer

app/Core/Services/NoteService.php

<?php

namespace App\Core\Services;

use App\Core\Repositories\Interfaces\NoteRepository;

class NoteService
{
    public function __construct(private NoteRepository $repository) {}

    public function create(string $name, string $content)
    {
        return $this->repository->create($name, $content);
    }

    public function getAll()
    {
        return $this->repository->getAll();
    }

    public function delete(int $id)
    {
        $this->repository->delete($id);
    }
}

🧼 Refactor the Controller to Use the Service

<?php

namespace App\Http\Controllers;

use App\Core\Services\NoteService;
use Illuminate\Http\Request;

class NoteController extends Controller
{
    public function __construct(private NoteService $service) {}

    public function index()
    {
        $notes = $this->service->getAll();
        return view('welcome', compact('notes'));
    }

    public function create(Request $request)
    {
        $request->validate([
            'name' => 'required',
            'content' => 'required',
        ]);
        $this->service->create($request->name, $request->content);
        return redirect('/');
    }

    public function delete(int $id)
    {
        $this->service->delete($id);
        return redirect('/');
    }
}

🎯 Swapping to Eloquent (Inversion in Action)

Now create:

app/Core/Repositories/NoteRepositoryEloquent.php

<?php

namespace App\Core\Repositories;

use App\Core\Repositories\Interfaces\NoteRepository;
use App\Models\Note;

class NoteRepositoryEloquent implements NoteRepository
{
    public function getAll(): array
    {
        return Note::all()->toArray();
    }

    public function create(string $name, string $content): array
    {
        $note = ['name' => $name, 'content' => $content];
        Note::create($note);
        return $note;
    }

    public function delete(int $id): void
    {
        Note::destroy($id);
    }
}

Then update the AppServiceProvider binding:

$this->app->singleton(
    NoteRepository::class,
    NoteRepositoryEloquent::class
);

Done! You swapped persistence from session to database without touching the controller or service.

(Spanish) ¡Listo! Cambiamos el almacenamiento de la sesión a una base de datos sin modificar el controlador ni el servicio, gracias al desacoplamiento.