数据转换器与约束



我偶然发现了一个关于SymfonyDataTransformer以及如何正确使用它们的问题。虽然我知道如何实现它们并将其添加到我的表单字段中,但我想知道DataTransformers应该如何与Constraints组合。

下面的代码显示了我的用例。

表单

<?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;
}
}

如您所见,任何提交的值(即nulltruefalse(都将转换为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方法将UserConsent转换为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"

最新更新