Como se usa e o que resolve o recurso de Route Model Binding do Laravel

O Route Model Binding não é um recurso novo no Laravel, porém, o pessoal que está começando no framework costuma ter dificuldade em entender qual problema ele resolve e como ele funciona.

Ele existe desde a versão 4.2 do Laravel e de lá para cá já passou por algumas pequenas mudanças e da versão 5.2 em diante não teve mais o modo de usar alterado, com isso podemos chegar à conclusão de que ele alcançou uma boa maturidade.

Qual problema o Route Model Binding tenta resolver?

Antes de falarmos o que é o recurso, é importante sabermos qual problema ele resolve. É muito comum em aplicações web recebermos a partir da URL o código de um registro que vamos precisar realizar uma ação no banco de dados. Veja abaixo alguns exemplos:

http://meusistema.dev/produto/1/mostrar
http://meusistema.dev/produto/1/editar
http://meusistema.dev/produto/1/atualizar
http://meusistema.dev/produto/1/deletar

No caso acima o número 1 representa o código do produto que desejamos realizar determinada ação. Pensando no nosso código, dentro de cada uma dessas ações precisaremos buscar o registro com código 1 no banco de dados. Mas, e se baseado nesse código o próprio framework já buscasse esse registro no banco de dados e injetasse ele prontinho para ser usado, evitando a repetição de código desnecessário? É exatamente isso que o Route Model Binding faz!

Como o Laravel faz isso?

O Laravel faz isso usando o parâmetro na rota e um model do Eloquent. Veja a explicação baseado no nome do recurso:

  • Route: utiliza o nome do parâmetro especificado na rota para saber qual recursos ele deve usar;
  • Model: utiliza o model injetado na ação para realizar a busca baseado no valor passado na rota;
  • Bind: faz o bind entre o parâmetro passado na rota e a variável injetada na ação.

Esse processo por padrão é feito implicitamente, basta passar o nome do parâmetro, o model que será usado e o nome da variável que receberá a instancia do model. Existem situações onde não é possível fazer isso de forma automática, pois o valor passado na URL precisa ser filtrado de um modo diferente e não baseado na chave padrão. Nesse caso podemos fazer esse filtro explicitamente.

Implicit Binding

No modo implícito o próprio Laravel faz todo trabalho automaticamente, entregando a instância do model já corretamente populada. Se o valor passado na URL não for encontrado no banco de dados, o Laravel retorna um erro de página não encontrada, evitando assim que a execução chegue se quer à ação.

Primeira coisa que precisamos fazer é declarar o parâmetro na rota:

Route::get('products/{product}', 'ProductController@edit');

Agora, na ação do controller, basta injetarmos o model referente ao produto:

/**
 * Show the form for editing the specified resource.
 *
 * @param  \App\Product $product
 * @return \Illuminate\Http\Response
 */
public function edit(Product $product)
{
    //aqui temos o produto já filtrado na variável $product
}

Alguns detalhes importantes para evitar problemas na hora de usar o recurso:

1) O nome do parâmetro passado na rota deve ser o mesmo nome da variável injetada da ação:

//nome do parâmetro product
Route::get('products/{product}', 'ProductController@edit');

//injeção na ação
public function edit(Product $product)

2) Se quiser alterar o nome da variável precisa alterar também o nome do parâmetro e vice-versa;

3) Também deve sempre se atentar se está usando a classe correta do model na injeção do método da ação.

Explicit Binding

Existem situações onde nem tudo segue as regras para realização do bind automático. Nesse caso, podemos informar para o framework como fazer o Route Model Binding manualmente.

Essa lógica pode ser criada dentro do método boot em um service provider qualquer. Veja o exemplo abaixo:

Route::bind('product', function ($value) {
    $id = decode($value); 

    return \App\Product::where('id', $id)->first() ?? abort(404);
});

No exemplo acima estamos usando uma função hipotética chamada decode que decodifica o valor passado na URL para o ID valido do banco de dados. Poderia usar qualquer outra lógica, como filtrar por outro campo e etc.

Veja como fica essa configuração usando o RouteServiceProvider:

<?php

namespace App\Providers;

use Illuminate\Support\Facades\Route;
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;

class RouteServiceProvider extends ServiceProvider
{
    /**
     * This namespace is applied to your controller routes.
     *
     * In addition, it is set as the URL generator's root namespace.
     *
     * @var string
     */
    protected $namespace = 'App\Http\Controllers';

    /**
     * Define your route model bindings, pattern filters, etc.
     *
     * @return void
     */
    public function boot()
    {
        parent::boot();

        Route::bind('product', function ($value) {
            $id = decode($value); 

            return \App\Product::where('id', $id)->first() ?? abort(404);
        });
    }

    /**
     * Define the routes for the application.
     *
     * @return void
     */
    public function map()
    {
        $this->mapApiRoutes();

        $this->mapWebRoutes();

        //
    }

    /**
     * Define the "web" routes for the application.
     *
     * These routes all receive session state, CSRF protection, etc.
     *
     * @return void
     */
    protected function mapWebRoutes()
    {
        Route::middleware('web')
             ->namespace($this->namespace)
             ->group(base_path('routes/web.php'));
    }

    /**
     * Define the "api" routes for the application.
     *
     * These routes are typically stateless.
     *
     * @return void
     */
    protected function mapApiRoutes()
    {
        Route::prefix('api')
             ->middleware('api')
             ->namespace($this->namespace)
             ->group(base_path('routes/api.php'));
    }
}

Conclusão

Esse é mais um recurso do Laravel que nos ajuda a tornar nosso controllers muito mais limpos, legíveis e fáceis de realizar manutenção. Além de tudo isso, ele permite personalizar totalmente a lógica na hora de filtra o registro, o que o torna muito flexível.

Deixe seu comentário

Desenvolvedor, autor e instrutor. Apaixonado por desenvolvimento de software e tudo ligado a área de tecnologia. É autor de cursos em diversos temas, como, desenvolvimento back-end, cloud computing e CMSs. Nas horas vagas adora estudar sobre o mercado financeiro, cozinhar e brincar com pequeno Daniel de 1 ano.