如何在排除特定日期元素的 OneToMany 关系中进行 DQL 查询?

How to make a DQL query in a OneToMany relation that exclude elements at a certain date?

我在 Symfony 中遇到查询问题。我有两个具有 OneToMany 关系的实体:

/**
 * Field
 *
 * @ORM\Table(name="field")
 * @ORM\Entity(repositoryClass="MyBundle\Repository\FieldRepository")
 */
class Field
{
    .
    .
    .
    /**
    * @var string
    *
    * @ORM\Column(name="name", type="string", length=255)
    */
   private $name;

     /**
     * @ORM\OneToMany(targetEntity="Booking", mappedBy="field", cascade={"remove","persist"})
     */
    private $bookings;

并且:

/**
 * Booking
 *
 * @ORM\Table(name="booking")
 * @ORM\Entity(repositoryClass="MyBundle\Repository\BookingRepository")
 */
class Booking
{
    .
    .
    .

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

    /**
     * @ORM\ManyToOne(targetEntity="Field", inversedBy="bookings")
     * @ORM\JoinColumn(name="field_id", referencedColumnName="id")
     */
    private $field;

而且我想获得在特定日期免费( 预订)的字段。我试过这个:

public function findFreeFields($date){

        $em = $this->getEntityManager();

        $queryText = "SELECT f, FROM MyBundle:Field f JOIN f.bookings b ";
        $queryText .= "WHERE  b.date!=:date";

        $query = $em->createQuery($queryText);
        $query->setParameter('date', $date);
        return $query->getResult();

问题是,例如,如果我有一个名为 "field1" 的字段,其中有 4 个预订,其中一个预订的日期是 2017 年 5 月 20 日。我用 findFreeFields() 查找那个日期 (20/05/2017) 的所有空闲字段,但我得到一个 "field1" 重复了三次的列表,这显然是错误的。 "field1" 不能出现在结果列表中,因为它是在指定日期预订的。

使用 DISTINCT 我没有得到重复的结果,但仍然得到 "field1" 结果。

public function findFreeFields($date){

        $em = $this->getEntityManager();

        $queryText = "SELECT DISTINCT f, FROM MyBundle:Field f JOIN f.bookings b ";
        $queryText .= "WHERE  b.date!=:date";

        $query = $em->createQuery($queryText);
        $query->setParameter('date', $date);
        return $query->getResult();

那么,我必须如何编写 DQL 查询才能排除在特定日期有预订的字段?

解决方案是做一个子查询,选择所有在指定日期预订的文件,然后使用该子查询过滤掉所有字段的选择。

为了清楚起见,我使用了 QueryBuilder 并划分了子查询和查询,但您可以根据自己的喜好随意修改任何内容。

...
$em = $this->getDoctrine()->getManager();

$qb = $em->createQueryBuilder();
$bookedFieldsQuery = $qb->select('bookedf')
                        ->from('MyBundle:Field', 'bookedf')
                        ->join('bookedf.bookings', 'b', 'WITH' ,'b.date = :date')
                        ->getDQL();

$qb = $em->createQueryBuilder();
$availableFieldsQuery = $qb->select('f')
                           ->from('MyBundle:Field', 'f')
                           ->where($qb->expr()->notIn('f', $bookedFieldsQuery))
                           ->getQuery();

$availableFields->setParameter('date', $date);

return $availableFieldsQuery->getResult();
...

最终的 DQL 本身:

SELECT f FROM MyBundle:Field f WHERE f.id NOT IN(SELECT bookedf.id FROM MyBundle:Field bookedf INNER JOIN bookedf.bookings b WITH b.date = :date)