Doctrine Query Builder使用join

时间:2018-06-12 10:35:37

标签: join doctrine repository conditional-statements query-builder

我有两个实体UserCalendarEvent。我的用户已连接到工厂,而calendarEvent可用于了解用户是否已被"借出"到他所属的那个工厂的另一家工厂......

我的两个实体是这样的:

用户:

class User extends BaseUser
{

    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;

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

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

    /**
     * @ORM\OneToMany(targetEntity="AppBundle\Entity\CalendarEvent", mappedBy="user")
     */
    private $events;

    /**
     * @ORM\ManyToOne(targetEntity="AppBundle\Entity\Factory", inversedBy="users")
     * @ORM\JoinColumn(name="factory_id", referencedColumnName="id")
     */
    private $factory;

}

CalendarEvent:

class CalendarEvent
{

    /**
     * @var int
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id; 

    /**
     * @var \DateTime
     * @ORM\Column(type="datetime", name="event_start")
     */
    private $startDate;

    /**
     * @var \DateTime
     * @ORM\Column(type="datetime", name="event_end")
     */
    private $endDate;

    /**
     * @var bool
     * @ORM\Column(name="isLoan", type="boolean")
     */
    private $isLoan = TRUE;

    /**
     * @ORM\ManyToOne(targetEntity="Factory")
     * @ORM\JoinColumn(name="factory_id", referencedColumnName="id", nullable = true)
     */
    private $factory;

    /**
     * @ORM\ManyToOne(targetEntity="UserBundle\Entity\User", inversedBy="events")
     * @ORM\JoinColumn(name="user_id", referencedColumnName="id")
     */
    private $user;
}

我想要做的是在UserRepository中创建一个存储库函数,当我在入口参数中使用工厂触发请求时,它会向我提供实际工作的人员... < / p>

为了达到这个目的,我尝试指定一个条件。那个条件应该是这样的:

select * from user where (
    (user.factory == $idfactory AND not loaned_to_other_factory) OR 
    (user.factory != $idfactory AND loaned_to_my_factory)
)

要知道用户是否已被租借到我的工厂,必须验证以下条件:

Does a record exist in CalendarEvent where startDate < NOW and endDate > NOW and loaned == true and factory == $factory

我尝试使用我的存储库的用户和/或条件,但我已经坚持使用该查询这么久了,因为findBy方法有效,我甚至都不习惯使用存储库在大多数情况下很好......

我试过这个,但我对此变量Expression of type 'Doctrine\ORM\QueryBuilder' not allowed in this context.

有错误$notLoanedToOther
public function findByFactory($idFactory)
{
    $qb = $this->_em->createQueryBuilder();
    $qb->select('u')
    ->from($this->_entityName, 'u')
    ->leftJoin('u.events', 'e');

    $sub = $this->createQueryBuilder("e");
    $sub->select("e");
    $sub->from("CalendarEvent","e");
    $sub->where('e.user = u.id');
    $sub->andWhere('e.startDate < :now');
    $sub->andWhere('e.endDate > :now');

    $notLoanedToOther = $qb->where($qb->expr()->not($qb->expr()->exists($sub->getDQL())));

    $sub = $this->createQueryBuilder("e");
    $sub->select("e");
    $sub->from("CalendarEvent","e");
    $sub->where('e.user = u.id');
    $sub->where('e.factory = :idf');
    $sub->andWhere('e.startDate < :now');
    $sub->andWhere('e.endDate > :now');

    $loanedToMyFactory = $qb->where($qb->expr()->exists($sub->getDQL()));

    $condition1 = $qb->expr()->andX(
        $qb->expr()->eq('u.factory', ':idf'),
        $notLoanedToOther
    );

    $condition2 = $qb->expr()->andX(
        $qb->expr()->neq('u.factory', ':idf'),
        $loanedToMyFactory
    );

    $qb ->where($qb->expr()->orX($condition1, $condition2));

    $qb ->setParameter('idf', $idFactory);

    return $qb->getQuery()->getResult();
}

我真的希望我有道理,有人可以给我提示来实现这一目标。提前致谢

2 个答案:

答案 0 :(得分:2)

我想您可以将现有查询转换为不在

$notLoanedToOther = $this->createQueryBuilder("e")
                         ->select("e.user")
                         ->from("CalendarEvent","e")
                         ->andWhere('e.startDate < :now')
                         ->andWhere('e.endDate > :now')
                         ->getDQL();

$loanedToMyFactory = $this->createQueryBuilder("e")
                          ->select("e.user")
                          ->from("CalendarEvent","e")
                          ->where('e.factory = :idf')
                          ->andWhere('e.startDate < :now')
                          ->andWhere('e.endDate > :now')
                          ->getDQL();

$qb = $this->_em->createQueryBuilder();
 $qb->select('u')
    ->from($this->_entityName, 'u');
    ->where(
        $qb->expr()->not(
            $qb->expr()->in(
            'u.id',
            $notLoanedToOther
            )
        )
    )
    ->andWhere(
        $qb->expr()->in(
        'u.id',
        $loanedToMyFactory
        )   
    )
    ->where(
        $qb->expr()->orX(
                $qb->expr()->andX(
                    $qb->expr()->eq('u.factory', ':idf'),
                    $qb->expr()->not(
                        $qb->expr()->in(
                        'u.id',
                        $notLoanedToOther
                        )
                    )
                ), 
                $qb->expr()->andX(
                    $qb->expr()->neq('u.factory', ':idf'),
                    $qb->expr()->in(
                    'u.id',
                    $loanedToMyFactory
                    )
                )
            )
        )
    ->setParameter('idf', $idFactory)
    ->setParameter('now', $someDate)
    ;

Doing a WHERE .. IN subquery in Doctrine 2

答案 1 :(得分:0)

根据您在SQL中指定的条件,如何使用带有联接的简单DQL查询而不是复杂的not in子句

  

从用户中选择*,其中(       (user.factory == $ idfactory,而不是借给其他_factory)或       (user.factory!= $ idfactory AND loaned_to_my_factory)   )

您可以在DQL中将其等效写为

SELECT u
FROM User u
LEFT JOIN u.events nl WITH e.startDate < :now AND e.endDate > :now
LEFT JOIN u.events l WITH e.startDate < :now AND e.endDate > :now AND e.factory = :idf
WHERE (
    u.factory = :idf AND nl.id IS NULL
) OR (
    u.factory != :idf AND l.id IS NOT NULL
)

使用带附加子句的用户与事件实体两次连接,一个用于借出,另一个未加载,然后应用您的条件,我添加了IS NULLIS NOT NULL过滤器以满足您的存在和不存在条件