fre5h / DoctrineEnumBundle

📦 Provides support of ENUM type for Doctrine in Symfony applications.
https://github.com/fre5h/DoctrineEnumBundle
MIT License
459 stars 75 forks source link

Invalid value "" for ENUM #173

Open TZK- opened 3 years ago

TZK- commented 3 years ago

I have an error when trying to set a value in my entity which is not part of my enum. In my entity, I set a validation constraint to ensure that values must one of the enum.

Howewer, It always throws InvalidArgumentException when it calls AbstractEnumType::convertToDatabaseValue() after I try to insert a new record in my database with a value which is not in the enum.

I do not really understand why convertToDatabaseValue() method throws an exception in this case since I want the validation constraint being called first.

Am I doing something wrong ?

declare(strict_types=1);

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;
use Fresh\DoctrineEnumBundle\Validator\Constraints as DoctrineAssert;
use Symfony\Component\Validator\Constraints as Assert;

class Permission
{
    /**
     * @ORM\Id()
     * @ORM\Column(type="uuid", unique=true)
     *
     * @Assert\Uuid
     *
     * @var string
     */
    private $id;

    /**
     * @ORM\Column(length=255, nullable=false, type="security_permission")
     *
     * @Assert\Length(max=255)
     * @DoctrineAssert\Enum(entity="App\PermissionEnum")
     *
     *
     * @var string
     */
    private string $name;
<?php

declare(strict_types=1);

namespace App;

use Fresh\DoctrineEnumBundle\DBAL\Types\AbstractEnumType;

final class PermissionEnum extends AbstractEnumType
{
    public const VIEW = 'view';
    public const CREATE = 'create';

    protected static $choices = [
        self::VIEW => self::VIEW,
        self::CREATE => self::CREATE
    ];
}
fre5h commented 3 years ago

Have you added the next line into your entity?

use Fresh\DoctrineEnumBundle\Validator\Constraints as DoctrineAssert;

I see it is missed in your example. But maybe you deleted it to make your code snippet shorter. Anyway I need to clarify, that you don't forget to include this validation constraint for you entity.

TZK- commented 3 years ago

Indeed, I have correctly imported the right constraint and missed to add it in my previous post after cleaning up a little bit my entity. (I edited the previous message to include the missing parts)

fre5h commented 3 years ago

Please add config for Doctrine in your project

TZK- commented 3 years ago
# config/packages/doctrine.yaml
doctrine:
    dbal:
        types:
            security_permission: App\PermissionEnum
    orm:
        auto_generate_proxy_classes: true
        mappings:
            App:
                is_bundle: false
                type: annotation
                dir: '%kernel.project_dir%/src/Entity'
                prefix: 'App\Entity'
                alias: App

        naming_strategy: doctrine.orm.naming_strategy.underscore
        auto_mapping: true

The type is working well when bound to my entity field since it generate a good SQL migration.

TZK- commented 3 years ago

And if override the method convertToDatabaseValue in my Enum:

    public function convertToDatabaseValue($value, AbstractPlatform $platform)
    {
        return $value;
    }

It works as expected and my validation constraint is well executed.

fre5h commented 3 years ago

Can you debug EnumValidator if it is executed on validation? From your words, looks like validator is not being called.

TZK- commented 3 years ago

The validation process happen after the execution of Doctrine\DBAL\Types::convertToDatabaseValue.

So if the method throws an exception, which is the case in the implementation of Fresh\DoctrineEnumBundle\DBAL\Types::convertToDatabaseValue() it will stop everything and not try to validate anything.

It is the same for all validations rules bound to a field which is an ORM Column of the type Fresh\DoctrineEnumBundle\DBAL\Type.

In my opinion, it must not throw exception in convertToDatabaseValue and let the users check if the values are in the Enum set through the validation rule. Or catch the exception and handle it during the validation process.

fre5h commented 3 years ago

Very strange. I've checked your cases in my code. I cannot reproduce it. If I set a wrong value, it is being catched by validator. I never reach the convertToDatabaseValue() method with wrong value. What version of Doctrine and EnumBundle do you use?

TZK- commented 3 years ago

I'm using API Platform on top of that, which could be the cause...

I don't know how it is handled internally when creating entities through API Platform endpoint. Maybe it calls the type class before the validation.

ATM, my 'fix' by overriding the convertToDatabaseValue() method suits me but in which case should we normally hit the exception thrown in convertToDatabaseValue() ?

fre5h commented 3 years ago

@TZK- I have to reproduce this bug with api-platform/api-pack. Right now I cannot reproduce it on my own projects

COil commented 3 years ago

Hi, I have the same problem with API-Platform 2.6. When filtering, if the value in not correct, it throws an error instead of returning a validation error.