DoctrineExtensions icon indicating copy to clipboard operation
DoctrineExtensions copied to clipboard

Timestampable (and maybe more extensions) don't work with DBAL 4.0.0-RC2

Open Arkanii opened this issue 1 year ago • 12 comments

Environment

Package

show

$ composer show --latest gedmo/doctrine-extensions
name     : gedmo/doctrine-extensions
descrip. : Doctrine behavioral extensions
keywords : Blameable, behaviors, doctrine, extensions, gedmo, loggable, nestedset, odm, orm, sluggable, sortable, timestampable, translatable, tree, uploadable
versions : * v3.14.0
latest   : v3.14.0
type     : library
license  : MIT License (MIT) (OSI approved) https://spdx.org/licenses/MIT.html#licenseText
homepage : http://gediminasm.org/
source   : [git] https://github.com/doctrine-extensions/DoctrineExtensions.git 3b5b5cba476b4ae32a55ef69ef2e59d64d5893cf
dist     : [zip] https://api.github.com/repos/doctrine-extensions/DoctrineExtensions/zipball/3b5b5cba476b4ae32a55ef69ef2e59d64d5893cf 3b5b5cba476b4ae32a55ef69ef2e59d64d5893cf
path     : /app/vendor/gedmo/doctrine-extensions
names    : gedmo/doctrine-extensions

support
email : [email protected]
issues : https://github.com/doctrine-extensions/DoctrineExtensions/issues
source : https://github.com/doctrine-extensions/DoctrineExtensions/tree/v3.14.0
wiki : https://github.com/Atlantic18/DoctrineExtensions/tree/main/doc

autoload
psr-4
Gedmo\ => src/

requires
behat/transliterator ^1.2
doctrine/annotations ^1.13 || ^2.0
doctrine/collections ^1.2 || ^2.0
doctrine/common ^2.13 || ^3.0
doctrine/event-manager ^1.2 || ^2.0
doctrine/persistence ^2.2 || ^3.0
php ^7.4 || ^8.0
psr/cache ^1 || ^2 || ^3
symfony/cache ^5.4 || ^6.0 || ^7.0
symfony/deprecation-contracts ^2.1 || ^3.0

requires (dev)
doctrine/cache ^1.11 || ^2.0
doctrine/dbal ^3.2
doctrine/doctrine-bundle ^2.3
doctrine/mongodb-odm ^2.3
doctrine/orm ^2.14.0
friendsofphp/php-cs-fixer ^3.14.0
nesbot/carbon ^2.71 || 3.x-dev as 3.0
phpstan/phpstan ^1.10.2
phpstan/phpstan-doctrine ^1.0
phpstan/phpstan-phpunit ^1.0
phpunit/phpunit ^9.6
rector/rector ^0.18
symfony/console ^5.4 || ^6.0 || ^7.0
symfony/phpunit-bridge ^6.0 || ^7.0
symfony/yaml ^5.4 || ^6.0 || ^7.0

suggests
doctrine/mongodb-odm to use the extensions with the MongoDB ODM
doctrine/orm to use the extensions with the ORM

conflicts
doctrine/dbal <3.2
doctrine/mongodb-odm <2.3
doctrine/orm <2.14.0 || 2.16.0 || 2.16.1
sebastian/comparator <2.0

Doctrine packages

show

$ composer show --latest 'doctrine/*'
Color legend:
- patch or minor release available - update recommended
- major release available - update possible
- up to date version

Direct dependencies required in composer.json:
doctrine/dbal                       4.0.0-RC2 4.0.0-RC2 Powerful PHP database abstraction layer (DBAL) with many features for database schema introspection and management.
doctrine/doctrine-bundle            2.11.1    2.11.1    Symfony DoctrineBundle
doctrine/doctrine-migrations-bundle 3.3.0     3.3.0     Symfony DoctrineMigrationsBundle
doctrine/orm                        3.0.0-RC1 3.0.0-RC1 Object-Relational-Mapper for PHP

Transitive dependencies not required in composer.json:
doctrine/annotations                2.0.1     2.0.1     Docblock Annotations Parser
doctrine/cache                      2.2.0     2.2.0     PHP Doctrine Cache library is a popular cache implementation that supports many different drivers such as redis, memcache, apc, mongodb and others.
doctrine/collections                2.1.4     2.1.4     PHP Doctrine Collections library that adds additional functionality on top of PHP arrays.
doctrine/common                     3.4.3     3.4.3     PHP Doctrine Common project is a library that provides additional functionality that other Doctrine projects depend on such as better reflection support, proxies and mu...
doctrine/deprecations               1.1.3     1.1.3     A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.
doctrine/event-manager              2.0.0     2.0.0     The Doctrine Event Manager is a simple PHP event system that was built to be used with the various Doctrine projects.
doctrine/inflector                  2.0.9     2.0.9     PHP Doctrine Inflector is a small library that can perform string manipulations with regard to upper/lowercase and singular/plural forms of words.
doctrine/instantiator               2.0.0     2.0.0     A small, lightweight utility to instantiate objects in PHP without invoking their constructors
doctrine/lexer                      3.0.0     3.0.0     PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.
doctrine/migrations                 3.7.2     3.7.2     PHP Doctrine Migrations project offer additional functionality on top of the database abstraction layer (DBAL) for versioning your database schema and easily deploying ...
doctrine/persistence                3.2.0     3.2.0     The Doctrine Persistence project is a set of shared interfaces and functionality that the different Doctrine object mappers share.
doctrine/sql-formatter              1.1.3     1.1.3     a PHP SQL highlighting library

PHP version

$ php -v
PHP 8.3.2 (cli) (built: Jan 27 2024 04:34:00) (ZTS)
Copyright (c) The PHP Group
Zend Engine v4.3.2, Copyright (c) Zend Technologies
    with Zend OPcache v8.3.2, Copyright (c), by Zend Technologies
    with Xdebug v3.3.1, Copyright (c) 2002-2023, by Derick Rethans

Subject

With DBAL 4.0.0-RC2, there is a bug with Timestampable. I found why and I can provide a fix, but I think this bug can be on other extensions... That's why I only create a bug issue.

In DBAL 4.0.0-RC2, the method $this->getObjectManager()->getConnection()->getDriver()->getDatabasePlatform() now wait an argument, which is not provided for the moment, and the getFieldMapping function return an object instead of an array.

I found the bug in the src/Timestampable/Mapping/Event/Adapter/ORM.php file.

Actual version :

show

<?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\Timestampable\Mapping\Event\Adapter;

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

/**
 * Doctrine event adapter for ORM adapted
 * for Timestampable behavior
 *
 * @author Gediminas Morkevicius <[email protected]>
 */
final class ORM extends BaseAdapterORM implements TimestampableAdapter
{
    /**
     * @param ClassMetadata $meta
     */
    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)
    {
        $datetime = new \DateTime();
        $type = $mapping['type'] ?? null;

        if ('integer' === $type) {
            return (int) $datetime->format('U');
        }

        if (in_array($type, ['date_immutable', 'time_immutable', 'datetime_immutable', 'datetimetz_immutable'], true)) {
            return \DateTimeImmutable::createFromMutable($datetime);
        }

        return $datetime;
    }
}

Fixed version :

show

<?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\Timestampable\Mapping\Event\Adapter;

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

/**
 * Doctrine event adapter for ORM adapted
 * for Timestampable behavior
 *
 * @author Gediminas Morkevicius <[email protected]>
 */
final class ORM extends BaseAdapterORM implements TimestampableAdapter
{
    /**
     * @param ClassMetadata $meta
     */
    public function getDateValue($meta, $field)
    {
        $mapping = $meta->getFieldMapping($field);
        $converter = Type::getType($mapping['type'] ?? Types::DATETIME_MUTABLE);
        $platform = $this->getObjectManager()->getConnection()->getDriver()->getDatabasePlatform($this->getObjectManager()->getConnection());

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

    /**
     * Generates current timestamp for the specified mapping
     *
     * @param FieldMapping $mapping
     *
     * @return \DateTimeInterface|int
     */
    private function getRawDateValue(FieldMapping $mapping)
    {
        $datetime = new \DateTime();
        $type = $mapping->type ?? null;

        if ('integer' === $type) {
            return (int) $datetime->format('U');
        }

        if (in_array($type, ['date_immutable', 'time_immutable', 'datetime_immutable', 'datetimetz_immutable'], true)) {
            return \DateTimeImmutable::createFromMutable($datetime);
        }

        return $datetime;
    }
}

This fix work on my side.

I'll be happy to help if needed !

Minimal repository with the bug

I'll do it if I have some free time, I apologize to haven't created it for now.

Steps to reproduce

Just create a project that use DBAL 4.0.0-RC2 instead of 3.x.x and use Timestampable on a updated_at property and try to save a entity.

Expected results

{"@context":"\/api\/contexts\/Todo","@id":"\/api\/todos\/10","@type":"Todo","id":10,"name":"Test","done":false,"createdAt":"2024-02-01T18:07:54+00:00","updatedAt":"2024-02-01T18:07:54+00:00"}

Actual results

"Too few arguments to function Doctrine\\DBAL\\Driver\\Middleware\\AbstractDriverMiddleware::getDatabasePlatform(), 0 passed in /app/vendor/gedmo/doctrine-extensions/src/Timestampable/Mapping/Event/Adapter/ORM.php on line 33 and exactly 1 expected"

Thanks ! 😄

Arkanii avatar Feb 01 '24 21:02 Arkanii

DBAL 4.0 isn't supported right now and probably won't be for a while (IIRC ORM 2.x isn't getting support for it, so ORM 3.x compat has to be sorted out first).

mbabker avatar Feb 02 '24 19:02 mbabker

The solution is actually to use the high-level API to access the platform (which has not change between 3.x and 4.x) instead of reaching for the low-level driver.

stof avatar Feb 12 '24 18:02 stof

It seems the Symfony 7 generator uses DBAL 4.0, this "bug" will annoy many people (I'm one of them ;)).

conradfr avatar Feb 16 '24 14:02 conradfr

Note that the Symfony generator should be forcing DBAL 3.x for now since a few days.

stof avatar Feb 16 '24 15:02 stof

Hi folks, I've installed Doctrine manually into a Laravel application and I'm experiencing the same problem.

DBAL 4.0 isn't supported right now and probably won't be for a while....

@mbabker is there a roadmap I could look at? Failing that, do you have any idea when DBAL 4.x will be implemented?

I'll build a workaround for my particular scenario.

joshmurrayeu avatar Apr 09 '24 14:04 joshmurrayeu

@mbabker is there a roadmap I could look at? Failing that, do you have any idea when DBAL 4.x will be implemented?

As this package is entirely supported by volunteers, there's no roadmap or timeline. But, the gist of what's required is basically to finish ORM 3 compat (as ORM 2 and DBAL 4 are not usable together, so there's nothing that can really be done to test this at the moment) then start working on DBAL 4 compatibility.

The fix noted in https://github.com/doctrine-extensions/DoctrineExtensions/issues/2752#issuecomment-1939313831 (basically changing any calls similar to $this->getObjectManager()->getConnection()->getDriver()->getDatabasePlatform() to $this->getObjectManager()->getConnection()->getDatabasePlatform()) could be sent as a PR at any time to fix this specific issue, but issues like https://github.com/doctrine-extensions/DoctrineExtensions/issues/2502 are pretty big blockers to allowing DBAL 4 (and if I'm being honest, my gut feeling says that might not come without a major release of this package considering what would be involved to resolve that one).

mbabker avatar Apr 09 '24 15:04 mbabker

I also just ran into this issue - it would be helpful to make a note what the extension is and is not compatible with in the README.md so people don't get started with the extension only to run into the same error (especially if the incompatibility issue 2502 is almost 2 years old)

fwiw @Arkanii I was able to get the Timestampable annotation working with your change and one other - here's a composer patch file, maybe it'll help somebody out. gedmo-doctrine-extenstions-timestampable.patch

nbennett25 avatar Jun 04 '24 15:06 nbennett25

I also just ran into this issue - it would be helpful to make a note what the extension is and is not compatible with in the README.md so people don't get started with the extension only to run into the same error

The best place to look at compatibility is going to be the composer.json file because that's what Composer is going to use to figure out what to install. Putting compatibility info in README files or other docs files ends up in info being outdated pretty easily (like I just learned that there's a table that tries to convey what versions of doctrine/orm and doctrine/common some things are compatible with, and that table's massively out-of-date and honestly not well presented (the two libraries have different versioning schemes, so trying to say it's compatible with both like the header implies isn't great).

especially if the incompatibility issue 2502 is almost 2 years old

And as that issue shows, there's actually a patch that can help fix that particular compatibility issue from a pure code perspective. Except it's not easy to ship it because the changes require data migrations for downstream users (the DBAL's object and array types store data using PHP's serialize() function, whereas the JSON type uses native JSON support, so it's not just a matter applying that patch and shipping the change but figuring out how to deal with any existing data as well).

I was able to get the Timestampable extension working with your change and one other - here's a composer patch file, maybe it'll help somebody out.

A pull request for the first change in that patch would be hugely appreciated instead of just patching your local app. As for the second change, a proper fix has already landed in this repository, so it's not necessary with the latest release.

mbabker avatar Jun 04 '24 16:06 mbabker

@nbennett25 patch works, thanks

but still, it needs fixing :|

jgwiazdowski avatar Jun 06 '24 08:06 jgwiazdowski

Is it planned to update dbal > 4.0.0 in the upcoming release?

michaelukolovrus avatar Jun 27 '24 13:06 michaelukolovrus

Is it planned to update dbal > 4.0.0 in the upcoming release?

Eventually, yes. Unfortunately, there is a huge task involved in that (see #2502 and #2825).

mbabker avatar Jun 27 '24 15:06 mbabker

any progress ?

piotrp321 avatar Aug 24 '24 09:08 piotrp321