在我的Symfony2存储库中,我想从时间表中获取已经开始但尚未完成的对象。对象内的间隔应作为"未完成"遇到,应作为变量传递。
使用纯 SQL,它的工作原理如下:
SELECT * FROM slots rbs
WHERE rbs.rundate = '2012-08-13'
AND rbs.runtime <= '11:05:00'
AND '11:05:00' <= rbs.runtime + interval '300 seconds'
ORDER BY rbs.rundate DESC, rbs.runtime DESC
我可以使用 DQL/查询生成器实现相同的目标吗?
这是我到目前为止所拥有的:
$qb = $this->createQueryBuilder('rbs');
$qb->where(
$qb->expr()->andX(
$qb->expr()->eq('rbs.rundate', ':date'),
$qb->expr()->lte('rbs.runtime', ':time'),
'rbs.runtime + interval 300 seconds >= :time'
)
)
->orderBy('rbs.rundate', 'DESC')
->addOrderBy('rbs.runtime', 'DESC')
->setParameter('date', date('Y-m-d'))
->setParameter('time', date('H:i:s'))
但这会返回以下错误:
[DoctrineORMQueryQueryException]
[Syntax Error] line 0, col 139: Error: Expected =, <, <=, <>, >, >=, !=, got '300'
我发现 Doctrine2/DQL 不支持"间隔",这里也提到了这一点。
关于如何使用 Doctrine2 的查询生成器或 DQL(并将间隔作为变量传递)完成此操作的任何建议?
据我所知,Interval没有移植到Doctrine中。我发现的解决方法是直接处理我作为参数传递的日期时间(在这里,我想使用 2 天的间隔,通过日期时间传递):
public function findOngoingPublicEvents()
{
return $this->createQueryBuilder('e')
->where('e.isActive = 1')
->andWhere('e.isPublic = 1')
->andWhere('e.begin <= :begin')
->andWhere('e.end >= :end')
->orderBy('e.id', 'ASC')
->setParameter('begin', new DateTime('+2 days'))
->setParameter('end', new DateTime('-2 days'))
->getQuery()
->execute();
}
在mysql comumn字段上使用INTERVAL(在Doctrine 2,DQL)中,你可以使用如下,
$qb->andWhere("DATE_ADD(pv.myDAte,48,'hour') >= UTC_TIMESTAMP()");
它将打印如下 SQL,
...... DATE_ADD(p0_.appointment_date, INTERVAL 48 HOUR) >= UTC_TIMESTAMP() .....
@Kiran只写DATE_ADD
,但你也可以使用DATE_SUB
$qb->andWhere("DATE(a2_.updatedAt) = DATE_SUB(CURRENT_DATE(), 6, 'day')");
它等效于 SQL:
DATE(a2_.updatedAt) = DATE_SUB(CURRENT_DATE, INTERVAL 6 DAY)
AFAIK 在教义中,但不支持 postgres 间隔。但是,可以编写列的自定义映射以及 dbal 类型。下面是一个示例:
#[ORMColumn(type: 'carbon_interval', columnDefinition: 'INTERVAL NOT NULL')]
private CarbonInterval $duration;
碳包装类型:
#[Immutable]
final class CarbonIntervalPostgresType extends DateIntervalPostgresPostgresType
{
public const NAME = 'carbon_interval';
public function convertToPHPValue($value, AbstractPlatform $platform): ?CarbonInterval
{
//1 years 0 mons 0 days 0 hours 0 mins 0.0 secs
$interval = parent::convertToPHPValue($value, $platform);
if ($interval instanceof DateInterval) {
$interval = CarbonInterval::instance($interval);
}
return $interval;
}
}
覆盖dateinterval
学说类型以使用 postgres 格式。请注意,在现有的代码库中,它可能会破坏很多东西,所以要小心,如果你不确定,不要覆盖。
#[Immutable]
class DateIntervalPostgresPostgresType extends IntervalPostgresType
{
public const NAME = Types::DATEINTERVAL;
}
下一个类负责提供适当的postgresql间隔格式和php DateInterval转换。顺便说一句,如果你定义getSQLDeclaration
,那么你甚至不需要手动编写列定义。
#[Immutable]
class IntervalPostgresType extends Type
{
public const NAME = 'interval';
/** @see DoctrineDBALTypesType::getName() */
public function getName(): string
{
return static::NAME;
}
/** @see DoctrineDBALTypesType::getSQLDeclaration() */
public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform): string
{
return static::NAME;
}
/** @see DoctrineDBALTypesType::convertToDatabaseValue() */
public function convertToDatabaseValue($value, AbstractPlatform $platform)
{
if ($value === null) {
return null;
}
if (!$value instanceof DateInterval) {
throw new InvalidArgumentException('Interval value must be instance of DateInterval');
}
$parts = array(
'y' => 'year',
'm' => 'month',
'd' => 'day',
'h' => 'hour',
'i' => 'minute',
's' => 'second',
);
$sql = '';
foreach ($parts as $key => $part) {
$val = $value->{$key};
if (empty($val)) {
continue;
}
$sql .= " {$val} {$part}";
}
return trim($sql);
}
/**
* @throws ConversionException
* @see DoctrineDBALTypesType::convertToPHPValue()
*/
public function convertToPHPValue($value, AbstractPlatform $platform): ?DateInterval
{
if ($value === null) {
return null;
}
$matches = array();
preg_match(
'/(?:(?P<y>[0-9]+) (?:year|years))?'
.' ?(?:(?P<m>[0-9]+) (?:months|month|mons|mon))?'
.' ?(?:(?P<d>[0-9]+) (?:days|day))?'
.' ?(?:(?P<h>[0-9]{2}):(?P<i>[0-9]{2}):(?P<s>[0-9]{2}))?/i',
$value,
$matches
);
if (empty($matches)) {
throw ConversionException::conversionFailed($value, static::NAME);
}
$interval = new DateInterval('PT0S');
if (!empty($matches['y'])) {
$interval->y = (int)$matches['y'];
}
if (!empty($matches['m'])) {
$interval->m = (int)$matches['m'];
}
if (!empty($matches['d'])) {
$interval->d = (int)$matches['d'];
}
if (!empty($matches['h'])) {
$interval->h = (int)$matches['h'];
}
if (!empty($matches['i'])) {
$interval->i = (int)$matches['i'];
}
if (!empty($matches['s'])) {
$interval->s = (int)$matches['s'];
}
return $interval;
}
}