Symfony 按角色查找用户(JSON 数组 Doctrine 属性)

Symfony find user by role (JSON array Doctrine property)

我正在做一个小项目,我有一个实体,其角色 属性 由一个数组组成。

我想做的是,在某些控制器中,找到一个在角色数组中具有特定角色的现有实体。

我正在尝试使用 findOneBy() 方法,但我似乎无法让它工作,它总是 returns null 即使我正在尝试寻找具有特定角色的实体存在。

这是我的实体及其属性:

/**
 * @ORM\Entity(repositoryClass=SalarieRepository::class)
 */
class Salarie
{
    /**
     * @ORM\Id
     * @ORM\GeneratedValue
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @ORM\Column(type="string", length=255)
     */
    private $nom;

    /**
     * @ORM\Column(type="string", length=255)
     */
    private $prenom;

    /**
     * @ORM\Column(type="string", length=255)
     */
    private $email;

    /**
     * @ORM\Column(type="string", length=255, nullable=true)
     */
    private $telephone;

    /**
     * @ORM\Column(type="string", length=255)
     */
    private $service;

    /**
     * @ORM\Column(type="json")
     */
    private $roles = [];

    // Getters & setters
}

这是我在控制器中使用 findOneBy() 尝试的示例,即 returns null:

$rolecheck = $this->salarieRepository->findOneBy(["roles" => ["ROLE_RESPONSABLE_RH"]]);

当我尝试使用不是数组的任何其他 属性 实体时,它运行良好,如果我这样做:

$rolecheck = $this->salarieRepository->findOneBy(["nom" => "test"]);
dd($rolecheck);

它将显示正确的实体:

SalarieController.php on line 47:
App\Entity\Salarie {#1501 ▼
  -id: 6
  -nom: "test"
  -prenom: "test"
  -email: "test@test.test"
  -telephone: null
  -service: "Graphisme"
  -roles: array:3 [▼
    0 => "ROLE_RESPONSABLE_RH"
    1 => "ROLE_RESPONSABLE_SERVICE"
    2 => "ROLE_SALARIE"
  ]
}

我们还可以看到它确实有角色数组,其中包含我试图在其中找到的角色。

关于我如何尝试找到一个具有特定角色的实体的任何线索"ROLE_RESPONSABLE_RH"

您的 $roles 属性 是 json 类型,这意味着它在您的数据库中存储为:

["ROLE_RESPONSABLE_RH", "ROLE_RESPONSABLE_SERVICE", "ROLE_SALARIE"]

如果 JSON 数组包含该角色,你需要询问 Doctrine,但你不能用 findOneBy() 方法做到这一点。

当您达到 ORM 限制时,您可以使用 Native Query with ResultSetMapping。它允许您使用 DBMS 的特定功能编写纯 SQL 查询,但仍然可以获得实体对象。

在您的 SalarieRepository class:

中创建此方法
public function findByRole(string $role): array
{
    // The ResultSetMapping maps the SQL result to entities
    $rsm = $this->createResultSetMappingBuilder('s');

    $rawQuery = sprintf(
        'SELECT %s
        FROM salarie s 
        WHERE /* your WHERE clause depending on the DBMS */',
        $rsm->generateSelectClause()
    );

    $query = $this->getEntityManager()->createNativeQuery($rawQuery, $rsm);
    $query->setParameter('role', $role);
    return $query->getResult();
}

然后你需要根据DBMS替换我在WHERE子句中的注释:

MariaDB - JSON_SEARCH():

SELECT %s
FROM salarie s 
WHERE JSON_SEARCH(s.roles, 'one', :role) IS NOT NULL

MySQL - JSON_CONTAINS():

SELECT %s
FROM salarie s 
WHERE JSON_CONTAINS(s.roles, :role, '$')

警告:您必须用双引号将 role 参数括起来:

$query->setParameter('role', sprintf('"%s"', $role));

PostgreSQL - jsonb escaped "?" operator:

SELECT %s
FROM salarie s 
WHERE s.roles::jsonb ?? :role

警告: 需要 PHP 7.4+。见 RFC

  1. CAST JSON 到 TEXT
class JSONText extends FunctionNode
{
    private $expr1;

    public function getSql(SqlWalker $sqlWalker)
    {
        return sprintf(
            "CAST(%s AS TEXT)",
            $this->expr1->dispatch($sqlWalker)
        );
    }

    public function parse(Parser $parser)
    {
        $parser->match(Lexer::T_IDENTIFIER);
        $parser->match(Lexer::T_OPEN_PARENTHESIS);
        $this->expr1 = $parser->StringPrimary();
        $parser->match(Lexer::T_CLOSE_PARENTHESIS);
    }
}
  1. 添加到您的 Doctrine DQL:
dql:
    string_functions:
        JSON_TEXT: YOUR_NAMESPACE\JSONText
  1. 使用你的转换函数
$qb->andWhere("JSON_TEXT(d.topics) LIKE '%$role%'")