Laravel Eloquent多表Decimal最值获取及排序问题
问题描述
我正在开发一个拍卖网站,涉及listings和bids两张数据表:
listings表存储商品的一口价(buy_now_price)和起拍价(auction_start_price)bids表记录所有出价信息
我需要实现按商品当前价格的最高价/最低价排序的搜索功能,但当前价格的取值逻辑比较复杂:可能来自起拍价、一口价,或是出价金额(offer_amount)。目前用orderByRaw写的排序逻辑无法得到正确结果:
$order_by = 'bids.offer_amount DESC, listings.buy_now_price DESC, listings.auction_start_price DESC'; $listings->orderByRaw($order_by)->get();
我已经写了一个辅助函数currentPrice来计算单个商品的当前价格,想知道能不能直接在Eloquent查询中调用这个函数来实现排序?
辅助函数代码:
function currentPrice($id) { // 先查找已成交的出价 $sold = DB::table('bids') ->where('listing_id', $id) ->where(function ($query) { $query->where('offer_accepted', 1) ->orWhere('buy_now_accepted', 1); }) ->first(); $current_price = 0; if ($sold) { if (!empty($sold->offer_amount)) { $current_price = $sold->offer_amount; } if (!empty($sold->buy_now_amount)) { $current_price = $sold->buy_now_amount; } } if (empty($current_price)) { $current_set_price = Bid::where('listing_id', $id)->orderBy('max_bid_amount', 'desc')->skip(1)->first(); if ($current_set_price) { $current_price = $current_set_price->max_bid_amount + 1; } else { // 没有次高出价,用起拍价 $listing = Listing::where('id', $id)->first(); if ($listing) { $auction_start_price = $listing->auction_start_price; $current_price = $auction_start_price; } } } return $current_price; }
解决方案
不能直接在Eloquent查询中调用PHP辅助函数currentPrice,因为Eloquent是生成SQL语句在数据库端执行,而PHP函数是在应用服务器端运行,数据库无法识别PHP代码。下面给两种可行的实现方式:
方式一:将currentPrice逻辑转为SQL表达式(推荐)
把currentPrice里的价格计算逻辑翻译成SQL的CASE WHEN语句,直接在orderByRaw中使用,让数据库层面完成排序,性能更优,也支持分页:
$direction = 'DESC'; // 可根据需求改为ASC $listings->orderByRaw(" CASE -- 优先取已成交的一口价或出价 WHEN EXISTS ( SELECT 1 FROM bids WHERE bids.listing_id = listings.id AND (bids.offer_accepted = 1 OR bids.buy_now_accepted = 1) ) THEN ( SELECT COALESCE(b.buy_now_amount, b.offer_amount) FROM bids b WHERE b.listing_id = listings.id AND (b.offer_accepted = 1 OR b.buy_now_accepted = 1) LIMIT 1 ) -- 没有成交的,取次高出价+1(如果有) WHEN (SELECT COUNT(*) FROM bids WHERE bids.listing_id = listings.id) >= 2 THEN ( SELECT b.max_bid_amount + 1 FROM bids b WHERE b.listing_id = listings.id ORDER BY b.max_bid_amount DESC LIMIT 1 OFFSET 1 ) -- 没有次高出价,用起拍价 ELSE listings.auction_start_price END {$direction} ")->get();
说明:
- 用
EXISTS判断是否有已成交的出价 - 用
COALESCE优先取buy_now_amount,再取offer_amount,对应函数里的逻辑 - 用
LIMIT 1 OFFSET 1模拟skip(1)的逻辑,取次高的max_bid_amount - 最后 fallback 到起拍价
方式二:先查询列表,再用集合排序
如果数据量不大,可以先获取所有listings,再用Laravel集合的sortBy或sortByDesc方法调用currentPrice排序:
// 先获取所有列表 $allListings = $listings->get(); // 按当前价格降序排序 $sortedListings = $allListings->sortByDesc(function ($listing) { return currentPrice($listing->id); }); // 转成可迭代的集合或数组 $sortedListings = $sortedListings->values();
注意:这种方式是在应用端排序,数据量大时性能会很差,而且无法直接配合数据库分页使用,适合小数据量场景。
内容的提问来源于stack exchange,提问作者phpcoder
相关产品推荐
相关产品推荐

