Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

restore comment hint (useful for enumtype declaration) #6444

Closed
wants to merge 4 commits into from

Conversation

xkzl
Copy link

@xkzl xkzl commented Jun 17, 2024

Bug Report

Q A
Version 4.0.0

Summary

I add/register my custom EnumType at early stage to doctrine and then I am used to add proper comment hint for EnumType to allow for conversion from StringType to correct EnumType using DC2Type at EnumSubscriber::postGenerateSchema event stage; Doctrine was using CommentHint and it was useful. This has been removed from dbal in the commit 4134b86cb986f6fc68fe9ba7c8a7416debfa22bd for some reasons.

I cannot find any alternative, but rolling back a minimal code reintroducing comment hint to DC2Type detection for Doctrine to Type conversion. Not doing so creates a diff in the DB schema resulting in a systematic update of the database scheme every time I launch a doctrine:schema:update command, due to fictive difference in the type of the columns.

<?php

namespace App\DatabaseSubscriber;

use App\Database\Type\EnumType;
use Doctrine\DBAL\Schema\Column;
use Doctrine\ORM\Tools\Event\GenerateSchemaEventArgs;

class EnumSubscriber
{
    public function postGenerateSchema(GenerateSchemaEventArgs $eventArgs)
    {
        $columns = [];

        foreach ($eventArgs->getSchema()->getTables() as $table) {
            foreach ($table->getColumns() as $column) {
                if ($column->getType() instanceof EnumType) {
                    $columns[] = $column;
                }
            }
        }

        /** @var Column $column */
        foreach ($columns as $column) {
            
            $enum = $column->getType();
            $column->setComment(trim(sprintf('%s (%s)', $column->getComment(), implode(',', $enum::getPermittedValues()))));
        }
    }
}

Current behaviour

Current behavior, makes db schema update infinitely refreshing.

How to reproduce

Just create a EnumType following; and use postGenerateSchema subscriber.

Expected behaviour

I would expect to restore in AbstractSchemaManager.php:

    public function extractDoctrineTypeFromComment($comment, $currentType)

Then restore on every platform,

        $type = $this->platform->getDoctrineTypeMapping($dbType);
        $type                   = $this->extractDoctrineTypeFromComment($tableColumn['comment'], $type);

Unless this was suppressed on purpose, it was very useful feature to incorporate EnumType.
If an alternative is possible, please advice.

@xkzl
Copy link
Author

xkzl commented Jun 17, 2024

I have restore the minimal code to make use of comment hint to detect EnumType based on (DC2Type:[xxx]) information as it was implemented before 4134b86cb986f6fc68fe9ba7c8a7416debfa22bd.
It allows to fix this issue implementing EnumType using EventListener::postGenerationSchema().

Please consider it, this was very useful feature and removed without much information to fix it.

@berkut1
Copy link
Contributor

berkut1 commented Jun 17, 2024

But can't you solve your problem by creating custom types?
https://www.doctrine-project.org/projects/doctrine-dbal/en/4.0/reference/types.html#custom-mapping-types

I use EnumType extensively in my project through custom types, and when testing with dbal4, I had no problems, and there shouldn't be any, because for Doctrine, it will still be int or string (depending on which EnumType you use).
The only thing you need to do when creating a type is to inherit from the parent Type and implement convertToDatabaseValue(), convertToPHPValue(), getSQLDeclaration(), and getBindingType().

@xkzl
Copy link
Author

xkzl commented Jun 18, 2024

Adding the type to doctrine is not a problem, this is standard in the documentation.

The problem lies in the conversion back from doctrine to type. Previous behavior was to look for (DC2Type:XXX) in the comment hint.
At the moment, the field gets back into 'StringType' instead of the correct 'EnumType', so on request the db schema gets indefinitely upgraded as the diff is triggering update.

@berkut1
Copy link
Contributor

berkut1 commented Jun 18, 2024

Sorry if I misunderstood you, but the Doctrine looks at the type in the same way as it did with DC2Type, that is, you need to create and register the type you need (custom type).

UPD: If you are getting StringType, it means that you did not create your custom type or did not update it after switching to DBAL4.

@xkzl
Copy link
Author

xkzl commented Jun 18, 2024

Could you please provide an example?

I already highlighted the suppressed lines that are problematic. I don't think there are strong motivations to explain why comment hints have been removed. Looking at the commit there is actually not much explanation of that part and why comment hint has been removed. It seems to be a mixed modification wrapped up in another commit.

I cannot get this EnumType working, but I am sure there must be a way if you say so. I would be happy to give it a try. I added the EnumType to Doctrine configuration, I can use EnumType but this issue is taking place when going from Doctrine to Type. If you can suggest a doctrine event to listen to I would be happy to use it and to have this configured in time, but without this, the only solution I have is to use comment hint.

Comment hint is a quite reasonable feature and using hints in various context is a common practice including for queries. I don't see why we should remove a useful feature if it gives some flexibility to the implementation.

@xkzl
Copy link
Author

xkzl commented Jun 18, 2024

#6257

@xkzl
Copy link
Author

xkzl commented Jun 18, 2024

All checks passed but codecov. (is that some key i should be providing ?)
Any maintainer please advice 🙏

@greg0ire
Copy link
Member

Regarding CodeCov, you have nothing to do, I reported it here: codecov/codecov-action#1487

@greg0ire
Copy link
Member

Regarding your issue, I think it might have been fixed with last week's 3.8.5 and 4.0.3 release. Have you tried upgrading, and disabling type comments?

@berkut1
Copy link
Contributor

berkut1 commented Jun 18, 2024

@greg0ire As I understand he uses DBAL4, not DBAL3.

@xkzl There is my custom type for int Enums.

enum Status: int
{
    case ACTIVE = 1;
    case SUSPENDED = 2;
    case ARCHIVED = 3;

    #[Pure]
    public function label(): string
    {
        return self::getLabel($this);
    }

    private function getLabel(self $value): string
    {
        return match ($value) {
            self::ACTIVE => 'STATUS_ACTIVE',
            self::SUSPENDED => 'STATUS_SUSPENDED',
            self::ARCHIVED => 'STATUS_ARCHIVED',
        };
    }

    public static function lists(): array
    {
        $cases = self::cases();
        $statuses = [];
        foreach ($cases as $status) {
            $statuses[$status->value] = $status->label();
        }
        return $statuses;
    }
}
final class StatusType extends Type
{
    final public const string NAME = 'user_user_status';

    #[\Override]
    public function convertToDatabaseValue(mixed $value, AbstractPlatform $platform): int
    {
        return $value instanceof Status ? $value->value : $value;
    }

    #[\Override]
    public function convertToPHPValue(mixed $value, AbstractPlatform $platform): ?Status
    {
        return !empty($value) ? Status::from($value) : null;
    }

    #[\Override]
    public function getSQLDeclaration(array $column, AbstractPlatform $platform): string
    {
        return $platform->getSmallIntTypeDeclarationSQL($column);
    }

    #[\Override]
    public function getBindingType(): int
    {
        return ParameterType::INTEGER;
    }

    public function getName(): string //I left this method for Symfony phpstorm plugin
    {
        return self::NAME;
    }
}

It works perfectly in DBAL4/ORM3, but I have to extend Type instead of SmallInt how it was in DBAL3, because they moved to strict types.

@xkzl
Copy link
Author

xkzl commented Jun 19, 2024

@berkut1 just to be on the same baseline here is the EnumType definition as proposed by Doctrine documentation https://www.doctrine-project.org/projects/doctrine-orm/en/3.2/cookbook/mysql-enums.html#solution-2-defining-a-type

I am interested to the registration type part and which doctrine event should be used, not really in your custom EnumType definition. How/where do you registered? I think this is the main point.

@greg0ire I just checked and in my latest checks I am using dbal 4.0.3, with orm 3.2.0. Could you advise how to disable type comment ?

Additionally, here is the latest reference to type comment I can find in the documentation and it is from dbal 3.8.5 https://www.doctrine-project.org/projects/doctrine-dbal/en/3.8/explanation/dc2type-comments.html
Having a page for 4.0 might be nice such that it would provide smooth example how to transition if not required anymore.

@berkut1
Copy link
Contributor

berkut1 commented Jun 19, 2024

How/where do you registered? I think this is the main point.

I register it through Symfony
https://symfony.com/doc/current/doctrine/dbal.html#registering-custom-mapping-types

Having a page for 4.0 might be nice such that it would provide smooth example how to transition if not required anymore.

There is no need transition, it just should work if you correctly created your custom types, especially getSQLDeclaration

@greg0ire
Copy link
Member

Types are always disabled in 4.0.x

@xkzl
Copy link
Author

xkzl commented Jun 19, 2024

How/where do you registered? I think this is the main point.

I register it through Symfony

https://symfony.com/doc/current/doctrine/dbal.html#registering-custom-mapping-types

Ok so you are not directly using the registration command mentioned in https://www.doctrine-project.org/projects/doctrine-dbal/en/4.0/reference/types.html#custom-mapping-types . Then, I think this is a different situation and it is done via doctrine configuration in your case. I think I better understand the differences between the two situations. My EnumType is part of a bundle. I don't think I can do it your way and I need à to listen to an event. I registered the type at the boot of my bundle using the initial command provided in the custom mapping type (basically Type::addType)

So according to my investigations, this issue is still here for 4.0.3 @greg0ire. So at this time, the only solution I can see is to use comment hints unless I missed something. Would you consider merging this PR @greg0ire, if it doesn't hurt current revisions/progress? If needed, I can additionally provide a test to make sure comment hints can get extracted so it will not be forgotten anymore.

@greg0ire
Copy link
Member

Would you consider merging this PR

DC2 comments are supposed to be completely gone in 4.0.x so… no?

@xkzl
Copy link
Author

xkzl commented Jun 19, 2024

What would be the alternative if we cannot add EnumType to bundles by registering manually through Type:addType on time for correct construction of columns...

@greg0ire
Copy link
Member

Use the enumType column attribute?

@xkzl xkzl closed this Jun 19, 2024
@xkzl
Copy link
Author

xkzl commented Jun 19, 2024

Thanks for trying.

@greg0ire
Copy link
Member

No worries. Also, it seems you can even avoid dealing with enumType if you use a recent enough version of PHP and the ORM:

As of version 2.11 Doctrine can also automatically map typed properties using a PHP 8.1 enum to set the right type and enumType.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants