我偶然发现了一个关于Symfony
的DataTransformer
以及如何正确使用它们的问题。虽然我知道如何实现它们并将其添加到我的表单字段中,但我想知道DataTransformer
s应该如何与Constraint
s组合。
下面的代码显示了我的用例。
表单
<?php
namespace AppBundleForm;
use AppBundleFormDataTransformerConsentConsentTransformer;
use SymfonyComponentFormAbstractType;
use SymfonyComponentFormExtensionCoreTypeCheckboxType;
use SymfonyComponentFormExtensionCoreTypeSubmitType;
use SymfonyComponentFormFormBuilderInterface;
use SymfonyComponentValidatorConstraintsIsTrue;
class ConsentTestForm extends AbstractType
{
/** @var ConsentTransformer $consentTransformer */
private $consentTransformer;
/**
* ConsentTestForm constructor.
* @param ConsentTransformer $consentTransformer
*/
public function __construct(ConsentTransformer $consentTransformer)
{
$this->consentTransformer = $consentTransformer;
}
/**
* @inheritDoc
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('accountConsent', CheckboxType::class, [
'constraints' => [
new IsTrue()
]
]);
$builder->get('accountConsent')->addModelTransformer($this->consentTransformer);
$builder->add('submit', SubmitType::class);
}
}
模型
<?php
class User extends Concrete implements PimcoreModelDataObjectDirtyIndicatorInterface
{
protected $accountConsent;
/**
* ...
*/
public function getAccountConsent () {
// ...
}
/**
* ...
*/
public function setAccountConsent ($accountConsent) {
// ...
}
}
为了简洁起见,省略了许多代码。该模型是太平洋投资管理公司的一个类。
数据转换器
<?php
namespace PassioneightBundleFormBuilderBundleFormDataTransformerConsent;
use PimcoreModelDataObjectDataConsent;
use SymfonyComponentFormDataTransformerInterface;
class ConsentTransformer implements DataTransformerInterface
{
/**
* @inheritDoc
* @param Consent|null $consent
*/
public function transform($consent)
{
return $consent instanceof Consent && $consent->getConsent();
}
/**
* @inheritDoc
* @param bool|null $consented
*/
public function reverseTransform($consented)
{
$consent = new Consent();
$consent->setConsent($consented ?: false);
return $consent;
}
}
如您所见,任何提交的值(即
null
、true
、false
(都将转换为Consent
,反之亦然。
控制器
<?php
namespace AppBundleController;
use AppBundleFormConsentTestForm;
use AppBundleModelDataObjectUser;
use SymfonyComponentHttpFoundationRequest;
use SymfonyComponentHttpFoundationResponse;
use SymfonyComponentRoutingAnnotationRoute;
/**
* Class TestController
* @package AppBundleController
*
* @Route("/test")
*/
class TestController extends AbstractFrontendController
{
/**
* @Route("/form")
* @param Request $request
* @return Response
*/
public function formAction(Request $request)
{
$user = new User();
$form = $this->createForm(ConsentTestForm::class, $user);
$form->handleRequest($request);
if ($form->isSubmitted()) {
if ($form->isValid()) {
p_r("VALID");
p_r($user);
} else {
p_r("NOT VALID");
}
};
return $this->renderTemplate(':Test:form.html.twig', [
"form" => $form->createView()
]);
}
}
注意
new User()
是如何作为实体传递的,以便用提交的值自动填充它。
视图
{{ form(form) }}
问题
表单可以构建得很好,最终,显示一个带有我指定标签的复选框。由于变压器,checked
-状态甚至可以正确显示,因为transform
方法将User
的Consent
转换为boolean
。
但是,在提交表单时,会显示一个错误,表示需要获得帐户同意。虽然在未经同意的情况下提交表格是可以的,但在实际同意的情况中,这并不是理想的结果。
当同意时,提交的值被转换为Consent
,然后它将保存值true
。但由于转换是在验证提交的值之前完成的,因此会显示之前的错误。之所以会发生这种情况,是因为在表单中添加的accountConsent
字段有一个Constraint
集合,即IsTrue
。因此,IsTrueValidator
验证Consent
(而不是实际提交的值(。
显然,
IsTrueValidator
不可能知道Pimcore的Consent
类。
问题
所有这些都给我留下了一个问题:如何正确地将IsTrue
-约束与ConsentDataTransformer
结合起来
验证的问题是您试图将对象验证为布尔类型。当您尝试验证时总是执行约束,当您提交表单时转换约束。因此,您已经转换了数据,这就是IsBool
验证失败的原因,因为值在Consent对象类型中;不是布尔值。
要解决此问题,您必须创建覆盖IsTrue的新验证约束。
<?php
namespace AppFormValidator;
use SymfonyComponentValidatorConstraintsIsTrue;
class IsConsented extends IsTrue
{
public $message = 'You need to consent!';
}
和同一命名空间上的验证器;
<?php
namespace AppFormValidator;
use SymfonyComponentValidatorConstraint;
use SymfonyComponentValidatorConstraintsIsTrueValidator;
class IsConsentedValidator extends IsTrueValidator
{
public function validate($value, Constraint $constraint)
{
return parent::validate($value->getConsent(), $constraint);
}
}
然后,您需要使用IsConsented
更改您的IsTrue
约束,如下所示;
<?php
namespace AppForm;
use AppEntityUser;
use AppFormDataTransformerConsentTransformer;
use AppFormValidatorIsConsented;
use SymfonyComponentFormAbstractType;
use SymfonyComponentFormExtensionCoreTypeCheckboxType;
use SymfonyComponentFormExtensionCoreTypeSubmitType;
use SymfonyComponentFormFormBuilderInterface;
use SymfonyComponentOptionsResolverOptionsResolver;
class ConsentTestFormType extends AbstractType
{
/** @var ConsentTransformer $consentTransformer */
private $consentTransformer;
/**
* ConsentTestForm constructor.
* @param ConsentTransformer $consentTransformer
*/
public function __construct(ConsentTransformer $consentTransformer)
{
$this->consentTransformer = $consentTransformer;
}
/**
* @inheritDoc
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('accountConsent', CheckboxType::class, [
'constraints' => [
new IsConsented()
]
]);
$builder->get('accountConsent')->addModelTransformer($this->consentTransformer);
$builder->add('submit', SubmitType::class);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => User::class,
]);
}
}
就是这样。你的表格现在有效。输出应该看起来像;
FormController.php on line 30:
"VALID"