我构建了一个应用程序来跟踪销售情况。在我的客户视图中,我想要一个每个客户的总销售额的列,但随着客户群的增长,列表的加载速度越来越慢。这就是我所做的(简化):
控制器:
$customers = App\Customer::get();
视图:
@foreach ($customers as $customer)
{{ $customer->name }} {{ $customer->totalSales() }}
@endforeach
模型:
public function totalSales()
{
$invoiceLines = InvoiceLine::whereHas('invoice', function ($query) {
$query->where('customer_id', $this->id);
})->get();
$sales = $invoiceLines->reduce(function ($carry, $invoiceLine) {
return $carry + ($invoiceLine->quantity * $invoiceLine->pricePerUnit);
});
return $sales ?: 0;
}
什么是使这种观点/报告更具“可扩展性”的最佳方法?
我一直在考虑创建命令来计算每个客户的总销售额,并将结果放在客户表中,但这意味着这些数字在白天不准确...
这似乎是一个非常有趣的问题。
我一直在考虑创建命令,计算每个客户的总销售额,并将结果放在客户表中
这是一个不错的选择。
但这意味着这些数字在白天不准确......
您可以通过执行以下操作来保持数字的准确性:通过在每次发票时递增客户表计数。
这应该适用于总销售额。
customer_id
列上有索引。SUM
”的方法。SUM
2上的SQL GROUP BY
。这样做将取代#2
加速应用程序的一个好方法是避免在循环中调用数据库。这就是#3的建议(在这种情况下,循环是你视图中的@foreach
,数据库调用是InvoiceLine::...->get();
中的totalSales()
)添加索引(如果缺少)并减少对DB的调用次数将产生最佳结果。
我对Laravel的了解有限,但使用原始SQL的一种方法是:
SELECT c.name, ts.totalSales
FROM customer c
INNER JOIN (
SELECT customer_id, SUM(quantity * pricePerUnit) as totalSales
FROM invoice
GROUP BY customer_id
) ts ON c.id = ts.customer_id
您可以看到您尝试打印的所有数据是如何同时提取的?我假设你想尝试使用Laravel的Eloquent thingy来编写它。
基于上面的答案,我得出以下解决方案:
我创建了一个事件:App\Events\InvoiceSaved
,每次发票被“触摸”(创建,更新或删除)时都会被调度。 App\Events\InvoiceSaved
事件将计算客户的总销售额并将结果添加到customers表(额外字段total_sales)。现在我可以查询我的customers表并需要查询关系。加载时间从7秒降至0.5秒!