我为我的酒店设置了一个复杂的过滤器,最后我有一个集合,我想按其嵌套关系对父关系进行排序,所以在这里我如下所示:
public function resultFilter($from_date, $to_date, $bed_count, $city_id, $stars, $type_id, $hits, $price, $accommodation_name, $is_recommended, $start_price, $end_price, $has_discount, $facility_id)
{
// $data = QueryBuilder::for(Accommodation::class)
// ->allowedFilters(['city_id','grade_stars','accommodation_type_id'])
// ->allowedIncludes('gallery')
// ->when($bed_count, function ($q, $bed_count) {
// $q->with([
// 'accommodationRoomsLimited' => function ($q) use ($bed_count) {
// $q->where('bed_count', $bed_count);
// }
// ]);
// })
// ->paginate(10);
// ->get();
// ->orderBy('hits','DESC')->paginate(10);
$data = Accommodation::with(['city','accommodationFacilities', 'gallery', 'accommodationRoomsLimited.discount', 'accommodationRoomsLimited', 'accommodationRoomsLimited.roomPricingHistorySearch' => function ($query) use ($from_date, $to_date) {
$query->whereDate('from_date', '<=', $from_date);
$query->whereDate('to_date', '>=', $to_date);
}])->when($bed_count, function ($q, $bed_count) {
$q->whereHas('accommodationRoomsLimited', function($query) use ($bed_count) {
$query->where('bed_count', $bed_count);
});
})->when($accommodation_name, function ($query, $accommodation_name) {
$query->where('name', 'like', $accommodation_name);
})->when($is_recommended, function ($query,$is_recommended){
$query->where('is_recommended', $is_recommended);
})->when($start_price, function ($query, $start_price) {
$query->with([
'accommodationRoomsLimited.roomPricingHistorySearch' => function ($q) use ($start_price) {
$q->where('sales_price', '<', $start_price);
}
]);
})->when($has_discount, function ($query, $has_discount) {
$query->with([
'accommodationRoomsLimited' => function ($q) use ($has_discount) {
$q->has('discount');
}
]);
})
->whereIn('city_id', $city_id)
->whereIn('grade_stars', $stars)
->orWhere('accommodation_type_id', $type_id);
if ($hits) { // or == 'blabla'
$data = $data->orderBy('hits','DESC');
} elseif ($price) { // == A-Z or Z-A for order asc,desc
$f = $data->get();
foreach ($f as $datas) {
foreach ($datas->accommodationRoomsLimited as $g) {
dd($data);
$data = $data->accommodationRoomsLimited()->orderBy($g->roomPricingHistorySearch->sales_price);
}
}
}
$data = $data->paginate(10);
return $data;
}
因此,如果您阅读了代码,那么在请求中存在$ price的情况下,我添加了sales_price,我希望通过它对$ data进行排序。因此,在一个短期问题中,我想在上述查询中按sales_price对$ data进行排序。
注意 :此过滤器可能会变得更加复杂,因此,尽管我尝试过这两个过滤器,但它们都有其自身的局限性,但是任何其他的最佳实践或更好的方法(如spatie查询生成器或本地范围)都将受到赞赏
答案 0 :(得分:3)
我以前遇到过这个问题。看来我需要先解释一下有关急切加载的问题。
您无法通过eager loading
进行订购,您可以在获取数据后对其进行订购。因为
更高的负载将拆分联接查询,以提高性能。例如,您查询accomodation
并与城市有关。 accomodation
表具有1000条记录,而city
表具有10.000条记录。假设急切加载的最大ID为250,city_id
表中唯一的accomodation
为780。将生成5个查询。
$data = Accomodation::with('city')->get();
select * from accomodation
select * from city where id in [unique_id_1 - unique_id_250]
select * from city where id in [unique_id_251 - unique_id_500]
select * from city where id in [unique_id_501 - unique_id_750]
select * from city where id in [unique_id_751 - unique_id_780]
然后laravel将通过city
查询结果来创建关系。通过这种方法,您将解决联接查询中的N+1
问题,因此它应该更快。
然后假设您想使用查询构建器中的accomodation
方法通过city.name
订购with
。让我们以第三个查询为例。
$data = Accomodation::with([
'city' => function($q) { return $q->orderBy('name'); },
])->get();
查询将是:
select * from city where id in [unique_id_251 - unique_id_500] order by name
将对城市结果进行排序,但是laravel将以相同的方式进行读取。它将首先创建accomodation
,然后将其与city
查询相关联。因此,city
的订单不会影响accomodation
的订单。
那如何订购呢?我找到了实现此目标的几种方法。
加入查询。这是最简单的方法,但是会使查询变慢。如果您的数据不是很大,并且联接查询不会影响您的性能。也许0.003秒的性能提升并不值得您花8个小时。
sortBy
。您可以使用集合中的方法对其进行排序。
例如,如果您要根据accomodation
关系中的country.name
订购city
,此脚本将为您提供帮助。
$data = Accomodation::with('city.country')->get();
$data->sortBy(function($item) {
return $item->city->country->name;
});
join
查询然后对其进行排序。您可以使用集合中的map
方法。我确实认为所有过滤器和可搜索字符串都应包含在数据中。$data->map(function($item) {
return [
'city_name' => $city->name,
...
all_searchable_data,
all_shareable_data,
...
];
})->sortBy('key1);
city
代替accomodation
通过city.name
$data = City::with('accomodation')->orderBy('name')->get();
最后,如果您的数据很少更改(例如每2小时更改一次),则您可能会考虑使用缓存。您只需要每2小时使缓存无效并创建一个新的缓存。根据我的经验,如果数据很大,缓存总是比查询数据库快。您只需要知道间隔或event
即可使缓存无效。
任何选择都取决于您。但是请记住这一点,当您使用laravel中的集合处理批量数据时,它可能比从数据库中查询要慢。也许是因为PHP的性能。
对我来说,最好的方法是先使用eager loading
然后使用->map()
然后使用cache
。为什么在缓存之前需要先map
?原因是,通过选择某些属性将减小缓存大小。然后,您将获得更多性能。而且我可以说它将产生更多可读性更好的代码。
奖金 这就是我的方法。
$data = Cache::remember("accomodation", 10, function() {
$data = Accommodation::with([
'city',
...
])
->get();
return $data->map(function($item) {
return [
'city_name' => $item->city->name,
...
all_additional_data,
all_searchable_data,
all_shareable_data,
...
];
});
}
return $data->search(function($item) use ($searchKey, $searchAnnotiation, $searchValue) {
switch ($searchAnnotiation) {
case '>':
return $item[$searchKey] > $searchValue;
break;
case '<':
return $item[$searchKey] < $searchValue;
break;
}
})->sortBy($sortKey)->paginate();
缓存将保存处理后的数据。因此,所需的执行时间是从缓存中获取数据,对其进行过滤和排序。然后将其转换为分页。您可以在这些流中设置任何其他缓存以更快地获得结果。
通过为$data->paginate()
创建宏paginate
来 Collection
。