You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 16:55:06