【问题标题】:Doctrine collection of enums and Symfony Form枚举和 Symfony 表单的教义集合
【发布时间】:2021-12-06 08:51:48
【问题描述】:

用户可以选择他想要接收的电子邮件通知。

通知类型由枚举(MyCLabs 库)定义:

use MyCLabs\Enum\Enum;

final class NotificationType extends Enum
{
    public const DEADLOCKED = 1;
    public const REJECTED = 2;
    public const SENT = 3;
    public const ACCEPTED = 4;
    public const REFUSED = 5;

    public function translationPath(): string
    {
        return 'user.notifications.'.$this->getKey();
    }
}

用户有更多的通知类型:

/**
 * @ORM\Entity()
 */
class Notification
{
    /**
     * @ORM\Id
     * @ORM\ManyToOne(targetEntity=User::class, inversedBy="notifications")
     */
    protected User $user;

    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     */
    protected int $notificationType;

    public function __construct(User $user)
    {
        $this->user = $user;
    }

    public function getNotificationType(): NotificationType
    {
        return new NotificationType($this->notificationType);
    }

    public function setNotificationType(NotificationType $notificationType): self
    {
        $this->notificationType = $notificationType->getValue();

        return $this;
    }
}

用户实体:

/**
 * @ORM\Entity()
 */
class User implements UserInterface, EquatableInterface
{
    /**
     * @var Collection|Notification[]
     * @ORM\OneToMany(targetEntity=Notification::class, mappedBy="user", cascade={"persist", "remove"}, orphanRemoval=true)
     */
    protected Collection $notifications;
    
    //...
}

这是正确的解决方案吗?现在我在为复选框列表制作 Symfony 表单时遇到问题。

类似这样的事情(我知道,这是错误的):

$builder->add('notifications', ChoiceType::class, [
    'choices' => NotificationType::values(),
    'expanded' => true,
    'multiple' => true,
    'choice_value' => 'value',
    'choice_label' => static function (NotificationType $type): string {
        return $type->translationPath();
    },
]);

我可以在我的情况下使用内置的 Symfony 表单吗?或者你对关系“实体多对多枚举”有更好的解决方案。

【问题讨论】:

  • 你到底有什么问题/错误?
  • 问题:表单字段“通知”预期的 NotificationType 实例数组(选择选项)。但是给定通知实体的集合。

标签: php symfony doctrine symfony-forms


【解决方案1】:

我用数据转换器编写了我的 EnumChoiceType,它将 NotificationType 的数组转换为 Notification 实体的集合,反之亦然。

不要忘记在实体集合中设置orphanRemoval=true

用法:

$builder->add('notifications', EnumChoiceType::class, [
    'enum' => NotificationType::class,
    'class' => Notification::class,
    'property' => 'notificationType',
    'object' => $options['data'],
    'expanded' => true,
    'multiple' => true,
]);

枚举选择类型:

class EnumChoiceType extends AbstractType implements DataTransformerInterface
{
    /**
     * @var mixed
     */
    private $object;
    private string $property;
    private string $class;

    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $this->object = $options['object'];
        $this->property = $options['property'];
        $this->class = $options['class'];

        if ($options['multiple']) {
            $builder->addModelTransformer($this);
        }
    }

    public function configureOptions(OptionsResolver $resolver): void
    {
        $resolver->setRequired([
            'enum',
        ]);

        $resolver->setDefaults([
            'choices' => static function (Options $options) {
                return call_user_func([$options['enum'], 'values']);
            },
            'choice_value' => 'value',
            'choice_label' => static function (Enum $enum): string {
                return $enum->translationPath();
            },
            'property' => 'enum',
            'object' => null,
            'class' => null,
        ]);
    }

    /**
     * @param mixed $value
     * @return Enum[]
     */
    public function transform($value): array
    {
        $array = [];

        foreach ($value as $item) {
            $array[] = PropertyAccess::createPropertyAccessor()->getValue($item, $this->property);
        }

        return $array;
    }

    /**
     * @param Enum[] $value
     */
    public function reverseTransform($value): ArrayCollection
    {
        $collection = new ArrayCollection();

        foreach ($value as $type) {
            $notification = new $this->class($this->object);
            PropertyAccess::createPropertyAccessor()->setValue($notification, $this->property, $type);

            $collection->add($notification);
        }

        return $collection;
    }

    public function getParent(): string
    {
        return ChoiceType::class;
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-14
    • 1970-01-01
    • 1970-01-01
    • 2020-08-10
    相关资源
    最近更新 更多