Laravel Eloquent/MySQL 如何从每个父记录的最后一个条目中获取去年的记录?
Posted
技术标签:
【中文标题】Laravel Eloquent/MySQL 如何从每个父记录的最后一个条目中获取去年的记录?【英文标题】:Laravel Eloquent/MySQL how to get one last year records from last entry of each parent record? 【发布时间】:2022-01-06 22:36:25 【问题描述】:假设我在遗留系统中有以下关系,无法改变它。
-
省表(有很多区)
地区表(有许多村庄)
村表(有很多医院)
hospital_types 表(hasOne 医院)
hospitals 表(属于 hospital_types,hasMany 监控)
监控表(hasMany rateLog)
我想要的是带医院的省份,并在去年对每家医院进行一次监测。
注意:上一年不是从当前日期算起,而是取每家医院的最后一次监测日期,然后从那个日期开始计算,到那个日期为止需要12个月
假设医院 A 已于 2020 年 11 月 1 日进行最后一次监测,则应在 2020 年 11 月 1 日至 2019 年 11 月 1 日期间完成所有监测。其他医院也一样。
已经用 Laravel 资源实现了,但是有点慢,也不知道在资源返回数据后如何实现 orderBy 功能,因为我需要基于监视的分页和排序,现在我通过资源处理它.
目前我正在使用 hasManyThrough 关系,在 get 之后,我将数据传递给资源集合并处理监控的 get。但是这样就不能实现排序和分页了。
$data = Province::with(['districts.hospitalTypes])->get();
然后我将这些数据传递给资源并获得监控,但是我如何应用排序和分页,因为排序是基于监控的。并且使用资源比急切加载要慢一些。 我正在寻找一种方法来使用带有这些条件的急切负载。
class ProvinceResource extends JsonResource
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
return [
'id' => $this->id,
'name' => $this->name,
'districts' => DistrictResource::collection($this->whenLoaded("districts"))
];
class DistrictResource extends JsonResource
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
return [
'id' => $this->id,
'name' => $this->name,
'hospitals' => HospitalTypeResource::collection($this->whenLoaded("hospital_types"))
];
class HospitalTypeResource extends JsonResource
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
$district = $this->village->district;
$oneLastYearRateLog = $this->hospital->last12MonthRageLogs();
$rateLogCount = $oneLastYearRateLog->count();
$oneLastYearMonitoringCount = $this->hospital->last12MonthMonitors()->count();
return [
"id" => $this->hospital->id,
"code" => $this->code,
"name" => $this->name,
"overallRating"=> $rateLogCount == 0 ? 0 : $oneLastYearRateLog->sum('rate') / $rateLogCount,
"ratingsCount" => $oneLastYearMonitoringCount
];
下面是我在医院模型中采取监控的关系。
class Hospital extends Model
/**
* Get hospital monitors
*/
public function monitors()
return $this->hasMany(Monitoring::class, 'hospital_id', 'id');
public function last12MonthMonitors()
$lastMonitoring = $this->lastMonitoring();
if($lastMonitoring)
return $this->monitors()->whereRaw('monitoring_date >= "'. $lastMonitoring->monitoring_date.'" - INTERVAL 12 month');
return $this->monitors();
private function lastMonitoring()
return $this->monitors()->select('monitoring_date')->latest('monitoring_date')->first();
/**
* Get rate logs
*/
public function rateLogs()
return $this->hasManyThrough(RateLog::class, Monitoring::class, 'hospital_id')
->where('rate', '!=', 'NA');
/**
* Get last 12 rate logs
*/
public function last12MonthRageLogs()
$lastMonitoring = $this->lastMonitoring();
if($lastMonitoring)
return $this->rateLogs()->whereRaw('monitoring.monitoring_date >= "'.$lastMonitoring->monitoring_date.'" - INTERVAL 12 month');
return $this->rateLogs();
【问题讨论】:
请您显示您的Resource
的代码以及您如何过滤监控。
@Rwd 请立即查看
你使用的是什么版本的 Laravel?
@Rwd Laravel 7,但可以升级到 8
你能分享模型的表格结构吗?请将表结构分享为 github gist
【参考方案1】:
您似乎并不急于加载hospital
关系。我将首先添加它,这样您就不必为每个 HospitalType
运行单个查询:
$data = Province::with(['districts.hospitalTypes.hospital'])->get();
我相信您查询的速度可能会慢一点的原因之一是您的lastMonitoring
检查。首先,这个查询将针对每个 Hospital
运行,其次,您似乎没有存储/缓存该值,因此它实际上将被查询两次。
如果您更新到 Laravel 8(最低 v8.4.2),您将能够为您的 lastMonitoring
方法使用 Has one of many relationship,例如:
public function latestMonitoring()
return $this->hasOne(Monitoring::class)->latestOfMany('monitoring_date');
那么您也可以预先加载该关系:
$data = Province::with(['districts.hospitalTypes.hospital.latestMonitoring'])->get();
您还需要更新 HospitalType
模型中的一些其他方法:
public function monitors()
return $this->hasMany(Monitoring::class, 'hospital_id', 'id');
/**
* Get rate logs
*/
public function rateLogs()
return $this->hasManyThrough(RateLog::class, Monitoring::class, 'hospital_id')
->where('rate', '!=', 'NA');
public function latestMonitoring()
return $this->hasOne(Monitoring::class, 'hospital_id', 'id')->latestOfMany('monitoring_date');
public function last12MonthMonitors()
return $this->monitors()->when($this->latestMonitoring, function ($query)
$query->where('monitoring_date', '>=', $this->latestMonitoring->monitoring_date->subYear());
);
/**
* Get last 12 rate logs
*/
public function last12MonthRageLogs()
return $this->rateLogs()->when($this->latestMonitoring, function ($query)
$query->where('monitoring.monitoring_date', '>=', $this->latestMonitoring->monitoring_date->subYear());
);
【讨论】:
感谢您的帮助,升级 Laravel 和 php 后,实现了您的解决方案,但它不起作用。问题是它永远不会进入when($this-> latestMonitoring
,因为它总是返回null。
@jones 啊,当然。对于那个很抱歉!我已经更新了latestMonitoring
关系以使用相同的foreignKey
和localKey
(hostpital_id
和id
)作为现在应该可以工作的monitors
关系。
@Jones 在last12MonthMonitors
和last12MonthRageLogs
中仅适用于延迟预加载,还是其他关系仅适用于延迟预加载?以上是关于Laravel Eloquent/MySQL 如何从每个父记录的最后一个条目中获取去年的记录?的主要内容,如果未能解决你的问题,请参考以下文章
如何将 App\Exceptions 从 laravel 7 升级到 laravel 8
如何在 Laravel 8 中使用 srmklive/laravel-paypal v3