Injeção de Dependência (DI) e Inversão de Controle (IoC) no Laravel

Danilo Lutz
4 min readMar 6, 2019

--

E aí galera, tudo bem? Vamos lá?

Como de costume, vou presumir que você tenha já algum conhecimento básico em PHP, MVC e POO… Esse assuntos são pre requisitos para entender muito do funcionamento do Laravel.

Contêiner de Serviço (Service Container)

Basicamente, Service Container (ou IoC Container) é uma ferramenta, centralizada, para gerenciar as dependências de classe e executar a injeção de dependência (Dependency Injection).

Uma compreensão mais aprofundada do Laravel Service Container se faz necessária para que você seja capaz de construir aplicações escaláveis, com baixo acoplamento e também, caso queira, contribuir com o próprio Laravel.

Vamos a um exemplo:

<?php

namespace App\Http\Controllers;

use App\Category;
use App\Repositories\CategoryRepository;
use App\Http\Controllers\Controller;

class CategoryController extends Controller
{
/**
* The category repository implementation.
*
* @var CategoryRepository
*/
protected $categories;

/**
* Create a new controller instance.
*
* @param CategoryRepository $categories
* @return void
*/
public function __construct(CategoryRepository $categories)
{
$this->categories = $categories;
}

/**
* Show the details for the given category.
*
* @param int $id
* @return Response
*/
public function show($id)
{
$category = $this->categories->find($id);

return view('category.detail', ['category' => $category]);
}
}

No exemplo o controller de categoria precisa buscar em uma fonte de dados, os detalhes de uma categoria. Sendo assim, injetamos um serviço (service) apto a recuperar as informações necessárias, no caso o CategoryRepository.

Como ele foi injetado, caso precisemos trocar por outra implementação, não necessitaremos alterar o código do controller. Outra vantagem é que podemos simular (Mocking) ou até mesmo criar uma implementação fictícia de CategoryRepository para fins de testes.

Para quem não é tão familiarizado como termo injetado, deve entender que quem usa o objeto injetado não é o responsável pela criação do mesmo. Inclusive no caso do Laravel quem faz isso é justamente o Service Container.

Por dentro do Service Container

No Laravel apenas as classes que dependem de uma interface devem ser vinculadas ao Service Container, isso para instruí-lo como as classes devem ser construídas. No caso de classes independentes de interface o Laravel as resolve através de Reflection.

Agora vou mostrar 3 das várias maneiras que a injeção de dependência pode ser realizada no Service Container do Laravel.

Vínculo Simples

Dentro de um Service Provider, você sempre pode acessar o Service Container através da propriedade $this->app. Podemos usar o método bind, passando a classe ou o nome da interface que desejamos registrar junto com um Closure que retorna uma instância da classe, desta forma:

$this->app->bind('Repositories\CategoryRepositoryInterface', function($app) {
return new Repositories\CategoryRepository();
});

Vinculando as Interfaces com suas implementações

Outra forma de realizar a instanciação é através da capacidade do Service Container vincular uma interface a um determinada implementação. Um exemplo seria algo como: dado que temos uma interface HumanRepositoryInterface e uma implementação ManRepository podemos registrar isso da seguinte maneira:

$this->app->bind(
'App\Contracts\HumanRepositoryInterface',
'App\Repositories\ManRepository'
);

Essa instrução informa ao Service Container que deve injetar o ManRepository sempre que uma classe precisar de uma implementação do HumanRepositoryInterface. Agora podemos digitar a interface HumanRepositoryInterace em um construtor ou em qualquer outro local onde as dependências são injetadas pelo Service Container:

use App\Contracts\HumanRepositoryInterface;

/**
* Create a new class instance.
*
* @param HumanRepositoryInterface $human
* @return void
*/
public function __construct(HumanRepositoryInterface $human)
{
$this->human= $human;
}

Vínculo Contextual

A terceira e última maneira que irei mostrar é o vínculo contextual, no exemplo anterior usamos a interface HumanRepositoryInterface a qual pode possuir mais de uma implementação, por exemplo ManRepository e WomanRepository.

O nosso contexto seria que temos dois controllers (o ManController e o WomanController) cada um dependendo de uma das implementações específicas, então no Service Container podemos fazer o seguinte:

use App\Http\Controllers\ManController;
use App\Http\Controllers\WomanController;
use Illuminate\Contracts\HumanRepositoryInterface;
use Illuminate\Contracts\ManRepository;
use Illuminate\Contracts\WomanRepository;
$this->app->when(ManController::class)
->needs(HumanRepositoryInterface::class)
->give(ManRepository::class);
// Ou$this->app->when(WomanController::class)
->needs(HumanRepositoryInterface::class)
->give(function() {
return New WomanRepository();
});

E o uso desse modo de vincular seria:

<?php

namespace App\Http\Controllers;

use App\Category;
use App\Repositories\HumanRepositoryInterface;
use App\Http\Controllers\Controller;

class WomanController extends Controller
{
/**
* The woman repository implementation.
*
* @var HumanRepositoryInterface
*/
protected $human;

/**
* Create a new controller instance.
*
* @param HumanRepositoryInterface $human
* @return void
*/
public function __construct(HumanRepositoryInterface $human)
{
$this->human= $human;
}

/**
* Show the details for the given woman.
*
* @param int $id
* @return Response
*/
public function show($id)
{
$woman= $this->human->find($id);

return view('woman.detail', ['woman' => $woman]);
}
}

Bem legal, certo? Fazer um código altamente desacoplado, manutenível e fácil de testar… por hoje eu vou parando por aqui… até a próxima. 😎

Por favor, não deixem de compartilhar, comentar e deixar seu feedback.

Referências

--

--

Danilo Lutz

Obsessed for learning. OOP, PHP, C# and Python lover. Since 2003 coding...