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.

Laravel - Desenvolvimento de APIs REST
Curso de Laravel - Desenvolvimento de APIs REST
CONHEÇA O CURSO

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 instância 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.

Laravel - Framework PHP (Parte 1/3)
Curso de Laravel - Framework PHP (Parte 1/3)
CONHEÇA O CURSO

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 sequer à 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  AppProduct $product
 * @return IlluminateHttpResponse
 */
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 AppProduct::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 válido 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 AppProviders;

use IlluminateSupportFacadesRoute;
use IlluminateFoundationSupportProvidersRouteServiceProvider 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 = 'AppHttpControllers';

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

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

            return AppProduct::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 filtrar o registro, o que o torna muito flexível.

Caso queira continuar seus estudos em Laravel e Eloquent, temos artigos aqui no blog ensinando a usar escopos do Eloquent para construir consultas mais limpas no Laravel, como remover a lógica das views com presenters no Laravel e como aliviar seus controllers com os eventos do Eloquent no Laravel, confira!

Desenvolvedor Laravel Full-Stack
Formação: Desenvolvedor Laravel Full-Stack
Nesta formação você aprenderá desenvolver aplicações PHP usando o framework Laravel com maestria. Ao final desta formação, você terá condições de trabalhar em grandes aplicações web ou APIs integradas com diversos serviços, tudo isso utilizando as melhores práticas do mercado.
CONHEÇA A FORMAÇÃO
Deixe seu comentário

Professor e desenvolvedor. Formado em análise e desenvolvimento de sistema, pós graduando em engenharia e arquitetura de software. É 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. @eltonfonsecadev

© 2004 - 2019 TreinaWeb Tecnologia LTDA - CNPJ: 06.156.637/0001-58 Av. Paulista, 1765, Conj 71 e 72 - Bela Vista - São Paulo - SP - 01311-200