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.