Symfony2 DQL如何连接OneToMany关系中的最后一行

时间:2015-11-05 10:52:56

标签: symfony doctrine dql

我有两个与OneToMany关系相关的实体:

<?php

namespace CRMBundle\Entity;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;

/**
 * User
 *
 * @ORM\Table()
 * @ORM\Entity(repositoryClass="CRMBundle\Entity\ContactRepository")
 */
class User
{

/*...*/

    /**
     * @ORM\OneToMany(targetEntity="CRMBundle\Entity\Message", mappedBy="user", cascade={"persist"})
     * @ORM\OrderBy({"datetime" = "DESC"})
     */
    protected $messages;

/*...*/

}

<?php

namespace CRMBundle\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * Message
 *
 * @ORM\Table()
 * @ORM\Entity
 */
class Message
{

/*...*/

    /**
     * @ORM\ManyToOne(targetEntity="CRMBundle\Entity\User", inversedBy="messages")
     * @ORM\JoinColumn(name="user_id", referencedColumnName="id", onDelete="SET NULL")
     */
    private $user;

    /**
     * @var \DateTime
     *
     * @ORM\Column(name="Datetime", type="datetime", nullable=true)
     */
    private $datetime;



/*...*/
}

我的问题是如何在UserController中创建一个查询,以便为每个用户提供每个用户的最后一条消息(即根据datetime属性的最新消息)?

1 个答案:

答案 0 :(得分:4)

我认为您要找的是one of my previous answersone of my own questions ^^

您必须使用子查询动态选择一个用户的消息的最新日期时间值,并加入具有此值的消息。

为此,您必须定义(子)查询,选择message.datetime的MAX值:

$qb2= $this->createQueryBuilder('ms')
        ->select('MAX(ms.datetime) maxDate')
        ->where('ms.user = u')
        ;

然后在你的join子句中使用它,整个函数在你的UserRepository中:

$qb = $this->createQueryBuilder('u');
$qb ->leftJoin('u.messages', 'm', 'WITH', $qb->expr()->eq( 'm.datetime', '('.$qb2->getDQL().')' ))
    ->addSelect('m');

您的用户(他们每个人)将有一个消息Collection,其中包含一个消息(如果没有来自用户的消息,则为null)消息,您将通过这种方式获得:

$user->getMessages()->first();

但是如果你使用Symfony的延迟加载函数,因为你已经在user.messages属性上定义了一个orderby注释,调用

$user->getMessages()->first()

应该向您返回最新消息(但也会以静默方式加载所有其他消息)。 要避免此静默的第二个数据库查询,您可以将其直接加入请求您的用户的查询中:

$qb = $this->createQueryBuilder('u');
$qb ->leftJoin('u.messages', 'm')
    ->addSelect('m');