DQL Query Doctrine 将连接元素 OrderBy 子句放在另一个元素的接合条件的子查询中

DQL Query Doctrine places joined element OrderBy clause in another element's juncture condition's subQuery

这是我的情况:

我有一个实体用户,有几个连接的实体集合:

class User
{
    private $id;

/**
 * @var UserData
 * @ORM\OneToMany(targetEntity="UserData", mappedBy="user")
 */
private $userDatas;

/**
 * @var ServiceAccess
 * @ORM\OneToMany(targetEntity="ServiceAccess", mappedBy="user")
 */
private $serviceAccesses;

...

}

ServiceAccess 在 Offers 上也有一个 OneToMany 连接,在其 level 字段 :[=16= 上有一个 Doctrine ORM 的 OrderBy 指令]

class ServiceAccess
{
private $id;

/**
 * Offers
 * @ORM\OneToMany(targetEntity="Offer",
 *                mappedBy="access",
 *                cascade={"persist","remove","refresh"})
 * @ORM\OrderBy({"level" = "DESC"})
 */
private $offers;

/**
 * @ORM\ManyToOne(
 *     targetEntity="User"
 * )
 * @ORM\JoinColumn(name="user_id", referencedColumnName="id", nullable=false)
 */
private $user;
...
}

我的出价class:

class Offer
{

private $id;

/**
 * The user access
 *
 * @var ServiceAccess
 * @ORM\ManyToOne(targetEntity="Fitnext\ServiceAccessBundle\Domain\Entity\UserServiceAccess", inversedBy="offers")
 * @ORM\JoinColumn(name="access_id", referencedColumnName="id", nullable=false)
 */
private $access;

/**
 * @ORM\Column(name="level", type="integer", nullable=false, options={"default"=0})
 */
private $level;
...
}

我正在实施 DQL 查询以获取一个具有多个依赖项的用户,包括其 UserDatas、其 ServiceAccesses 和 ServiceAccesses.offers。

这里的主要技巧是 User.userDatas 接合点 的条件: UserData 有一个 name 和一个 createdAt 字段,并且可以包含具有相同 name 的多行。 我的目标是查询每个定义的 name 组的最新行。 为此,我使用了一个子查询(这是在 Whosebug 上发送给我的 :)),它工作得很好。

此外,我还有几个 class 逻辑节点,其中一个在 User.serviceAccesses 上,然后 一个在 ServiceAccesses.offers 上,这导致了我的问题,当结合 UserData 子查询...

这是我的 QueryBuilder:

// First I define the subquery for the UserDatas juncture condition
    $queryDatas = $this->_em->createQueryBuilder()
        ->select('a')
        ->from('UserData', 'a')
        ->leftJoin( 'UserData', 'b', 'WITH', 'a.name = b.name AND a.createdAt < b.createdAt' )
        ->where( 'b.createdAt IS NULL' )
        ->andWhere('a.name IN (:names)')
        ->andWhere('a.user = u')
        ->orderBy( 'a.createdAt', 'DESC' )
        ->getDQL();

// Notice the orderBy clause here, which is part of the trick in order to get the latest registration of each named data group, and which is also part of the issue...

// And here is the main QueryBuilder
$this->createQueryBuilder('u')

        ->where('u.id = :id')
        ->setParameter('id', $id)

        // UserDatas
        ->leftJoin('u.userDatas', 'd', 'WITH', 'd IN ('. $queryDatas .')')
        ->addSelect('d')
        ->setParameter('names', ['height', 'weight'])

        // ServiceAccess
        ->leftJoin('u.serviceAccesses', 'svacc', 'WITH', 'svacc.activated = 1 AND svacc.type = :type' )
        ->setParameter('type','default')
        ->addSelect('svacc')

        // ServiceAccessOffers
        ->leftJoin('svacc.offers', 'offers', 'WITH', 'offers.activated = 1' )
        ->addSelect('offers')
 ...

这个returns我一个Doctrine\DBAL\Exception\InvalidFieldNameException:

SQLSTATE[42S22]: Column not found: 1054 Unknown column 'u7_.level' in 'order clause'

这是导致问题的生成的 SQL 代码部分:

   FROM users u0_
   LEFT JOIN user_data e1_ ON u0_.id = e1_.user_id
                              AND (e1_.id IN (
                                                 SELECT e8_.id
                                                 FROM user_data e8_
                                                   LEFT JOIN user_data e9_ ON (e8_.name = e9_.name AND e8_.created_at < e9_.created_at)
                                                 WHERE e9_.created_at IS NULL AND e8_.name IN ('height', 'weight')
                                                           AND e8_.user_id = u0_.id
                                                           ORDER BY e8_.created_at DESC, u7_.level DESC
                                               )
                                )
   LEFT JOIN service_access u6_ ON u0_.id = u6_.user_id AND (u6_.activated = 1 AND u6_.type = 'default')
   LEFT JOIN offer u7_ ON u6_.id = u7_.access_id AND (u7_.activated = 1)

Doctrine 采用在 Access.offers 属性(orderBy 级别 DESC)中定义的 OrderBy 子句,并将其添加到子查询 orderBy!!当然,它不知道报价 table 及其级别字段,因为它们不在子查询范围内!

它的工作原理就好像 Doctrine 将 orderBy 添加到它在查询中找到的 only/first OrderBy,在这种情况下这是行不通的..

IMO 这是一个 Doctrine 错误。

我目前处于 "symfony/symfony": "^3.4", "doctrine/orm": "2.5.12",.

有人知道吗?我是第一个遇到这个问题的吗?

非常感谢。

好吧,我现在觉得有点菜鸟..但我解决了这个问题.. 我只是重新组织了我的 QueryBuilder 方法,以便将 Offers 接合点 放在另一个接合点之前 ,以便它在正确的位置生成第一个 orderBy 子句,而不是将它添加到在子查询。

有效。

仍然认为这有点奇怪,因为 QueryBuilder 方法应该不管它们被调用的顺序如何工作..