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

如何用Doctrine 2 QueryBuilder实现按酒店取最低旅游价格记录

没问题,我来帮你搞定这个需求!要实现每个酒店对应最低旅游价格的记录,用Doctrine 2 QueryBuilder是很合适的,我分方案给你说明:

一、Doctrine 2 QueryBuilder 实现方案

假设你的实体类是Hotel和Tour,其中Hotel与Tour是一对多关联(一个酒店对应多个旅游产品),Tour实体包含关联酒店的字段(比如$hotel,ManyToOne映射)和价格字段$price。下面是具体的实现代码:

use Doctrine\ORM\EntityManagerInterface;
use App\Entity\Hotel;
use App\Entity\Tour;

// 注入EntityManagerInterface到你的服务/控制器中
public function getHotelsWithCheapestTour(EntityManagerInterface $em)
{
    // 第一步:子查询 - 获取每个酒店对应的最低旅游价格
    $subQuery = $em->createQueryBuilder()
        ->select('IDENTITY(t.hotel) as hotel_id, MIN(t.price) as min_price')
        ->from(Tour::class, 't')
        ->groupBy('t.hotel');

    // 第二步:主查询 - 关联酒店和对应最低价的旅游记录
    $query = $em->createQueryBuilder()
        ->select('h, t')
        ->from(Hotel::class, 'h')
        ->join(Tour::class, 't', 'WITH', 'h = t.hotel')
        ->join(
            '(' . $subQuery->getDQL() . ')',
            'sub',
            'WITH',
            'sub.hotel_id = h.id AND sub.min_price = t.price'
        )
        ->getQuery();

    return $query->getResult();
}

代码说明:

  • 子查询里用IDENTITY(t.hotel)是为了直接获取酒店的ID值(Doctrine中关联对象默认返回实体,用IDENTITY可以提取主键),然后按酒店分组计算最低价格。
  • 主查询通过两次关联:先关联酒店和所有旅游记录,再关联子查询的结果,确保只保留价格等于对应酒店最低价的旅游记录。
  • 如果存在多个旅游记录价格同为某酒店的最低价,这个查询会返回所有符合的记录;如果只需要每个酒店一条,可以在主查询末尾添加->groupBy('h.id')来限制。
二、原生SQL 参考方案(用于理解逻辑)

如果你需要对照原生SQL来理解,对应的SQL语句如下:

SELECT h.*, t.*
FROM hotel h
INNER JOIN tour t ON h.id = t.hotel_id
INNER JOIN (
    SELECT hotel_id, MIN(price) AS min_price
    FROM tour
    GROUP BY hotel_id
) sub ON sub.hotel_id = h.id AND sub.min_price = t.price;

额外提示:

  • 如果你的实体中关联字段是直接存ID的(比如$hotelId而非关联对象),记得把QueryBuilder里的t.hotel改成t.hotelId,子查询的IDENTITY(t.hotel)也可以直接换成t.hotelId。
  • 如果需要包含没有任何旅游产品的酒店,可以把主查询中的JOIN改成LEFT JOIN,不过这类酒店对应的Tour数据会是null,按需调整即可。

内容的提问来源于stack exchange,提问作者Petr Klein

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 07:11:19