Laravel-Injection And Inversion Of Dependencies

Hernan Castilla
5 min readNov 9, 2021

--

Uncouple, that’s the idea

let’s do a simple example where see how inject and invert dependecies, laravel bring all necessary configuration for doing this process of simple shape.

As an example, let’s build an app to create and delete notes, using variables of session and after using eloquent with any database manager.

(spanish) Hagamos un ejemplo sencillo donde se vea como inyectar e invertir dependencias, laravel trae toda configuración necesaria para hacer este proceso de forma simple.

Como ejemplo, creemos una app para crear y eliminar notas, usando variables de session y después usando eloquent con el gestor de base de datos que prefieras.

¿Problem?

Before start, let’s consider the folder names, files, and class to working autoload without problems.

(spanish) Antes de iniciar, debemos tener en cuenta los nombres de nuestras carpetas, archivos y clases para que funciona el autoload sin problemas.

Example:

app/Repositories/NoteRepository.php

<?phpnamespace App\Repositories\NoteRepository;class NoteRepository {}

Is time to code!

we create the folders first

fine, we create the controller app/Http/Controllers/NoteController.php

php artisan make:controller NoteController

and we aggregate the basics

<?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('/');
}
}

now we have to edit the 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']);

we create the files

app/Core/Services/NoteService.php

app/Core/Repositories/Interfaces/NoteRepository.php

app/Core/Repositories/NoteRepositoryInMemory.php

now we have to tell laravel how to use these files, we can do it in two ways:

  • creating a new provider
  • using the AppServiceProvider

we use the AppServiceProvider, but i leave the link to see how to register one

(spanish) voy a usar el AppServiceProvider pero te dejo el enlace de como crear y registrar uno.

app/Providers/AppServiceProvider.php

The magic is to specify in laravel what instance building when call the classes, either new instances or applying singleton.

(spanish) la magia es especificarle a laravel que instanciar cuando se llamen que clases, ya sea retornando nuevas instancias o aplicando singleton.

<?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
{
/**
* Register any application services.
*
* @return void
*/
public function register()
{
$this->app
->singleton(
NoteRepository::class,
NoteRepositoryInMemory::class
);
$this->app
->singleton(
NoteService::class,
function ($app) {
return new NoteService(
$app->make(NoteRepository::class)
);
}
);
}

/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
}
}

As observed, we indicate that calls to interface NoteRepository return an instance of the implementation NoteRepositoryInMemory, when you want to change of implementation only is indicated that returns another class.

(spanish) Como se observa, indicamos que las llamadas a la interface NoteRepository devolverán una instancia de la implementación NoteRepositoryInMemory, por lo que cuando se quiera cambiar la implementación solo es indicarle que retorne otra clase.

We create the logic for repository:

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'),
function ($note) use ($id) {
return $note['id'] != $id;
}
);
session([
'notes' => $notes
]);
}
}

We create logic for service:

app/Core/Services/NoteService.php

Watch as is injected the interface and not the implementation, achieving that inversion of dependence.

(spanish) Observa como se inyecta la interface y no la implementacion logrando esa inversion de dependencia.

<?php

namespace App\Core\Services;

use App\Core\Repositories\Interfaces\NoteRepository;

class NoteService
{
function __construct(NoteRepository $repository)
{
$this->repository = $repository;
}

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

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

We refactor the controller:

<?php

namespace App\Http\Controllers;

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

class NoteController extends Controller
{
function __construct(NoteService $service)
{
$this->service = $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('/');
}
}

let’s see the magic

we create a new implementation of the repository that use eloquent.

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);
}
}

We change the configuration so when call to the interface NoteRepository return NoteRepositoryEloquent

app/Providers/AppServiceProvider.php

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

Fine, that’s all, we have our app working with other implementation of the repository, we pass of save the data in memory to save the data in one database and because we uncouple the code, this one does not suffer changes.

(spanish) listo, eso es todo, tenemos nuestra app funcionando con una nueva implementacion de otro repositorio, pasamos de almacenar la data en session a una base de datos y debido al nivel desacoplamiento el codigo prácticamente no sufrió este cambio.

Github Repository.

--

--

Hernan Castilla
Hernan Castilla

Written by Hernan Castilla

” Le vent se lève!… Il faut tenter de vivre!”

No responses yet