Tire essas consultas da view!
Embora esse texto use como referência códigos de Laravel, o problema existe em todo lugar e deve ser evitado sempre!
Evite inserir consultas em arquivos blade, pois eles são responsáveis apenas pela camada de apresentação (View). Mover consultas para essa camada, cria acoplamentos desnecessários, sem falar que você estará espalhando lógica de negócio para tudo quanto é lado. Tenha bom senso 😺
Isso aqui é horrível
@foreach (User::all() as $user)
{{ $user->profile->name }}
@endforeach
Por que é horrível? Além do motivo que abriu a discussão do tópico, a consulta acima faz N + 1 consultas (famoso problema N + 1), ou seja, se tu for listar os perfis de 100 usuários, serão 101 consultas.
Agora imagine essa página sendo acessada por 10 mil pessoas. Esse aí seria um claro gargalo de performance.
Como melhorar isso?
// Na controller
$users = User::with('profile')->get();
// No blade (view)
@foreach ($users as $user)
{{ $user->profile->name }}
@endforeach
Veja que o blade template só sabe que existem usuários com perfis para apresentar, não sabe a regra aplicada na filtragem, ou qualquer outra regra, e… são apenas 2 consultas.
Como assim, N + 1?
Em uma visão mais simplificada do problema, tente visualizar o seguinte cenário:
- Você possui uma base com 100 usuários;
- Você vai acessar uma página que exibe o nome do perfil de cada usuário (relação 1:N);
- Por debaixo dos panos, o que o exemplo N + 1 faz é:
SELECT * FROM users; SELECT * FROM profiles WHERE user_id = 1; SELECT * FROM profiles WHERE user_id = 2; SELECT * FROM profiles WHERE user_id = 3; ... SELECT * FROM profiles WHERE user_id = 100;
Porque ele primeiro pega a referência de usuários em 1 consulta e a do perfil nas outras 100.
No segundo caso, ele faz uma consulta para retornar os usuários e uma segunda consulta que permitirá retornar os dados do perfil, dessa forma:
SELECT * FROM users;
SELECT * FROM profiles WHERE user_id in (1, 2, 3, 4, 5, ...)
Isso porque a função all vai retornar todos os usuários de uma vez, e toda vez que no loop você acessar a variável profile será feita uma nova consulta para pegar a informação de perfil do usuário. A função with usa a estratégia de eager loading, que exemplifiquei acima, por isso são realizadas apenas 2 consultas.
Fique sempre atento às estratégias que você utiliza para recuperar informações de um banco de dados. A maior parte dos problemas de performance podem ser resolvidos com boas estratégias de cache, e melhor execução das consultas.
PS: O texto da fonte é de minha autoria