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

如何在Symfony与PostgreSQL中用CreateQueryBuilder查询指定角色用户

解决Symfony+PostgreSQL下Doctrine查询JSON角色数组的问题

问题根源

你能正常运行的原生SQL用了PostgreSQL的jsonb @>操作符,检查数组是否包含指定的JSON字符串元素(比如'"ROLE_ADMIN"'),但你的Doctrine代码有两个核心问题:

  • 传递的参数是纯字符串(ROLE_ADMIN),而非原生SQL要求的JSON格式字符串("ROLE_ADMIN")
  • 使用的JSON_CONTAINS函数在PostgreSQL中的行为和你预期的原生@>操作符不匹配

解决方案

方案一:使用PostgreSQL原生@>操作符(推荐)

直接复用你验证有效的原生SQL逻辑,在Doctrine查询构建器中使用原生操作符,并修正参数格式:

public function findAdministrators(int $offset): Paginator
{        
    $roles = ["ROLE_ADMIN", "ROLE_ADMIN_DELEGATION"];
    $qb = $this->createQueryBuilder('u');

    $orConditions = [];
    $parameters = [];

    foreach ($roles as $index => $role) {
        $paramName = 'role' . $index;
        // 使用PostgreSQL jsonb包含操作符,检查数组是否包含目标角色的JSON字符串
        $orConditions[] = "u.roles::jsonb @> :{$paramName}";
        // 将角色转为JSON格式字符串(自动添加双引号)
        $parameters[$paramName] = json_encode($role);
    }

    $qb->where(implode(' OR ', $orConditions))
        ->setParameters($parameters)
        ->setMaxResults(self::MEMBERS_PER_PAGE)
        ->setFirstResult($offset);

    return new Paginator($qb->getQuery());
}

优化:将字段改为jsonb类型

为了避免查询中手动转换类型(::jsonb),并提升查询性能,建议修改实体字段定义为jsonb类型:

/**
* @var list<string> The user roles     
*/
#[ORM\Column(type: 'jsonb')]     
private array $roles = [];

之后查询中的条件可以简化为:

$orConditions[] = "u.roles @> :{$paramName}";

方案二:使用Doctrine的JSON_ARRAY_CONTAINS函数

如果你的Doctrine ORM版本在2.10以上,可以使用官方提供的JSON_ARRAY_CONTAINS函数,它会自动适配PostgreSQL的@>操作符,无需手动处理JSON格式:

public function findAdministrators(int $offset): Paginator
{        
    $roles = ["ROLE_ADMIN", "ROLE_ADMIN_DELEGATION"];
    $qb = $this->createQueryBuilder('u');

    foreach ($roles as $index => $role) {
        $paramName = 'role' . $index;
        $qb->orWhere("JSON_ARRAY_CONTAINS(u.roles, :{$paramName})")
           ->setParameter($paramName, $role);
    }

    $qb->setMaxResults(self::MEMBERS_PER_PAGE)
       ->setFirstResult($offset);

    return new Paginator($qb->getQuery());
}

关键说明

  • 原生SQL中@> '"ROLE_ADMIN"'的本质是检查jsonb数组是否包含JSON字符串元素,因此参数必须是带双引号的JSON格式,json_encode($role)会自动完成这个转换
  • JSON_ARRAY_CONTAINS是Doctrine的跨数据库JSON函数,会根据底层数据库自动生成对应的SQL语法,兼容性更好

内容的提问来源于stack exchange,提问作者David Bermudez

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 12:06:03