简体   繁体   中英

Symfony form 2 different value types in one field

I'm actually building a very simple REST Api and I'm having an issue with this important feature. I tried to found something useful in the Symfony documentation to deal with this problem but I didn't find anything related with this.

I would like to receive a request body like this for example:

{
    "amount": "200",
    "account": {
        "name": "XXX XXXX", 
        "value": "asdasdasdasdasdsadsad"
    }
}

That request will create a new account entity and persist it (no problems with that).

And some other times I would like to receive this kind of request

{
    "amount": "200",
    "account": "e76ad9ea-dbc1-11e5-a764-109add42947b"
}

The idea of my app is handle this relating the new entity with the provided account id (UUID).

EntityType.php

class EntityType extends AbstractType
{
    /**
     * @param FormBuilderInterface $builder
     * @param array $options
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('amount')
            ->add('account', AccountType::class);
    }

    /**
     * @param OptionsResolver $resolver
     */
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(['data_class' => Entity::class, 'csrf_protection' => false]);
    }
}

the Entity is quite simple:

class Entity
{
    /**
     * @ORM\Column(type="guid")
     * @ORM\Id
     *
     * @var string
     */
    protected $id;

    /**
     * UUIDableEntity constructor.
     */
    public function __construct()
    {
        $this->id = Uuid::uuid1()->toString();
    }

    /**
     * @return string
     */
    public function getId(): string
    {
        return $this->id;
    }

    /**
     * @var float
     *
     * @ORM\Column(type="float")
     */
    protected $amount;

    /**
     * @var Account
     *
     * @ORM\ManyToOne(targetEntity="Account", cascade={"persist"})
     * @ORM\JoinColumn(referencedColumnName="id", nullable=false)
     * @Assert\Valid()
     */
    protected $account;

    /**
     * @return float
     */
    public function getAmount()
    {
        return $this->amount;
    }

    /**
     * @param float $amount
     */
    public function setAmount(float $amount)
    {
        $this->amount = $amount;
    }

    /**
     * @return Account
     */
    public function getAccount()
    {
        return $this->account;
    }

    /**
     * @param Account $account
     */
    public function setAccount(Account $account)
    {
        $this->account = $account;
    }
}

I know one solution could be detect the type of the account value and use the Type I need (so I'll need two different Form Types). But this solution seems to me a bit dirty. Any idea about the AccountType implementation to handle it?

The implementation of the AccountType for create a new entity every time is quite simple but no idea how I can handle 2 different types of request values.

In these situations, you'd better off handling the mapping yourself instead of relying on SF forms default behaviour. I think that you could achieve something like that by using empty_data :

$resolver->setDefaults([
    'data_class' => 'Account::class',
    'empty_data' => function (FormInterface $form) {
        //on create
        $account = new Account(
            $form->get('name')->getData(),
            $form->get('value')->getData()
        );

        //on update you'll need to inject the repo
        $account = $this->accountRepository->findOne(
                       $form->getData()
                   );

        return $account;
    },
]);

As per the buildForm, you might need to generate your form fields dynamically. You can do so by adding an eventListener on pre-setting your data.

public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) {
        $data = $event->getData();
        // check what data is coming in the request and add form fields accordingly
        $form = $event->getForm();
        $form->add(...);
    });

}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM