如何在Doctrine查询构建器中用Criteria复用含OR的混合查询条件?
我完全懂你的需求——用Doctrine的Criteria来统一管理带嵌套AND/OR的查询条件,同时给分页查询和总数统计复用,避免重复写逻辑对吧?这其实比你想的要直接,咱们一步步来:
用Doctrine Criteria实现嵌套AND/OR查询条件
1. 先明确目标SQL结构
假设你要生成的目标SQL大概是这样(拿一个实际场景举例,方便对应代码):
SELECT * FROM your_entity WHERE status = 'active' AND ( (category = 'A' AND created_at > '2024-01-01') OR (category = 'B' AND views > 100) )
对应的Laravel Eloquent写法你肯定熟悉:
$query->where('status', 'active') ->where(function ($q) { $q->where(function ($subq) { $subq->where('category', 'A') ->where('created_at', '>', '2024-01-01'); })->orWhere(function ($subq) { $subq->where('category', 'B') ->where('views', '>', 100); }); });
接下来咱们把这个逻辑用Doctrine的Criteria完美复刻。
2. 用Criteria + Expr构建嵌套逻辑
Doctrine的Criteria配合Expr类可以轻松实现多层嵌套的逻辑组合,核心就是用Expr::andX()和Expr::orX()来包裹子条件,和Eloquent的闭包写法逻辑完全对应。
步骤1:初始化表达式构建器
首先引入并创建Expr实例:
use Doctrine\Common\Collections\Expr\Expr; $expr = new Expr();
步骤2:逐层构建嵌套条件
先写最内层的两个AND组合条件:
// 第一个OR分支:category=A 且 created_at晚于2024年初 $conditionGroup1 = $expr->andX( $expr->eq('category', 'A'), $expr->gt('created_at', new \DateTime('2024-01-01')) ); // 第二个OR分支:category=B 且 views大于100 $conditionGroup2 = $expr->andX( $expr->eq('category', 'B'), $expr->gt('views', 100) );
然后把这两个分支用OR包裹,再和外层的status=active条件做AND组合:
// 最终组合条件:status=active 且 (组1 或 组2) $finalCondition = $expr->andX( $expr->eq('status', 'active'), $expr->orX($conditionGroup1, $conditionGroup2) );
步骤3:封装Criteria并复用
把刚才的条件封装成Criteria对象:
use Doctrine\Common\Collections\Criteria; $criteria = Criteria::create() ->where($finalCondition);
现在就可以把这个Criteria同时应用到你的分页查询和总数统计Query Builder了:
// 给分页查询附加条件 $paginationQuery->addCriteria($criteria); // 给总数统计查询附加条件 $countQuery->addCriteria($criteria);
3. 实用注意事项
- 参数安全:如果你的查询值是动态用户输入,不用担心SQL注入——Criteria会自动帮你处理参数绑定,直接传值即可。
- 关联字段处理:如果是关联实体的字段,要使用Doctrine的关联别名,比如
$expr->eq('user.status', 'active')(假设当前实体关联了User实体)。 - 分页排序复用:如果分页需要排序,可以直接在Criteria里追加
->orderBy(['created_at' => Criteria::DESC]);总数查询不需要排序的话,可以单独创建一个不带排序的Criteria,或者在总数Query Builder里移除排序语句。
完整可运行示例
use Doctrine\Common\Collections\Criteria; use Doctrine\Common\Collections\Expr\Expr; use App\Entity\YourEntity; // 替换成你的实体类 // 1. 构建嵌套查询条件 $expr = new Expr(); $innerGroup1 = $expr->andX( $expr->eq('category', 'A'), $expr->gt('created_at', new \DateTime('2024-01-01')) ); $innerGroup2 = $expr->andX( $expr->eq('category', 'B'), $expr->gt('views', 100) ); $mainCondition = $expr->andX( $expr->eq('status', 'active'), $expr->orX($innerGroup1, $innerGroup2) ); // 2. 创建统一的Criteria $criteria = Criteria::create()->where($mainCondition); // 3. 应用到分页查询 $paginationQuery = $entityManager->createQueryBuilder() ->select('e') ->from(YourEntity::class, 'e') ->setMaxResults(20) // 每页20条 ->setFirstResult(0); // 第一页 $paginationQuery->addCriteria($criteria); $pageResults = $paginationQuery->getQuery()->getResult(); // 4. 应用到总数统计查询 $countQuery = $entityManager->createQueryBuilder() ->select('COUNT(e.id)') ->from(YourEntity::class, 'e'); $countQuery->addCriteria($criteria); $totalRecords = $countQuery->getQuery()->getSingleScalarResult();
这样就完美实现了和Eloquent类似的嵌套逻辑,同时统一了查询条件,再也不用在两个Query Builder里重复写相同的过滤规则啦。
内容的提问来源于stack exchange,提问作者Leonardo Beal
相关产品推荐
相关产品推荐

