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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 09:58:49