詳細検索

Injeção de Dependência com Laravel

Avatar
por Ming Xiang
5 min de leitura

Injeção de Dependência com Laravel
Traduzido do English • Ver original

Injeção de Dependência - Com Laravel

A injeção de dependências é um padrão de design comumente usado em programação orientada a objetos. Por meio de algumas convenções pré-estabelecidas, conseguimos gerenciar a criação de nossas dependências com mais facilidade. Podemos declarar, substituir ou até mesmo simular as dependências conforme necessário, sem a necessidade de alterar o código que depende delas.

Por exemplo, digamos que temos alguma lógica de autenticação

classe AuthLogic
{ 
    function authenticate(Request $request): bool
    { 
        try { 
            $payload = $request->todos(); 
            $token = $this->verifyAuthToken($payload); 
            $this->verificarCliente($token); 
            $this->verificarPermissões($token); 
            retorno verdadeiro; 
        } captura (Exceção $e) { 
            retorne falso; 
        }
    }
}

Queremos escrever um teste para qualquer código que interaja com essa lógica, você pode ver que precisaríamos semear muitos dados inicialmente. Para qualquer teste de controlador autenticado, precisaríamos semear um cliente, um usuário, algumas permissões e então gerar um token de autenticação para o que foi dito acima. Usando injeção de dependências, podemos evitar toda essa configuração e apenas simular 'autenticar' para sempre devolver 'verdadeiro' ou 'falso'. Vamos dar uma olhada em como fazer isso no Laravel!

Como realizar a Injeção de Dependência

Como a maioria dos frameworks, o Laravel nos permite usar injeção de dependências para organizar nosso código. Vamos dar uma olhada em como fazer isso! Esses são os passos que precisaremos executar.

  1. Defina a dependência
  2. Vincular a dependência
  3. Injetar a dependência
  4. (Opcional) Mock/Substituir a dependência

Defina a Dependência

Primeiro, você precisa definir uma classe de objetos da qual outras classes dependerão. Aqui, vamos criar uma classe simples chamada 'HelperService' que queremos usar em outras partes do nosso código.

 

Classe HelperService
{ 
    função __construct() { 
        $this->contador = 0; 
    }

    função incrementAndGetCounter(): int
    { 
        $this->contador += 1; 
        retorne $this->contador; 
    }
}

Prenda a Dependência

Agora, precisamos construir a dependência e vinculá-la ao contêiner de serviço Laravel. No Laravel, isso geralmente é feito criando um provedor de serviço e registrando-o.

php artisan make:provider HelperServiceProvider

No arquivo gerado, você registraria sua vinculação e a função que deve ser executada sempre que essa ligação for necessária. É aqui que construiríamos a classe.

use Illuminate\Support\ServiceProvider; 

classe HelperServiceProvider estende ServiceProvider
{ 
    registrador de função()
    { 
        Use $this->app->singleton para instanciar apenas uma vez
        $this->app->bind(HelperService::class, function() { 
            retorno de novo Serviço de Ajuda(); 
        }); 
    }
}

Por fim, você vincularia o 'HelperServiceProvider' ao contêiner de serviço Laravel em 'config/app.php'

'providers' => [ 
    # ... Provedores existentes
    App\Providers\HelperServiceProvider::class, 
]

Injetar a Dependência

Agora que a vinculação de dependência está declarada no contêiner de serviço Laravel, podemos injetá-la no nosso código Laravel onde for necessária. Por exemplo, você pode usá-la em seus controladores ou middleware por meio de injeção automática. Laravel detectará que você quer uma classe 'HelperService' que ela pode corresponder à vinculação que você declarou anteriormente.

classe HomeController estende o Controller
{ 
    função __construct(Serviço de Ajuda $helperService)
    { 
        $this->helperService = $helperService; 
    }

    função home()
    { 
        retorno [ 
            'counter' => $this->helperService->incrementAndGetCounter()
        ]; 
    }
}

 

Também poderíamos injetar manualmente essa dependência em qualquer outra classe ou função diretamente usando a fachada do 'App' do Laravel.

use Illuminate\Support\Facades\App; 

classe UserService
{ 
    função tryAlgo(Serviço de Ajuda $helperService)
    { 
        print($this->helperService->incrementAndGetCounter()); 
    }
}
 
injetar automaticamente a instância como um argumento de função
$result = App::call([novo UserService, 'trySomething']); 

Alternativamente, você pode resolver a instância e passar você mesmo
$helperService = App::make(HelperService::class); 
$result = (novo UserService)->tryAlgo($helperService); 

 

Você pode ver que agora podemos usar o 'novo HelperService()' invocado pelo provedor de serviço em todo o código subsequente sem que ele seja explicitamente construído. A construção da classe é controlada pelo processo de inicialização do Laravel.

Mock/Substituir a Dependência

Agora que estamos usando injeção de dependência para gerenciar os objetos de dependência, temos a capacidade de simulá-los e substituí-los durante a execução. Isso é particularmente útil durante testes (por exemplo, simulando sua autenticação para sempre devolver verdadeiro).

Você pode ler mais sobre como vincular objetos ao contêinero de serviço aqui: https://laravel.com/docs/9.x/container

usar Mockery\MockInterface; 
usar Illuminate\Foundation\Testing\RefreshDatabase; 
usar Tests\TestCase; 

classe HomeControllerTest estende o TestCase
{ 
    usar o RefreshDatabase; 

    teste de função pública Home(): nulo
    { 
        $this->mock(HelperService::class, function (MockInterface $mock) { 
            $mock->deveriaReceber('incrementeAndGetCounter')->eReturn(5); 
        }); 

        $response = $this->getJson('/', []); 

        $this->assertEquals($response['counter'], 5); 
    }
}

 

Agora, em vez de instanciar o 'HelperService' real em nossos testes, podemos usar um mock desse objeto! Isso é ótimo para isolar partes do seu código e realizar seus testes unitários com mais facilidade.

Você pode encontrar mais sobre zombaria aqui: https://laravel.com/docs/9.x/mocking#mocking-objects

Quando usar a Injeção de Dependência

A injeção de dependências é uma ótima ferramenta para organizar nosso código, mas, como qualquer outra ferramenta, devemos usá-la apenas quando apropriada. Pessoalmente, acho que a injeção de dependência é mais útil para os seguintes dois propósitos:

  1. Precisamos manter e modificar algum estado ao longo do ciclo de vida da requisição (por exemplo, um singleton usado em middlewares, controladores e classes de serviço). Isso é um pouco parecido com o motivo pelo qual eu colocaria algo no Redux, quando passar um objeto por várias camadas de componentes/serviços se torna complicado.

  2. Queremos alguma lógica ou estado que possamos querer abstrair e torná-lo ridicularizável (por exemplo, clientes http, estado de autenticação)

Um ótimo exemplo é o estado de autenticação. Normalmente, você pode precisar realizar várias etapas antes de criar uma requisição autenticada. Isso pode ser muito complicado e fazer seus testes se encaixarem demais com sua lógica de autenticação. Você realmente precisa testar novamente como gerar um token de autenticação em cada teste de requisição de API que cria?

Usando injeção de dependências, poderíamos simplesmente semear o mínimo necessário e simular a camada de autenticação. O código de teste resultante poderia ficar abaixo:

classe AuthMiddleware
{ 
    função __construct(AuthLogic $authLogic)
    { 
        $this->authLogic = $authLogic; 
    }

    handle de função (Request $request, Closure $next)
    { 
        if ($this->authLogic->authenticate($request)) { 
            retorno $next($request); 
        } else { 
            abortar (Resposta:HTTP_FORBIDDEN); 
        }
    }
}

classe HomeControllerTest estende o TestCase
{ 
    teste de funçãoHome()
    { 
        $this->mock(AuthLogic::class, function (MockInterface $mock) { 
            $mock->deveriaReceber('autenticar')->eRetornar(verdadeiro); 
        }); 

        $response = $this->getJson('/', []); 

        $this->assertNotEquals($response->code, Response:HTTP_FORBIDDEN); 
    }
}

 

Conclusão

Agora você sabe por que, quando e como usar Injeção de Dependências no Laravel. Essa técnica também pode ser aplicada a outros frameworks, Spring Boot em Java, Ruby on Rails, Django e a maioria dos outros frameworks web têm formas de implementar essa técnica. Só lembre-se de usá-la quando for apropriado! Não exagere nas suas mockings! Testar com a coisa real sempre é necessário em algum momento!

Related Articles