【问题标题】:Laravel Eloquent/MySQL how to get one last year records from last entry of each parent record?Laravel Eloquent/MySQL 如何从每个父记录的最后一个条目中获取去年的记录?
【发布时间】:2022-01-06 22:36:25
【问题描述】:

假设我在遗留系统中有以下关系,无法改变它。

  1. 省表(有很多区)
  2. 地区表(有许多村庄)
  3. 村表(有很多医院)
  4. hospital_types 表(hasOne 医院)
  5. hospitals 表(属于 hospital_types,hasMany 监控)
  6. 监控表(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

标签: php mysql laravel groupwise-maximum


【解决方案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 关系以使用相同的foreignKeylocalKeyhostpital_idid)作为现在应该可以工作的monitors 关系。
  • @Jones 在last12MonthMonitorslast12MonthRageLogs 中仅适用于延迟预加载,还是其他关系仅适用于延迟预加载?
猜你喜欢
  • 2017-09-18
  • 2019-06-23
  • 2020-10-01
  • 2012-02-13
  • 2019-12-28
  • 2022-10-14
  • 1970-01-01
  • 2019-02-21
  • 2022-11-17
相关资源
最近更新 更多