#php #mysql #laravel #groupwise-maximum
#php #mysql #laravel #groupwise-максимум
Вопрос:
Предположим, у меня есть следующие отношения в устаревшей системе, которые не могут ее изменить.
- таблица провинций (имеет множество районов)
- таблица районов (много деревень)
- таблица деревень (много больниц)
- таблица hospital_types (имеет одну больницу)
- таблица больниц (принадлежит hospital_types, мониторинг hasMany)
- таблица мониторинга (hasMany rateLog)
Я хочу взять провинции с больницами и провести мониторинг каждой больницы за последний год.
Примечание: один прошлый год означает не текущую дату, это означает, что мы должны взять дату последнего мониторинга каждой больницы, а затем рассчитать с этой даты, чтобы занять 12 месяцев до этой даты
Предположим, что больница А в последний раз отслеживалась в 2020-11-01 годах, весь мониторинг должен проходить в период с 2020-11-01 по 2019-11-01. И то же самое для других больниц.
Уже реализовано с помощью Laravel resource, но это немного медленно, а также не знаю, как мы можем реализовать функцию OrderBy после того, как ресурс вернул данные, потому что мне нужна разбивка на страницы и сортировка на основе мониторинга, который теперь я обрабатываю с помощью resource.
В настоящее время я использую отношение hasManyThrough, и после получения я передаю данные в сбор ресурсов и обрабатываю получение мониторинга. Но при этом невозможно реализовать сортировку и разбивку на страницы.
$data = Province::with(['districts.hospitalTypes])->get();
Затем я передаю эти данные ресурсу и получаю мониторинг, но как я могу применить сортировку и разбивку на страницы, потому что сортировка основана на мониторинге. И использование ресурса немного медленнее, чем быстрая загрузка.
Я ищу способ использовать нетерпеливую загрузку с учетом этих критериев.
class ProvinceResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param IlluminateHttpRequest $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 IlluminateHttpRequest $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 IlluminateHttpRequest $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();
}
Комментарии:
1. Пожалуйста, не могли бы вы показать код для вашего
Resource
и как вы фильтруете мониторинг.2. @Rwd Пожалуйста, проверьте сейчас
3. Какую версию Laravel вы используете?
4. @Rwd Laravel 7, но открыт для обновления до 8
5. Можете ли вы поделиться структурой таблиц моделей? Пожалуйста, поделитесь структурой таблицы в качестве github gist
Ответ №1:
Похоже, вы не хотите загружать hospital
отношения. Я бы начал с добавления этого, чтобы вам не приходилось запускать один запрос для каждого HospitalType
:
$data = Province::with(['districts.hospitalTypes.hospital'])->get();
Я считаю, что одна из причин, по которой ваш запрос может выполняться немного медленнее, связана с вашей lastMonitoring
проверкой. Во-первых, этот запрос будет выполняться для каждого Hospital
, а во-вторых, вы, похоже, не сохраняете / кэшируете это значение, поэтому на самом деле это будут запросы дважды.
Если вы обновитесь до Laravel 8 (минимальная версия 8.4.2), вы сможете использовать отношение Has one of many для вашего lastMonitoring
метода, например:
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());
});
}
Комментарии:
1. Спасибо за вашу помощь, после обновления Laravel и PHP реализовал ваше решение, но оно не работает. Проблема в том, что она никогда не попадает внутрь
when($this-> latestMonitoring
, потому что она всегда возвращает null.2. @jones Ах, конечно. Извините за это! Я обновил
latestMonitoring
отношение, чтобы использовать то жеforeignKey
самое иlocalKey
(hostpital_id
иid
),monitors
что и отношение, которое теперь должно работать.3. @Jones Как в
last12MonthMonitors
иlast12MonthRageLogs
работает только с отложенной загрузкой, или другое отношение работает только с отложенной загрузкой?