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

How to use with native PHP enum #236

Open
SirMishaa opened this issue Dec 30, 2023 · 4 comments
Open

How to use with native PHP enum #236

SirMishaa opened this issue Dec 30, 2023 · 4 comments
Assignees
Labels

Comments

@SirMishaa
Copy link

Hello,

According this example :

<?php
namespace App\DBAL\Types;

use Fresh\DoctrineEnumBundle\DBAL\Types\AbstractEnumType;

/**
 * @extends AbstractEnumType<string, string>
 */
final class BasketballPositionType extends AbstractEnumType
{
    public final const POINT_GUARD = 'PG';
    public final const SHOOTING_GUARD = 'SG';
    public final const SMALL_FORWARD = 'SF';
    public final const POWER_FORWARD = 'PF';
    public final const CENTER = 'C';

    protected static array $choices = [
        self::POINT_GUARD => 'Point Guard',
        self::SHOOTING_GUARD => 'Shooting Guard',
        self::SMALL_FORWARD => 'Small Forward',
        self::POWER_FORWARD => 'Power Forward',
        self::CENTER => 'Center'
    ];
}

I wonder why we don't use PHP's native enumerations?
I have this enum, and I'd want to use as a type :

<?php

namespace App\Enum;

enum MovieStatus
{
    /**
     * We don't have any information about the movie.
     */
    case UNKNOWN;

    /**
     * The production of the movie has been temporarily postponed.
     */
    case PRODUCTION_POSTPONED;

    /**
     * The movie has been officially announced,
     * and details such as production and release date are in progress.
     */
    case ANNOUNCED;

    /**
     * The movie is in the writing phase (scenario development).
     */
    case WRITING_STARTED;

    /**
     * The movie is currently in the production phase (being filmed, recorded, ...).
     */
    case PRODUCTION_STARTED;

    /**
     * The movie has been produced and edited,
     * but the release date is yet to be confirmed.
     */
    case WAITING_FOR_RELEASE;

    /**
     * The release of the movie has been temporarily postponed.
     */
    case RELEASE_POSTPONED;

    /**
     * The movie has been officially released to the public.
     */
    case RELEASED;

    /**
     * The production of the movie has been canceled.
     */
    case CANCELED;
}

Regards ❤️

@fre5h fre5h added the question label Dec 30, 2023
@fre5h fre5h self-assigned this Dec 30, 2023
@fre5h
Copy link
Owner

fre5h commented Dec 30, 2023

@SirMishaa Hi. Maybe this article could help you https://www.doctrine-project.org/2022/01/11/orm-2.11.html
The approach is different, now Doctrine supports native PHP enums out of box

@hackzilla
Copy link

The reason we wanted to use PHP enums was better reason about the value in our application logic and the reason to keep using DoctrineEnumBundle was to enforce the value in the database.

We've had success with enums and DoctrineEnumBundle.

Here is our implementation.
We have an enum for Review types.

enum ReviewEnum: string
{
    case WRITTEN         = 'written';
    case PHONE_INTERVIEW = 'phone_interview';
}

And we reference the enum in the Type.

final class ReviewType extends AbstractEnumType
{
    protected static array $choices = [
        ReviewEnum::WRITTEN->value         => 'Written',
        ReviewEnum::PHONE_INTERVIEW->value => 'Phone interview',
    ];
}
    #[ORM\Column(type: ReviewType::class, nullable: false, enumType: ReviewEnum::class)]
    #[PhpEnumType(entity: ReviewType::class)]
    private ReviewEnum $type;
    
    
    public function getType(): ReviewEnum
    {
        return $this->type;
    }

    public function setType(ReviewEnum|string $type): self
    {
        if (is_string($type)) {
            $type = ReviewEnum::from($type);
        }

        $this->type = $type;

        return $this;
    }

setType() accepts strings to make it easier to load fixtures when testing.

However, the EnumTypeValidator will not work with php enums, so we need to wrap it to allow it to convert the enum into a string.

namespace App\Validator\Constraints;

use Fresh\DoctrineEnumBundle\Exception\RuntimeException;
use Fresh\DoctrineEnumBundle\Validator\Constraints\EnumType;
use Fresh\DoctrineEnumBundle\Validator\Constraints\EnumTypeValidator;
use Symfony\Component\Validator\Constraint;

class PhpEnumTypeValidator extends EnumTypeValidator
{
    /**
     * @throws RuntimeException
     */
    public function validate(mixed $value, Constraint|EnumType $constraint): void
    {
        if (!$constraint instanceof EnumType) {
            throw new RuntimeException(\sprintf('Object of class %s is not instance of %s', \get_class($constraint), EnumType::class));
        }

        $constraint->choices = $constraint->entity::getValues();

        if ($value instanceof \BackedEnum) {
            $value = $value->value;
        }

        parent::validate($value, $constraint);
    }
}

and

namespace App\Validator\Constraints;

use Fresh\DoctrineEnumBundle\Validator\Constraints\EnumType;

/**
 * Required to activate PhpEnumTypeValidator.
 */
#[\Attribute(\Attribute::TARGET_PROPERTY)]
class PhpEnumType extends EnumType
{
}

@hackzilla
Copy link

@fre5h If you could incorporate the unwrapping of the enum in the validator, then this bundle would support php enums.

@fre5h
Copy link
Owner

fre5h commented May 7, 2024

@hackzilla thanks for advice, I'll consider this 👍

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

No branches or pull requests

3 participants