Symfony 在一个字段中形成 2 种不同的值类型
Symfony form 2 different value types in one field
我实际上正在构建一个非常简单的 REST Api,但我对这个重要功能有疑问。我试图在 Symfony 文档中找到一些有用的东西来解决这个问题,但我没有找到任何与此相关的东西。
我想收到这样的请求正文,例如:
{
"amount": "200",
"account": {
"name": "XXX XXXX",
"value": "asdasdasdasdasdsadsad"
}
}
该请求将创建一个新的帐户实体并将其保留(没有问题)。
还有一些时候我想收到这种请求
{
"amount": "200",
"account": "e76ad9ea-dbc1-11e5-a764-109add42947b"
}
我的应用程序的想法是处理将新实体与提供的帐户 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]);
}
}
实体非常简单:
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;
}
}
我知道一种解决方案是检测帐户值的类型并使用我需要的类型(因此我需要两种不同的表单类型)。但是这个解决方案在我看来有点脏。
关于 AccountType 实现来处理它有什么想法吗?
每次创建新实体的 AccountType 的实现非常简单,但不知道如何处理 2 种不同类型的请求值。
在这些情况下,您最好自己处理映射,而不是依赖 SF 表单的默认行为。我认为您可以通过使用 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;
},
]);
根据 buildForm,您可能需要动态生成表单字段。您可以通过在预先设置的数据上添加一个 eventListener 来实现。
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(...);
});
}
我实际上正在构建一个非常简单的 REST Api,但我对这个重要功能有疑问。我试图在 Symfony 文档中找到一些有用的东西来解决这个问题,但我没有找到任何与此相关的东西。
我想收到这样的请求正文,例如:
{
"amount": "200",
"account": {
"name": "XXX XXXX",
"value": "asdasdasdasdasdsadsad"
}
}
该请求将创建一个新的帐户实体并将其保留(没有问题)。
还有一些时候我想收到这种请求
{
"amount": "200",
"account": "e76ad9ea-dbc1-11e5-a764-109add42947b"
}
我的应用程序的想法是处理将新实体与提供的帐户 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]);
}
}
实体非常简单:
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;
}
}
我知道一种解决方案是检测帐户值的类型并使用我需要的类型(因此我需要两种不同的表单类型)。但是这个解决方案在我看来有点脏。 关于 AccountType 实现来处理它有什么想法吗?
每次创建新实体的 AccountType 的实现非常简单,但不知道如何处理 2 种不同类型的请求值。
在这些情况下,您最好自己处理映射,而不是依赖 SF 表单的默认行为。我认为您可以通过使用 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;
},
]);
根据 buildForm,您可能需要动态生成表单字段。您可以通过在预先设置的数据上添加一个 eventListener 来实现。
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(...);
});
}