一个度量模型有多个测量值。
我已经建立了关系,因此我可以执行以下操作:
$metrics = Metric::with('measurements' => function($query) {
return $query->where('timestamp', '>=', '20190731');
});
这可以获取 2019 年 7 月 31 日以来的所有测量结果。
但是我现在想要实现的是获取聚合的测量数据。
编辑:这是我所追求的更好的解释
如果上面给出类似:
results = [
{ id: 1,
name: 'Metric 1',
measurements: [
{ id: 123, metric_id: 1, value: 4, timestamp: 2019-07-31T09:00 },
{ id: 124, metric_id: 1, value: 10, timestamp: 2019-07-31T10:00 },
...
{ id: 124, metric_id: 1, value: 10, timestamp: 2019-08-01T09:00 },
] },
...
]
那么我想要实现的结果就是:;
results = [
{ id: 1,
name: 'Metric 1',
measurementStats: [
{ metric_id: 1, period: 2019-07-31, min: 4, max: 10 },
{ metric_id: 1, period: 2019-08-01, min: 10, max: 10 },
] },
...
]
我想当急切加载时,Eloquent 应该运行这样的东西:
SELECT metric_id,
DATE_FORMAT(timestamp, '%Y%m%d') as period,
MIN(value) AS min,
MAX(value) AS max
FROM measurements
WHERE metric_id IN (...)
GROUP BY metric_id, DATE_FORMAT(timestamp, '%Y%m%d')
有没有办法在 Eloquent/Laravel 查询生成器中实现这一点? (Laravel 5.8)
您可以通过向模型添加新属性(测量值的平均值)来实现。
将以下行添加到您的公制模型中。
protected $appends = array('min', 'max');
public function getMinAttribute()
{
return $this->measurements()->where('timestamp', '>=', '20190731')->min('value');
}
public function getMaxAttribute()
{
return $this->measurements()->where('timestamp', '>=', '20190731')->max('value');
}
现在,您可以像普通模型属性一样访问最小值和最大值,
foreach($metrics as $metric){
// $metric->max
// $metric->min
}
希望你能理解。
我曾希望使用 Laravel 自己的热切加载,但使用从我的统计查询派生的“模型”,但我认为这是不可能的; Laravel 的急切加载与 Eloquent 模型过于紧密相关。不过,我确实找到了这个解决方案:
我们将:
创建一个measurementStats属性(创建公共属性的一种非常冗长的方法!)
告诉模型使用自定义集合而不是普通的 Eloquent 集合。
告诉模型在将其自身转换为 JSON 时附加 stats 属性。
应用程序/Metric.php:
namespace App;
use App\MetricCollection;
...
class Metric
{
...
protected $appends = ['measurementStats'];
/** @var array */
protected $measurementStats;
public function getMeasurementStatsAttribute() {
return $this->measurementStats ?? [];
}
public function setMeasurementStatsAttribute($stats) {
$this->measurementStats = $stats;
}
public function newCollection(array $models=[]) {
return new MetricCollection($models);
}
这将使我们能够在一个查询中加载所有测量统计数据并将它们附加到模型中。
namespace App;
use \Illuminate\Database\Eloquent\Collection;
use \DB;
class MetricCollection extends Collection
{
public function addMeasurementStats($filters) {
$metric_ids = $this->modelKeys();
$stats_query = DB::table('measurements')
->select('metric_id')
->selectRaw("DATE_FORMAT(timestamp, '%Y%m%d') as period")
->selectRaw("MIN(value) AS min")
->selectRaw("MAX(value) AS max")
->selectRaw("AVG(value) AS value")
->whereIn('metric_id', $metric_ids)
->groupBy('metric_id', 'period');
if (!empty($filters['since'])) {
$stats_query->where('timestamp', '>=', $filters['since']);
}
foreach ($stats_query->get()->groupBy('metric_id') as $metric_id => $stats) {
$this->find($metric_id)->measurementStats = $stats;
}
return $this;
}
}
$data = Metric::all()->addMeasrementStats(['since' => '2019-07-31']);