Posted on: July 26, 2023 04:20 PM
Posted by: Renato
Views: 429
Laravel Eloquent Eager Loading
O que é carregamento ansioso?
O carregamento antecipado é um conceito no qual, ao recuperar itens, você obtém todos os itens necessários junto com todos (ou a maioria) itens relacionados ao mesmo tempo. Isso contrasta com o carregamento lento , no qual você obtém apenas um item de uma só vez e, em seguida, recupera os itens relacionados somente quando necessário.
Para colocá-lo em uma perspectiva mais de banco de dados; digamos que você tenha um banco de dados contendo duas tabelas — perguntas e opções. Todas as opções estão relacionadas a perguntas através da question_id
coluna sobre elas e, claro, uma pergunta tem várias opções. Precisamos recuperar nossas perguntas do banco de dados para serem exibidas na exibição. Há duas maneiras de fazermos isso:
Modo 1: Pegamos as perguntas, começamos a percorrê-las e, para cada pergunta, vamos ao banco de dados para recuperar as opções relacionadas. *Isso é carregamento lento* ou seja, estamos atrasando a recuperação das opções até que precisemos delas.
Caminho 2: Diretamente do banco de dados, pegamos as questões e buscamos as opções associadas. Para que, ao exibir as perguntas, não façamos viagens separadas ao banco de dados para buscar as opções, mas exibimos as opções que já existem nas perguntas. E isso, é *Eager loading*
Deste ponto em diante no artigo, falarei em termos mais do Laravel, mas ainda manterei a ideia o mais genérica possível, então, vamos lá!
O carregamento antecipado nos ajuda a resolver bem o problema de consulta N+1. Vejamos uma ilustração simples usando nossas questões anteriores e dados de opções.
Vamos supor que temos um modelo eloquenteQuestion
<?phpnamespace App;use Illuminate\Database\Eloquent\Model;class Question extends Model{ public function options(){ return $this->hasMany(Option::class); } }
.
And an Option
model:
<?php namespace App' use Illuminate\Database\Eloquent\Model;class Option extends Model{ public function question(){ return $this->belongsTo(Question::class); } }
Vamos supor novamente que temos 20 registros em nossa tabela de perguntas, para obter todas as perguntas, tudo o que precisamos fazer é:
$questions = Question::all();
Então, para obter todas as opções associadas a uma pergunta, faríamos algo como:
foreach ($questions as $question){
echo $question->option->count() . "<br>";
}
Isso é o que acontecerá com o cenário acima:
A consulta será executada uma vez pela primeira vez para obter as 20 perguntas e, em seguida, mais 20 consultas para obter as opções associadas. Isso totaliza 21 consultas, ou seja, 20(N) + 1
Isso é praticamente um carregamento lento.
Porém, com carregamento antecipado, para obter as 20 questões e suas opções, necessitamos apenas de duas consultas, que podem ser realizadas da seguinte forma:
$questions = Question::with('options')->get();foreach ($questions as $question){ echo $question->option->count(); }
Ao usar o with
método, carregamos simultaneamente todas as 20 perguntas junto com suas opções. Para melhor compreensão, a consulta SQL resultante se parece com isto:
select * from questions
select* from options where question_id IN (1, 2, 3, 4, 5 ... 20)
onde (1, 2, 3 ... 20)
estão os ids das perguntas em questão.
Com o carregamento antecipado, economizamos as viagens de ida e volta para acessar o banco de dados, o que é realmente bom para a integridade de nosso aplicativo e servidor.
Com o laravel, podemos até carregar vários relacionamentos em uma chamada.
Vamos supor que temos outro relacionamento em nosso modelo de pergunta, por exemplo, Categoria
public function category(){
return $this->belongsTo(Category::class);
}
Em uma única chamada, podemos obter as perguntas, juntamente com as opções associadas e as categorias, e isso pode ser feito como:
$questions = Question::with(['options', 'category'])->get();
Sem que nosso código precise acessar o banco de dados várias vezes, podemos acessar os relacionamentos facilmente
foreach ($questions as $question){
echo "Category name is {$question->category->name} <br>";
foreach ($question->options as $option){
echo "Option is {$option->text} <br>";
}
}
Laravel ainda torna nossas vidas mais fáceis, dando-nos a chance de carregar relacionamentos aninhados. isto é, queremos carregar um relacionamento do relacionamento que estamos carregando. Por exemplo, supondo que o relacionamento de categoria de nossas perguntas esteja relacionado a um autor, podemos reunir as perguntas com a categoria relacionada (e, em seguida, criar o autor fazendo o seguinte):
$questions = Question::with('category.author')->get();
Aqui está um link para a documentação do laravel sobre carregamento ansioso
- https://laravel.com/docs/5.5/eloquent-relationships#eager-loading
Algumas notas pessoais:
Por mais que o carregamento antecipado pareça uma coisa muito boa de se fazer, nem sempre é uma solução para todos os casos.
Alguns prós e contras do carregamento antecipado:
Prós
- Você obtém tudo o que precisa de uma só vez
- Não há atraso ao acessar dados durante o uso pelos usuários
- Menos consultas para acessar o banco de dados
Contras
- Você pode estar obtendo dados de que não precisa
- A consulta inicial é lenta e pode ser ruim para os casos em que você precisa obter * muitos * registros de uma só vez.
- Claro, usa mais memória.
Um exemplo que estou trabalhando:
Para juntar essas duas consultas e buscar todas as informações necessárias em uma única consulta, você pode utilizar o método with()
para incluir as relações de companiesCredential
, companySegment
, e TourismSegmentation
em uma única chamada.
Aqui está como fazer isso:
php
use App\Models\Company;
// ... $company = Company::where('cnpj', $cnpj) ->with(['companiesCredential', 'companySegment.TourismSegmentation']) ->first();
Neste exemplo, usamos a função with()
para incluir as relações. Observe que utilizamos a notação de ponto (.
) para indicar que queremos buscar a relação TourismSegmentation
dentro da relação companySegment
.
Após executar essa consulta, você terá todos os dados relacionados disponíveis no objeto $company
. Você pode acessá-los da seguinte maneira:
php
if ($company) {
// Acesso aos dados da tabela "companies_credential" relacionada $credentials = $company->companiesCredential;
// Acesso aos dados da tabela "company_segment" relacionada $segment = $company->companySegment;
// Acesso aos dados da tabela "tourism_segmentation" relacionada através de "companySegment" $tourismSegmentation = $segment->TourismSegmentation;
}
Dessa forma, você evita realizar múltiplas consultas ao banco de dados e obtém todos os dados necessários para a empresa e seus segmentos em uma única consulta otimizada.
No entanto, posso explicar que o método with()
que mencionei é uma funcionalidade conhecida como "Eager Loading" no Laravel.
O Eager Loading é uma técnica utilizada para otimizar o carregamento de relacionamentos em um conjunto de dados, evitando o problema do "N+1". Em vez de carregar os relacionamentos sob demanda à medida que você acessa os dados relacionados, o Eager Loading permite que você recupere todos os dados relacionados com uma única consulta SQL.
No Laravel, o método with()
é utilizado para especificar quais relacionamentos devem ser carregados antecipadamente para uma consulta Eloquent. Ele aceita argumentos como strings ou arrays associativos que representam os nomes das relações a serem carregadas.
Exemplo com relação simples:
php
$posts = Post::with('comments')->get();
Exemplo com múltiplas relações:
php
$users = User::with(['posts', 'comments'])->get();
Exemplo com relação aninhada:
php
$users = User::with('posts.comments')->get();
Essa técnica é muito útil para melhorar o desempenho de consultas que envolvem muitas relações e ajuda a evitar consultas excessivas ao banco de dados.
Para encontrar mais informações sobre o Eager Loading e o método with()
no Laravel, você pode procurar por "Laravel Eager Loading" ou "Laravel with() method" em motores de busca como o Google. Dessa forma, você encontrará uma série de tutoriais, artigos e documentações oficiais do Laravel que explicam esse recurso em detalhes.
Isso será tudo de mim, espero que você goste do artigo.
Donate to Site
Renato
Developer