Skip to content

Commit

Permalink
[BUGFIX] Respect DateTimeImmutable in Extbase
Browse files Browse the repository at this point in the history
The DateTimeConverter and DataMapper of Extbase now checks for
DateTimeInterface, and can thus handle DateTimeImmutable, otherwise
Extbase cannot handle these kinds of formats.

Resolves: #72053
Releases: master
Change-Id: Ic922e715a31e1d02f5f6daa18415e8376788da8b
Reviewed-on: https://review.typo3.org/57388
Tested-by: TYPO3com <no-reply@typo3.com>
Reviewed-by: Benni Mack <benni@typo3.org>
Tested-by: Benni Mack <benni@typo3.org>
Reviewed-by: Georg Ringer <georg.ringer@gmail.com>
Tested-by: Georg Ringer <georg.ringer@gmail.com>
  • Loading branch information
bmack authored and georgringer committed Nov 5, 2018
1 parent 2130b34 commit aabe5f7
Show file tree
Hide file tree
Showing 7 changed files with 208 additions and 14 deletions.
Expand Up @@ -302,10 +302,15 @@ protected function thawProperties(DomainObjectInterface $object, array $row)
$this->fetchRelated($object, $propertyName, $row[$columnName])
);
break;
case is_subclass_of($propertyData['type'], \DateTimeInterface::class):
$propertyValue = $this->mapDateTime(
$row[$columnName],
$columnMap->getDateTimeStorageFormat(),
$propertyData['type']
);
break;
default:
if ($propertyData['type'] === 'DateTime' || in_array('DateTime', class_parents($propertyData['type']))) {
$propertyValue = $this->mapDateTime($row[$columnName], $columnMap->getDateTimeStorageFormat(), $propertyData['type']);
} elseif (TypeHandlingUtility::isCoreType($propertyData['type'])) {
if (TypeHandlingUtility::isCoreType($propertyData['type'])) {
$propertyValue = $this->mapCoreType($propertyData['type'], $row[$columnName]);
} else {
$propertyValue = $this->mapObjectToClassProperty(
Expand Down Expand Up @@ -342,9 +347,9 @@ protected function mapCoreType($type, $value)
* @param int|string $value Unix timestamp or date/datetime/time value
* @param string|null $storageFormat Storage format for native date/datetime/time fields
* @param string|null $targetType The object class name to be created
* @return \DateTime
* @return \DateTimeInterface
*/
protected function mapDateTime($value, $storageFormat = null, $targetType = 'DateTime')
protected function mapDateTime($value, $storageFormat = null, $targetType = \DateTime::class)
{
$dateTimeTypes = QueryHelper::getDateTimeTypes();

Expand Down Expand Up @@ -739,7 +744,7 @@ public function getPlainValue($input, $columnMap = null)
$parameter = (int)$input;
} elseif (is_int($input)) {
$parameter = $input;
} elseif ($input instanceof \DateTime) {
} elseif ($input instanceof \DateTimeInterface) {
if ($columnMap !== null && $columnMap->getDateTimeStorageFormat() !== null) {
$storageFormat = $columnMap->getDateTimeStorageFormat();
$timeZoneToStore = clone $input;
Expand Down
Expand Up @@ -111,7 +111,7 @@ public function canConvertFrom($source, $targetType)
* @param string $targetType must be "DateTime"
* @param array $convertedChildProperties not used currently
* @param \TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface $configuration
* @return \DateTime|\TYPO3\CMS\Extbase\Error\Error
* @return \DateTimeInterface|\TYPO3\CMS\Extbase\Error\Error
* @throws \TYPO3\CMS\Extbase\Property\Exception\TypeConverterException
* @internal only to be used within Extbase, not part of TYPO3 Core API.
*/
Expand Down Expand Up @@ -159,7 +159,7 @@ public function convertFrom($source, $targetType, array $convertedChildPropertie
return new \TYPO3\CMS\Extbase\Validation\Error('The date "%s" was not recognized (for format "%s").', 1307719788, [$dateAsString, $dateFormat]);
}
if (is_array($source)) {
$this->overrideTimeIfSpecified($date, $source);
$date = $this->overrideTimeIfSpecified($date, $source);
}
return $date;
}
Expand Down Expand Up @@ -190,7 +190,7 @@ protected function getDefaultDateFormat(\TYPO3\CMS\Extbase\Property\PropertyMapp
if ($configuration === null) {
return self::DEFAULT_DATE_FORMAT;
}
$dateFormat = $configuration->getConfigurationValue(\TYPO3\CMS\Extbase\Property\TypeConverter\DateTimeConverter::class, self::CONFIGURATION_DATE_FORMAT);
$dateFormat = $configuration->getConfigurationValue(DateTimeConverter::class, self::CONFIGURATION_DATE_FORMAT);
if ($dateFormat === null) {
return self::DEFAULT_DATE_FORMAT;
}
Expand All @@ -203,17 +203,18 @@ protected function getDefaultDateFormat(\TYPO3\CMS\Extbase\Property\PropertyMapp
/**
* Overrides hour, minute & second of the given date with the values in the $source array
*
* @param \DateTime $date
* @param \DateTimeInterface $date
* @param array $source
* @return \DateTimeInterface
*/
protected function overrideTimeIfSpecified(\DateTime $date, array $source)
protected function overrideTimeIfSpecified(\DateTimeInterface $date, array $source): \DateTimeInterface
{
if (!isset($source['hour']) && !isset($source['minute']) && !isset($source['second'])) {
return;
return $date;
}
$hour = isset($source['hour']) ? (int)$source['hour'] : 0;
$minute = isset($source['minute']) ? (int)$source['minute'] : 0;
$second = isset($source['second']) ? (int)$source['second'] : 0;
$date->setTime($hour, $minute, $second);
return $date->setTime($hour, $minute, $second);
}
}
Expand Up @@ -20,7 +20,7 @@ class TypeHandlingUtility
/**
* A property type parse pattern.
*/
const PARSE_TYPE_PATTERN = '/^\\\\?(?P<type>integer|int|float|double|boolean|bool|string|DateTime|[A-Z][a-zA-Z0-9\\\\]+|object|resource|array|ArrayObject|SplObjectStorage|TYPO3\\\\CMS\\\\Extbase\\\\Persistence\\\\ObjectStorage)(?:<\\\\?(?P<elementType>[a-zA-Z0-9\\\\]+)>)?/';
const PARSE_TYPE_PATTERN = '/^\\\\?(?P<type>integer|int|float|double|boolean|bool|string|DateTimeImmutable|DateTime|[A-Z][a-zA-Z0-9\\\\]+|object|resource|array|ArrayObject|SplObjectStorage|TYPO3\\\\CMS\\\\Extbase\\\\Persistence\\\\ObjectStorage)(?:<\\\\?(?P<elementType>[a-zA-Z0-9\\\\]+)>)?/';

/**
* A type pattern to detect literal types.
Expand Down
@@ -0,0 +1,78 @@
<?php
declare(strict_types = 1);
namespace ExtbaseTeam\BlogExample\Domain\Model;

use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;

class DateTimeImmutableExample extends AbstractEntity
{

/**
* A datetimeImmutable stored in a text field
*
* @var \DateTimeImmutable
*/
protected $datetimeImmutableText;

/**
* A datetime stored in an integer field
*
* @var \DateTimeImmutable
*/
protected $datetimeImmutableInt;

/**
* A datetime stored in a datetime field
*
* @var \DateTimeImmutable
*/
protected $datetimeImmutableDatetime;

/**
* @return \DateTimeImmutable
*/
public function getDatetimeImmutableText(): \DateTimeImmutable
{
return $this->datetimeImmutableText;
}

/**
* @param \DateTimeImmutable $datetimeImmutableText
*/
public function setDatetimeImmutableText(\DateTimeImmutable $datetimeImmutableText)
{
$this->datetimeImmutableText = $datetimeImmutableText;
}

/**
* @return \DateTimeImmutable
*/
public function getDatetimeImmutableInt(): \DateTimeImmutable
{
return $this->datetimeImmutableInt;
}

/**
* @param \DateTimeImmutable $datetimeImmutableInt
*/
public function setDatetimeImmutableInt(\DateTimeImmutable $datetimeImmutableInt)
{
$this->datetimeImmutableInt = $datetimeImmutableInt;
}

/**
* @return \DateTimeImmutable
*/
public function getDatetimeImmutableDatetime(): \DateTimeImmutable
{
return $this->datetimeImmutableDatetime;
}

/**
* @param \DateTimeImmutable $datetimeImmutableDatetime
*/
public function setDatetimeImmutableDatetime(\DateTimeImmutable $datetimeImmutableDatetime)
{
$this->datetimeImmutableDatetime = $datetimeImmutableDatetime;
}
}
@@ -0,0 +1,40 @@
<?php
return [
'ctrl' => [
'title' => 'DateTimeImmutable Example',
'label' => 'title',
],
'columns' => [
'datetime_immutable_text' => [
'exclude' => 1,
'label' => 'eval=datetime, db=text',
'config' => [
'type' => 'input',
'renderType' => 'inputDateTime',
'eval' => 'datetime',
]
],
'datetime_immutable_int' => [
'exclude' => 1,
'label' => 'eval=datetime, db=int',
'config' => [
'type' => 'input',
'renderType' => 'inputDateTime',
'eval' => 'datetime',
]
],
'datetime_immutable_datetime' => [
'exclude' => 1,
'label' => 'eval=datetime, db=datetime',
'config' => [
'dbType' => 'datetime',
'type' => 'input',
'renderType' => 'inputDateTime',
'eval' => 'datetime',
]
]
],
'types' => [
'1' => ['showitem' => 'datetime_text', 'datetime_int', 'datetime_datetime']
],
];
Expand Up @@ -119,3 +119,11 @@ CREATE TABLE tx_blogexample_domain_model_info (
name varchar(255) DEFAULT '' NOT NULL,
post int(11) DEFAULT '0' NOT NULL
);

# Table structure for table 'tx_blogexample_domain_model_datetimeimmutableexample'
#
CREATE TABLE tx_blogexample_domain_model_datetimeimmutableexample (
datetime_immutable_int int(11) DEFAULT '0' NOT NULL,
datetime_immutable_text varchar(255) DEFAULT '' NOT NULL,
datetime_immutable_datetime datetime
);
Expand Up @@ -3,6 +3,7 @@

use ExtbaseTeam\BlogExample\Domain\Model\Comment;
use ExtbaseTeam\BlogExample\Domain\Model\DateExample;
use ExtbaseTeam\BlogExample\Domain\Model\DateTimeImmutableExample;
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
use TYPO3\CMS\Core\Utility\GeneralUtility;

Expand Down Expand Up @@ -60,6 +61,7 @@ public function datetimeObjectsCanBePersistedToDatetimeDatabaseFields()

$this->assertEquals($date->getTimestamp(), $existingComment->getDate()->getTimestamp());
}

/**
* @test
*/
Expand Down Expand Up @@ -119,4 +121,64 @@ public function dateValuesAreStoredInUtcInDatetimeDatabaseFields()

$this->assertEquals($example->getDatetimeDatetime()->getTimestamp(), $date->getTimestamp());
}

/**
* @test
*/
public function dateTimeImmutableIntIsHandledAsDateTime()
{
$subject = new DateTimeImmutableExample();
$date = new \DateTimeImmutable('2018-07-24T20:40:00');
$subject->setDatetimeImmutableInt($date);

$this->persistenceManager->add($subject);
$this->persistenceManager->persistAll();
$uid = $this->persistenceManager->getIdentifierByObject($subject);
$this->persistenceManager->clearState();

/** @var DateTimeImmutableExample $subject */
$subject = $this->persistenceManager->getObjectByIdentifier($uid, DateTimeImmutableExample::class);

$this->assertEquals($date, $subject->getDatetimeImmutableInt());
}

/**
* @test
*/
public function dateTimeImmutableTextIsHandledAsDateTime()
{
$subject = new DateTimeImmutableExample();
$date = new \DateTimeImmutable('2018-07-24T20:40:00');
$subject->setDatetimeImmutableText($date);

$this->persistenceManager->add($subject);
$this->persistenceManager->persistAll();
$uid = $this->persistenceManager->getIdentifierByObject($subject);
$this->persistenceManager->clearState();

/** @var DateTimeImmutableExample $subject */
$subject = $this->persistenceManager->getObjectByIdentifier($uid, DateTimeImmutableExample::class);

$this->assertEquals($date, $subject->getDatetimeImmutableText());
}

/**
* @test
*/
public function dateTimeImmutableDateTimeIsHandledAsDateTime()
{
$subject = new DateTimeImmutableExample();
$date = new \DateTimeImmutable('2018-07-24T20:40:00');
$subject->setDatetimeImmutableDatetime($date);

$this->persistenceManager->add($subject);
$this->persistenceManager->persistAll();
$uid = $this->persistenceManager->getIdentifierByObject($subject);
$this->persistenceManager->clearState();

/** @var DateTimeImmutableExample $subject */
$subject = $this->persistenceManager->getObjectByIdentifier($uid, DateTimeImmutableExample::class);

$this->assertSame($date->getTimestamp(), $subject->getDatetimeImmutableDatetime()->getTimestamp());
}
}

0 comments on commit aabe5f7

Please sign in to comment.