【问题标题】:How to use Datetime comparison as condition in JOIN statement如何在 JOIN 语句中使用日期时间比较作为条件
【发布时间】:2018-01-10 02:25:16
【问题描述】:

背景:

我有两个实体:AdvertRental

广告

/**
 * @ORM\Table(name = "advert")
 * @ORM\Entity(repositoryClass = "App\Repository\AdvertRepository")
 */
class Advert
{

    /**
     * @ORM\OneToMany(targetEntity = "App\Entity\Rental", mappedBy = "advert")
     */
    private $rentals;

    private $beginDate;

    private $endDate;

    public function addRental(\App\Entity\Rental $rental)
    {
        $this->rentals[] = $rental;
        $rental->setAdvert($this);
        return ($this);
    }

    public function getRentals()
    {
        return $this->rentals
    }

出租

/**
 * @ORM\Table(name = "rental")
 * @ORM\Entity(repositoryClass = "App\Repository\RentalRepository")
 */
class Rental
{

    /**
     * @ORM\ManyToOne(targetEntity = "App\Entity\Advert", inversedBy = "rentals")
     */
    private $advert;

    /**
     * @var \Datetime
     */
    private $beginDate;

    /**
     * @var \Datetime
     */
    private $endDate;

    public function setAdvert(\App\Entity\Advert $advert)
    {
        $this->advert = $advert;
        return ($this);
    }

    public function getAdvert()
    {
        return $this->advert;
    }

}

问题:

我正在尝试编写一种存储库方法,该方法可以像提供的那样在数据库中获取广告。现在,我想排除在给定期间租用的所有广告。这是我尝试过的:

在广告库中:

//This method creates the query builder and use other methods to
//improve the query
public function fetchSearchResult(Advert $advertType)
{
    $qb = $this->createQueryBuilder('a');
    // Other methods, similar to hasValidPeriods, using other attributes.
    // Some of them preform JOIN, successfully. One of them compare dates
    // (but no JOIN) also successfully.
    // [...]
    $this->hasValidRentalPeriods($qb, $advertType->getBeginDate(), $advertType->getEndDate());

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

public function hasValidRentalPeriods(QueryBuilder $qb, \Datetime $beginDate, \Datetime $endDate)
{
    $qb->leftJoin('a.rentals', 'rental', 'WITH NOT',
    '(
        (rental.beginDate < :beginDate AND :beginDate < rental.endDate)
        OR (rental.beginDate < :endDate AND :endDate < rental.endDate)
        OR (:beginDate < rental.beginDate AND rental.endDate <:endDate)
    )')
        ->setParameter('beginDate', $beginDate)
        ->setParameter('endDate', $endDate)
    ;
}

有了这个左连接,我希望得到:

  • 所有没有出租的广告
  • 所有广告,实际租金与广告中提供的日期没有冲突

但它没有返回预期的结果。我得到了所有没有按预期出租的广告,但也有碰撞的广告。此外,删除 NOT 语句不会改变结果。这就是为什么我意识到这里出了点问题。

之后我尝试使用更简单的连接:

$qb->leftJoin('a.rentals', 'rental', 'WITH NOT',
    '(rental.beginDate < :beginDate AND :beginDate < rental.endDate)'
    )

这一次,我希望得到所有广告,但租金与 beginDate 发生冲突的广告除外。但是我仍然收到所有广告

我还尝试按照this post 中的建议将日期转换为带有Datetime::format('Y-m-d H:i:s') 的字符串,但我仍然得到相同的结果。

我很确定我误用了 Datetime 对象之间的比较。另一方面,我已经成功地进行了这样的比较。或者,我的 JOIN 错误?

注意:已为广告正确设置了租金。

注意2:如果需要,您可以在my github查看完整的代码和测试

【问题讨论】:

  • 您是否通过日志或 Symfony 的配置栏检查了从您的 dql 派生的实际 sql?有时 dql 的解释与您的意思不同(即使您的 dql 的条件是正确的),所以也许您可以先看到它们。
  • 您使用的是左连接,这意味着它用NULL 条目替换未找到的值。您的条件应该在WHERE 子句中。

标签: symfony doctrine dql


【解决方案1】:

试试这个:

public function hasValidRentalPeriods(QueryBuilder $qb, \DateTime $beginDate, \DateTime $endDate)
{
    if (!$beginDate OR !$endDate) {
        throw new PreconditionRequiredHttpException('You must provide begin and end dates');
    }

    if ($endDate <= $beginDate) {
        throw new \Exception('wrong dates');
    }

    $qb
        ->leftJoin('a.rentals', 'rental')
        ->where('rental.beginDate > :endDate')
        ->orWhere('rental.endDate < :beginDate')
        ->orWhere('rental.beginDate IS NULL')
        ->orWhere('rental.endDate IS NULL')
        ->setParameter('beginDate', $beginDate->format('Y-m-d H:i:s'))
        ->setParameter('endDate', $endDate->format('Y-m-d H:i:s'));
}

这将返回所有在 beginDate-endDate 期间没有租金的广告

【讨论】:

    猜你喜欢
    • 2019-02-16
    • 2021-01-19
    • 1970-01-01
    • 2022-01-06
    • 2021-03-06
    • 2011-11-27
    • 1970-01-01
    • 1970-01-01
    • 2015-04-15
    相关资源
    最近更新 更多