使用 QueryBuilder 制作一个基于多对多关系的 Doctrine 子查询 简单的 SQL我失败的尝试

问题描述

试图摆脱一些锈迹,并了解我将如何使用查询构建器来做到这一点!我尝试了几种方法,但遇到了各种神秘的错误。好久没有访问这种做事方式了,感谢帮助!

我有一个属于群组的用户实体:

/**
 * @ORM\Entity
 * @ORM\Table(name="users")
 * 
class User {

    /**
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     * @ORM\Column(type="integer",nullable=false,options={"unsigned"=true})
     */
    private $id;

    /**
     * @ORM\ManyToMany(targetEntity="Group",cascade={"persist"})
     */
    private $groups;
}


/**
 * @ORM\Entity
 * @ORM\Table(name="`groups`")
 *
 */
class Group {
    /**
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     * @ORM\Column(type="integer",options={"unsigned"=true})
     */
    private $id;
}

现在,我还有一个单独的 GroupsCerts 类,它是特定组所需的认证列表。

/**
 * @ORM\Entity
 * @ORM\Table(name="groups_certs")
 * This entity contains Meta data about group-certification relationship
 *
 */
class GroupCertRecord
{
    /**
     * @var Group
     * @ORM\Id
     * @ORM\ManyToOne(targetEntity="Group")
     * @ORM\JoinColumn(onDelete="cascade")
     */
    protected $group;

    /**
     * @var Certification
     * @ORM\Id
     * @ORM\ManyToOne(targetEntity="Certification",inversedBy="group_records")
     * @ORM\JoinColumn(onDelete="cascade")
     */
    protected $cert;

    /**
     * @var \DateTime
     * @ORM\Column(type="datetime",options={"default": "CURRENT_TIMESTAMP"})
     */
    private $time_added;
}

任务是获取属于特定认证索引的组的所有用户

简单的 sql

我目前很好地使用了 ResultSetMappingBuilder,使用该方法sql 的要点非常简单。例如,查找证书 3,sql 为:

SELECT U.*
FROM users U
WHERE U.id IN (
   SELECT user_id 
   FROM users_groups UG
   WHERE UG.group_id IN (
       SELECT id 
       FROM groups G 
       WHERE G.id IN (
           SELECT group_id 
           FROM groups_certs GC 
           WHERE GC.cert_id=3
       )
   )
) 

我曾尝试将其映射到 QueryBuilder 表达式中,但它在“IN”的表达式构建器中消失了。它不喜欢我正在做的事情 - 快速解决

我失败的尝试

    $em = $this->getEntityManager();
    $expr = $em->getExpressionBuilder();

    $subQuery = $em->createqueryBuilder()
        ->select('(gc.group)')
        ->from(GroupCertRecord::class,'gc')
        ->where('gc.cert = :cert');

    $queryD = $em->createqueryBuilder()
        ->select('u')
        ->from(User::class,'u')
        ->where($expr->in('u.groups',$subQuery->getDQL()))
        ->setParameter('cert',$cert);

谢谢!

解决方法

我相信您可以使用连接而不是子查询来实现相同的效果,并且纯 SQL 中的查询看起来像

select u.*
from users u
join users_groups ug on u.id = ug.user_id
join groups g on g.id = ug.group_id
join groups_certs gc on g.id = gc.group_id
join certification c on c.id = gc.cert_id
where c.id = 3

与子查询相比,这些连接查询在 DQL 或查询构建器中转换非常方便,在 DQL 中,您使用连接子句中的实体映射,其余原则将处理映射背后的表,如 {{1} } 指向与连接表的多对多关系,u.groups 将处理一对多映射等

除了您的问题之外,您几乎提供了所有必要的映射,除了 g.groupCertificationsGroup 的关系我猜它会是这样的

GroupCertRecord

您的 DQL 看起来像

class Group
{
    // ...
    
    /**
     * @ORM\OneToMany(targetEntity="GroupCertRecord",mappedBy="groups")
     */
    private $groupCertifications;
}

查询构建将是

SELECT u
FROM  AppBundle\Entity\User u
JOIN u.groups g
JOIN g.groupCertifications gc
JOIN gc.cert c
WHERE c.id = :id