Modal: Assinatura.php
class Subscription extends Model {
use HasFactory;
public function payments(): HasMany {
return $this->hasMany(SubscriptionPayment::class, 'sub_id');
}
public function totalDurationDays(): Attribute {
return Attribute::make(
get: fn() => $this->payments()->sum('duration_days')
)->shouldCache();
}
}
Controlador: SubscriptionController.php
$subs = SharedServer::from('subscriptions AS ss')
->select(
'ss.*',
DB::raw("(SELECT SUM(duration_days) FROM subscription_payments AS ssp WHERE ssp.sub_id = ss.id) AS total_duration_days"),
)
->paginate();
Se total_duration_days
for invocado a partir do atributo personalizado, a barra de depuração do Laravel mostra muitas consultas em comparação com a invocação direta do controlador.
Existe uma maneira de instruir o construtor de consultas a carregar rapidamente total_duration_days
atributos personalizados diretamente em uma consulta?
O importante é qual parte causa muitas consultas. Se falarmos apenas sobre atributo personalizado, então o
->payments()
causa o problema. Por quê? porque você está tentando somar a duração de dias consultando toda vez.Quais são as soluções?
Carga ansiosa:
Primeiro, você precisa usar with ou load para carregamento rápido, então você precisa usar a relação sem parênteses. Código final:
E assim como @Tarashdeep-Singh mencionou:
Mas tenha duas coisas em mente 1- Remova os parênteses e use
payment->
para relação 2- Não se esqueça de usar os métodos de carregamento ansioso como with ou load. Se você esquecer qualquer um desses dois, você terá um problema n+1.Juntar:
Esta é a maneira mais eficiente de consultar esses dados:
Isso pode não dar o resultado que você quer porque depende de como você quer filtrar o resultado, e inner join ou left join podem ser as respostas possíveis. Mas com isso, você tem uma e somente uma consulta, então é mais performático.
Subconsulta:
Subconsultas são uma maneira boa e muito limpa de consultar e, para ser honesto, também é fácil. Mas o custo é que se a subconsulta for Correlacionada, ela pode ter menos desempenho do que junções. Não incluirei o exemplo porque você já incluiu a subconsulta em sua descrição. Como você pode ver, sua subconsulta depende da
ss
tabela (subscriptions) como uma Subconsulta Correlacionada.Se você quiser usar o método totalDurationDays como um atributo personalizado e evitar múltiplas consultas, que o relacionamento de pagamentos seja carregado avidamente. Dessa forma, a soma pode ser calculada a partir da coleção de pagamentos carregada em vez de fazer uma consulta adicional por modelo de Assinatura.
Em seu subscriptioncontroller.php