Skip to content

Commit

Permalink
Convert DateTime to PHP value using Doctrine converter
Browse files Browse the repository at this point in the history
  • Loading branch information
k0ka authored Dec 31, 2021
1 parent 907cd6e commit 609a763
Show file tree
Hide file tree
Showing 8 changed files with 685 additions and 1 deletion.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ a release.
- Tree: Support to use annotations as attributes on PHP >= 8.0.
- References: Support to use annotations as attributes on PHP >= 8.0.
- ReferenceIntegrity: Support to use annotations as attributes on PHP >= 8.0.
- SoftDeleteable: Support for custom column types (like Carbon).
- Timestampable: Support for custom column types (like Carbon).

### Fixed
- Blameable, IpTraceable, Timestampable: Type handling for the tracked field values configured in the origin field.
Expand Down
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
"doctrine/mongodb-odm": "^2.0",
"doctrine/orm": "^2.10.2",
"friendsofphp/php-cs-fixer": "^3.0",
"nesbot/carbon": "^2.55",
"phpstan/phpstan": "^1.1",
"phpstan/phpstan-doctrine": "^1.0",
"phpstan/phpstan-phpunit": "^1.0",
Expand Down
20 changes: 19 additions & 1 deletion src/SoftDeleteable/Mapping/Event/Adapter/ORM.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@

namespace Gedmo\SoftDeleteable\Mapping\Event\Adapter;

use Doctrine\DBAL\Types\Type;
use Doctrine\DBAL\Types\Types;
use Gedmo\Mapping\Event\Adapter\ORM as BaseAdapterORM;
use Gedmo\SoftDeleteable\Mapping\Event\SoftDeleteableAdapter;

Expand All @@ -21,14 +23,30 @@
final class ORM extends BaseAdapterORM implements SoftDeleteableAdapter
{
/**
* {@inheritDoc}
* {@inheritdoc}
*/
public function getDateValue($meta, $field)
{
$mapping = $meta->getFieldMapping($field);
$converter = Type::getType($mapping['type'] ?? Types::DATETIME_MUTABLE);
$platform = $this->getObjectManager()->getConnection()->getDriver()->getDatabasePlatform();

return $converter->convertToPHPValue($this->getRawDateValue($mapping), $platform);
}

/**
* Generates current timestamp for the specified mapping
*
* @param array<string, mixed> $mapping
*
* @return \DateTimeInterface|int
*/
private function getRawDateValue(array $mapping)
{
if (isset($mapping['type']) && 'integer' === $mapping['type']) {
return time();
}

if (isset($mapping['type']) && in_array($mapping['type'], ['date_immutable', 'time_immutable', 'datetime_immutable', 'datetimetz_immutable'], true)) {
return new \DateTimeImmutable();
}
Expand Down
18 changes: 18 additions & 0 deletions src/Timestampable/Mapping/Event/Adapter/ORM.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@

namespace Gedmo\Timestampable\Mapping\Event\Adapter;

use Doctrine\DBAL\Types\Type;
use Doctrine\DBAL\Types\Types;
use Gedmo\Mapping\Event\Adapter\ORM as BaseAdapterORM;
use Gedmo\Timestampable\Mapping\Event\TimestampableAdapter;

Expand All @@ -26,9 +28,25 @@ final class ORM extends BaseAdapterORM implements TimestampableAdapter
public function getDateValue($meta, $field)
{
$mapping = $meta->getFieldMapping($field);
$converter = Type::getType($mapping['type'] ?? Types::DATETIME_MUTABLE);
$platform = $this->getObjectManager()->getConnection()->getDriver()->getDatabasePlatform();

return $converter->convertToPHPValue($this->getRawDateValue($mapping), $platform);
}

/**
* Generates current timestamp for the specified mapping
*
* @param array<string, mixed> $mapping
*
* @return \DateTimeInterface|int
*/
private function getRawDateValue(array $mapping)
{
if (isset($mapping['type']) && 'integer' === $mapping['type']) {
return time();
}

if (isset($mapping['type']) && in_array($mapping['type'], ['date_immutable', 'time_immutable', 'datetime_immutable', 'datetimetz_immutable'], true)) {
return new \DateTimeImmutable();
}
Expand Down
107 changes: 107 additions & 0 deletions tests/Gedmo/SoftDeleteable/CarbonTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
<?php

/*
* This file is part of the Doctrine Behavioral Extensions package.
* (c) Gediminas Morkevicius <[email protected]> http://www.gediminasm.org
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Gedmo\Tests\SoftDeleteable;

use Carbon\Carbon;
use Carbon\Doctrine\DateTimeType;
use Doctrine\Common\EventManager;
use Doctrine\DBAL\Types\Type as DoctrineType;
use Doctrine\DBAL\Types\Types;
use Gedmo\SoftDeleteable\Filter\SoftDeleteableFilter;
use Gedmo\SoftDeleteable\SoftDeleteableListener;
use Gedmo\Tests\SoftDeleteable\Fixture\Entity\Article;
use Gedmo\Tests\SoftDeleteable\Fixture\Entity\Comment;
use Gedmo\Tests\Tool\BaseTestCaseORM;

final class CarbonTest extends BaseTestCaseORM
{
public const ARTICLE_CLASS = Article::class;
public const COMMENT_CLASS = Comment::class;
public const SOFT_DELETEABLE_FILTER_NAME = 'soft-deleteable';

/**
* @var SoftDeleteableListener
*/
private $softDeleteableListener;

protected function setUp(): void
{
parent::setUp();

$evm = new EventManager();
$this->softDeleteableListener = new SoftDeleteableListener();
$evm->addEventSubscriber($this->softDeleteableListener);
$config = $this->getDefaultConfiguration();
$config->addFilter(self::SOFT_DELETEABLE_FILTER_NAME, SoftDeleteableFilter::class);
$this->em = $this->getDefaultMockSqliteEntityManager($evm, $config);
$this->em->getFilters()->enable(self::SOFT_DELETEABLE_FILTER_NAME);

DoctrineType::overrideType(Types::DATETIME_MUTABLE, DateTimeType::class);
}

protected function tearDown(): void
{
parent::tearDown();

DoctrineType::overrideType(Types::DATETIME_MUTABLE, \Doctrine\DBAL\Types\DateTimeType::class);
}

public function testSoftDeleteable(): void
{
$repo = $this->em->getRepository(self::ARTICLE_CLASS);
$commentRepo = $this->em->getRepository(self::COMMENT_CLASS);

$comment = new Comment();
$commentField = 'comment';
$commentValue = 'Comment 1';
$comment->setComment($commentValue);
$art0 = new Article();
$field = 'title';
$value = 'Title 1';
$art0->setTitle($value);
$art0->addComment($comment);

$this->em->persist($art0);
$this->em->flush();

$art = $repo->findOneBy([$field => $value]);

static::assertNull($art->getDeletedAt());
static::assertNull($comment->getDeletedAt());

$this->em->remove($art);
$this->em->flush();

$art = $repo->findOneBy([$field => $value]);
static::assertNull($art);
$comment = $commentRepo->findOneBy([$commentField => $commentValue]);
static::assertNull($comment);

// Now we deactivate the filter so we test if the entity appears in the result
$this->em->getFilters()->disable(self::SOFT_DELETEABLE_FILTER_NAME);

$art = $repo->findOneBy([$field => $value]);
static::assertIsObject($art);
static::assertIsObject($art->getDeletedAt());
static::assertInstanceOf(Carbon::class, $art->getDeletedAt());
$comment = $commentRepo->findOneBy([$commentField => $commentValue]);
static::assertIsObject($comment);
static::assertIsObject($comment->getDeletedAt());
static::assertInstanceOf(Carbon::class, $comment->getDeletedAt());
}

protected function getUsedEntityFixtures(): array
{
return [
self::ARTICLE_CLASS,
self::COMMENT_CLASS,
];
}
}
161 changes: 161 additions & 0 deletions tests/Gedmo/Timestampable/CarbonTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
<?php

/*
* This file is part of the Doctrine Behavioral Extensions package.
* (c) Gediminas Morkevicius <[email protected]> http://www.gediminasm.org
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Gedmo\Tests\Timestampable;

use Carbon\Carbon;
use Carbon\CarbonImmutable;
use Carbon\Doctrine\DateTimeImmutableType;
use Carbon\Doctrine\DateTimeType;
use DateTime;
use Doctrine\Common\EventManager;
use Doctrine\DBAL\Types\DateType;
use Doctrine\DBAL\Types\Type as DoctrineType;
use Doctrine\DBAL\Types\Types;
use Gedmo\Tests\Timestampable\Fixture\ArticleCarbon;
use Gedmo\Tests\Timestampable\Fixture\Author;
use Gedmo\Tests\Timestampable\Fixture\CommentCarbon;
use Gedmo\Tests\Timestampable\Fixture\Type;
use Gedmo\Tests\Tool\BaseTestCaseORM;
use Gedmo\Timestampable\TimestampableListener;

final class CarbonTest extends BaseTestCaseORM
{
public const ARTICLE = ArticleCarbon::class;
public const COMMENT = CommentCarbon::class;
public const TYPE = Type::class;

protected function setUp(): void
{
parent::setUp();

$evm = new EventManager();
$evm->addEventSubscriber(new TimestampableListener());

$this->getDefaultMockSqliteEntityManager($evm);

/**
* DATE_MUTABLE => Carbon
* DATETIME_MUTABLE => CarbonImmutable
* TIME_MUTABLE => DateTime
*/
DoctrineType::overrideType(Types::DATE_MUTABLE, DateTimeType::class);
DoctrineType::overrideType(Types::DATETIME_MUTABLE, DateTimeImmutableType::class);
}

protected function tearDown(): void
{
parent::tearDown();

DoctrineType::overrideType(Types::DATE_MUTABLE, DateType::class);
DoctrineType::overrideType(Types::DATETIME_MUTABLE, \Doctrine\DBAL\Types\DateTimeType::class);
}

public function testShouldHandleStandardBehavior(): void
{
$sport = new ArticleCarbon();
$sport->setTitle('Sport');
$sport->setBody('Sport article body.');

$sportComment = new CommentCarbon();
$sportComment->setMessage('hello');
$sportComment->setArticle($sport);
$sportComment->setStatus(0);

$author = new Author();
$author->setName('Original author');
$author->setEmail('[email protected]');

$sport->setAuthor($author);

$this->em->persist($sport);
$this->em->persist($sportComment);
$this->em->flush();

/** @var ArticleCarbon $sport */
$sport = $this->em->getRepository(self::ARTICLE)->findOneBy(['title' => 'Sport']);
static::assertInstanceOf(CarbonImmutable::class, $sport->getUpdated(), 'Type DATETIME_MUTABLE should become CarbonImmutable');
static::assertInstanceOf(Carbon::class, $sport->getCreated(), 'Type DATE_MUTABLE should become Carbon');

static::assertNotNull($sc = $sport->getCreated());
static::assertNotNull($su = $sport->getUpdated());
static::assertNull($sport->getContentChanged());
static::assertNull($sport->getPublished());
static::assertNull($sport->getAuthorChanged());

$author = $sport->getAuthor();
$author->setName('New author');
$sport->setAuthor($author);

/** @var \Gedmo\Tests\Timestampable\Fixture\CommentCarbon $sportComment */
$sportComment = $this->em->getRepository(self::COMMENT)->findOneBy(['message' => 'hello']);
static::assertInstanceOf(DateTime::class, $sportComment->getModified(), 'Type TIME_MUTABLE should stay DateTime');

static::assertNotNull($scm = $sportComment->getModified());
static::assertNull($sportComment->getClosed());

$sportComment->setStatus(1);
$published = new Type();
$published->setTitle('Published');

$sport->setType($published);
$this->em->persist($sport);
$this->em->persist($published);
$this->em->persist($sportComment);
$this->em->flush();

$sportComment = $this->em->getRepository(self::COMMENT)->findOneBy(['message' => 'hello']);
static::assertInstanceOf(CarbonImmutable::class, $sportComment->getClosed(), 'Type DATETIME_MUTABLE should become CarbonImmutable');
static::assertInstanceOf(CarbonImmutable::class, $sport->getPublished(), 'Type DATETIME_MUTABLE should become CarbonImmutable');
static::assertInstanceOf(CarbonImmutable::class, $sport->getAuthorChanged(), 'Type DATETIME_MUTABLE should become CarbonImmutable');

static::assertNotNull($scc = $sportComment->getClosed());
static::assertNotNull($sp = $sport->getPublished());
static::assertNotNull($sa = $sport->getAuthorChanged());

$sport->setTitle('Updated');
$this->em->persist($sport);
$this->em->persist($published);
$this->em->persist($sportComment);
$this->em->flush();

static::assertSame($sport->getCreated(), $sc, 'Date created should remain same after update');
static::assertNotSame($su2 = $sport->getUpdated(), $su, 'Date updated should change after update');
static::assertInstanceOf(CarbonImmutable::class, $sport->getUpdated(), 'Type DATETIME_MUTABLE should become CarbonImmutable');
static::assertSame($sport->getPublished(), $sp, 'Date published should remain the same after update');
static::assertNotSame($scc2 = $sport->getContentChanged(), $scc, 'Content must have changed after update');
static::assertInstanceOf(CarbonImmutable::class, $sport->getContentChanged(), 'Type DATETIME_MUTABLE should become CarbonImmutable');
static::assertSame($sport->getAuthorChanged(), $sa, 'Author should remain same after update');

$author = $sport->getAuthor();
$author->setName('Third author');
$sport->setAuthor($author);

$sport->setBody('Body updated');
$this->em->persist($sport);
$this->em->persist($published);
$this->em->persist($sportComment);
$this->em->flush();

static::assertSame($sport->getCreated(), $sc, 'Date created should remain same after update');
static::assertNotSame($sport->getUpdated(), $su2, 'Date updated should change after update');
static::assertSame($sport->getPublished(), $sp, 'Date published should remain the same after update');
static::assertNotSame($sport->getContentChanged(), $scc2, 'Content must have changed after update');
static::assertNotSame($sport->getAuthorChanged(), $sa, 'Author must have changed after update');
}

protected function getUsedEntityFixtures(): array
{
return [
self::ARTICLE,
self::COMMENT,
self::TYPE,
];
}
}
Loading

0 comments on commit 609a763

Please sign in to comment.