Usando “before_action” no Laravel

Estive mergulhando no mundo de RoR (Ruby on Rails) recentemente e notei uma feature que me chamou muita a atenção, a before_action;

before_action `your_method`, only: ['methodOne', 'methodTwo']

O que ele basicamente faz é executar um method X, no caso “your_method”, antes das funções “methodOne” e “methodTwo” serem usadas, o beneficio disso é simples, suponhamos que você queira buscar um registro no banco de dados com o ID, sempre que as funções show, edit e destroy forem chamadas, normalmente suas funções no Laravel ficariam mais ou menos assim;

public function show($id) { $client = Client::find($id); }
public function edit($id) { $client = Client::find($id); }
public function destroy($id) { $client = Client::find($id); }
public function update($id) { $client = Client::find($id); }

Agora suponha que exista uma função que funcione como um “middleware”, e faça uma verificação sempre que a sua classe for chamada para saber qual o método sera usado, e se for algum que utilize um cliente baseado em uma busca no banco com o ID, ele procura no banco de dados antes que a função seja usada, bem interessante não acha?

Bom, para fazer isso preciso saber das seguintes coisas;

  • Nome da rota
  • Nome do método
  • Parâmetro passado (ID)

Para este exemplo vou usar a nomenclatura que o Laravel usa por padrão, nas minhas rotas eu adicionei a seguinte linha:

Route::resource(`clients`, `ClientController`);

Esta rota vai gerar o seguinte padrão:

| Verb    Path                        Action  Route Name
| GET /clients index clients.index
| GET /clients/create create clients.create
| POST /clients store clients.store
| GET /clients/{id} show clients.show
| GET /clients/{user}/edit edit clients.edit
| PUT /clients/{user} update clients.update
| DELETE /clients/{user} destroy clients.destroy

Note que o nome das rotas sempre possuem um “.” (ponto), sabendo disso podemos usar a classe Request para pegar este nome dentro da função _construct()

public function __construct(Client $client) 
{
  $this->client = $client; # Dependency injection
  $route_method = explode('.', Request::route()->getName());
// Resulta em: array [0 => 'clientes', 1 => 'show']
}

Com o método route()->getName() da classe Request eu consigo saber qual o nome da rota sendo usada no momento e com o explode() eu consigo separar o nome do método da rota.

Porém ainda não definimos quais os métodos terão esse “middleware” antes de serem chamados, então criei um atributo com todos os nomes:

protected $routes = ['show', 'update', 'edit', 'destroy'];

Ótimo, agora que já sei o nome da rota e já sei quais funções eu quero verificar eu posso utilizar a função nativa do PHP in_array() para saber se o nome da rota esta nas rotas que eu pre-defini.

public function __construct(Client $client) 
{
$this->client = $client;
   $route_method = explode('.', Request::route()->getName());
// Resulta em: array [0 => 'clientes', 1 => 'show']
   if (in_array(end($route_method), $this->routes)) {
...
}
}

> A função end() pega o ultimo valor da ultima chave do array

Agora que consigo saber se a rota que esta sendo usada é uma das que eu quero, preciso saber qual foi o ID passado pela uri , e consigo pegar este parâmetro com Request::route()->getParameter(`id`) , afim de deixar o código bem legível criei uma função para buscar o client pelo ID, fora do construtor.

public function set_client($id)
{
   $this->client = $this->client->find($id);
}

O código completo ficara da seguinte maneira;

use Illuminate\Support\Facades\Route;
class ClientController extends Controller {
public $client;
protected $routes = ['show', 'update', 'edit', 'destroy'];
public function __construct(Client $client) 
{
$this->client = $client;
   $route_method = explode('.', Request::route()->getName());
   if (in_array(end($route_method), $this->routes)) {
$this->set_client(\Request::route()->getParamater('id'));
}
}
/**
* Find the client if parameter ID was sended
*
* @return void
*/
public function set_client($id)
{
$this->client = $this->client->find($id);
}
...

Pronto! agora quando utilizar os métodos show, update, edit e destroy você pode utilizar o atributo $this->client pois ele sempre terá um objeto cliente com o ID passado pela rota e assim você não precisa usar em todas as funções o $client = Client::find($id) .

Show your support

Clapping shows how much you appreciated Rafa’s story.