Laravel Eloquent/MySQL 如何从每个父记录的最后一个条目中获取去年的记录?
Laravel Eloquent/MySQL how to get one last year records from last entry of each parent record?
假设我在遗留系统中有以下关系,无法更改它。
- 省table(有许多区)
- 地区table(有许多村庄)
- 村庄table(有许多医院)
- hospital_types table(有一家医院)
- 医院table(属于hospital_types,有很多监控)
- 监控table(hasMany rateLog)
我要的是带医院的省份,每家医院做一个去年的监测。
注:最后一年不是从现在算起,意思是取每家医院最后一次监测的日期,然后从那个日期算起,到那个日期需要12个月
假设A医院最后一次监测是在2020-11-01,那么应该把2020-11-01到2019-11-01之间的所有监测都拿去。其他医院也一样。
已经用Laravelresource实现了,但是有点慢,也不知道resource返回数据后怎么实现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();
}
您似乎并不急于加载 hospital
关系。我会首先添加这个,这样你就不必 运行 每个 HospitalType
:
的单个查询
$data = Province::with(['districts.hospitalTypes.hospital'])->get();
我相信您查询的原因之一可能是运行宁慢一点是因为您的lastMonitoring
检查。首先,此查询将为每个 Hospital
获取 运行,其次,您似乎不是 storing/caching 该值,因此实际上将查询两次。
如果您更新到 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());
});
}
假设我在遗留系统中有以下关系,无法更改它。
- 省table(有许多区)
- 地区table(有许多村庄)
- 村庄table(有许多医院)
- hospital_types table(有一家医院)
- 医院table(属于hospital_types,有很多监控)
- 监控table(hasMany rateLog)
我要的是带医院的省份,每家医院做一个去年的监测。
注:最后一年不是从现在算起,意思是取每家医院最后一次监测的日期,然后从那个日期算起,到那个日期需要12个月
假设A医院最后一次监测是在2020-11-01,那么应该把2020-11-01到2019-11-01之间的所有监测都拿去。其他医院也一样。
已经用Laravelresource实现了,但是有点慢,也不知道resource返回数据后怎么实现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();
}
您似乎并不急于加载 hospital
关系。我会首先添加这个,这样你就不必 运行 每个 HospitalType
:
$data = Province::with(['districts.hospitalTypes.hospital'])->get();
我相信您查询的原因之一可能是运行宁慢一点是因为您的lastMonitoring
检查。首先,此查询将为每个 Hospital
获取 运行,其次,您似乎不是 storing/caching 该值,因此实际上将查询两次。
如果您更新到 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());
});
}