如何用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
相关产品推荐
相关产品推荐

