Laravel: filtrando queries utilizando scopes
É comum aparecer situações em que precisamos repetir trechos de códigos para fazer filtros em um projeto. Para resolver esse tipo de problema, o Laravel (mais precisamente, o Eloquent) possui um recurso para filtrar de forma “automágica” as consultas.
Já pararam pra pensar como uma collection do Laravel trata os soft deletes de forma automática? É utilizado um recurso chamado scope, que serve exatamente para isso, definir um filtro que remove todos os resultados com deleted_at preenchido.
Algo parecido com isso:
Assim sempre vamos retornar apenas os usuários não excluídos, sendo possível chamar diretamente:
Ok, mas, como podemos usar o scope para personalizar outros filtros? Por exemplo, no caso de produtos, vamos trabalhar nisso e ver o que conseguimos.
Existem duas maneiras para isso: Local Scopes e Global Scopes.
Local Scopes
Local Scopes podem ser chamados dinamicamente pela sua aplicação. Não seria exatamente o caso de um deleted_at. Mas, ajuda quando o filtro é necessário apenas em alguns pontos do sistema. É perfeito para situações em que temos estados, como ativo/inativos…
Para adicionar um filtro com scope precisamos apenas criar um método na model iniciando o nome scope. Esse método receberá NO MÍNIMO o parâmetro Builder da query e também retornará um objeto da classe Builder.
Assim quando for necessário consultar apenas os produtos ativos, podemos usar a sintaxe abaixo:
(Repare que aqui o método foi chamado apenas pelo nome APÓS o scope utilizado na declaração do método.)
Bem mais prático e claro do que a versão sem scope:
Repare que falei logo acima que recebe NO MÍNIMO?
O scope pode receber N parâmetros após a $query, podemos fazer scopes para coletar por status de forma dinâmica, por exemplo:
O uso do status vira um parâmetro do scope criado:
Nesse formato podemos ter quantos parâmetros forem necessários para a realidade do projeto, ali utilizei apenas o primeiro parâmetro como string. Reparar que o Builder do primeiro parâmetro é suprimido na utilização da consulta.
- Eita… Massa, hein? Mas, por exemplo, em uma loja cada User teria os próprios Products… Vou SEMPRE colocar a chamada de método para o scope?
- Não!
Para os casos em que SEMPRE (ou quase sempre) vamos utilizar um scope, existe um recurso chamado Global Scope, que vamos ver agora.
Global Scopes
Ao contrário do Local Scope, o Global Scope não precisa ser chamado, o uso fica implícito em cada consulta, exatamente como o deleted_at que já é nativo do Laravel.
Quando criamos Global Scope, esse scope recebe dois parâmetros: o primeiro é o nome do scope, e no segundo é uma função que recebe e retorna o $builder para que seja manipulada a consulta.
Como, por exemplo, para pegar todos os produtos do usuário logado no sistema:
Para fazer consultas com esse formato é bem simples… Simplesmente ignoramos que o scope existe e é só realizar a consulta normalmente!
Fácil, não?
O Primeiro parâmetro que ali foi denominado UserProducts pode ser qualquer nome, serve para manipular o uso, assim como é adicionado, podemos ignorar o scope em alguma consulta:
Dessa forma, coletamos todos os produtos ignorando o scope, vindo produtos de todos os usuários.
Porém, da forma que foi adicionado ali, pode ser meio confuso, ainda mais se a função para manipular o scope for muito grande. Para isso podemos criar uma classe separada. Por exemplo:
Arquivo do scope:
Model utilizando a classe do scope:
Assim a classe da model fica mais organizada, limpa e os scopes podem ficar em uma estrutura separada, sendo possível até ser reutilizado. Repare que o nome do scope é genérico, qualquer model que trabalhe com o user_id pode consumir esse mesmo scope.
No caso de um scope em classe separada, podemos usar próprio nome para ignorar o seu uso:
Podemos inclusive, misturar os usos, com local e global scope, como no exemplo abaixo:
Assim podemos consultar todos os produtos ativos apenas do usuário logado:
Bem melhor do que repetir os filtros abaixo em cada consulta, não é mesmo?
Concluindo, os scopes do Laravel ajudam a remover repetições, deixam o código mais legível e ainda centralizam filtros comuns, ajudando na manutenção do código. Ficou com alguma dúvida? Deixa aqui nos comentários e até a próxima!
Aproveite que já ta por aqui e confira outros dos nossos tutoriais aqui no blog.