【发布时间】:2020-07-02 11:59:24
【问题描述】:
我偶然发现了一个关于Symfony's DataTransformers 以及如何正确使用它们的问题。虽然我知道如何实现并将它们添加到我的表单字段中,但我想知道 DataTransformers 应该如何与 Constraints 结合使用。
以下代码显示了我的用例。
形式
<?php
namespace AppBundle\Form;
use AppBundle\Form\DataTransformer\Consent\ConsentTransformer;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Validator\Constraints\IsTrue;
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 \Pimcore\Model\DataObject\DirtyIndicatorInterface
{
protected $accountConsent;
/**
* ...
*/
public function getAccountConsent () {
// ...
}
/**
* ...
*/
public function setAccountConsent ($accountConsent) {
// ...
}
}
为简洁起见,省略了很多代码。型号是Pimcore class。
数据转换器
<?php
namespace Passioneight\Bundle\FormBuilderBundle\Form\DataTransformer\Consent;
use Pimcore\Model\DataObject\Data\Consent;
use Symfony\Component\Form\DataTransformerInterface;
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 AppBundle\Controller;
use AppBundle\Form\ConsentTestForm;
use AppBundle\Model\DataObject\User;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
/**
* Class TestController
* @package AppBundle\Controller
*
* @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 方法将Users Consent 转换为boolean。
但是,在提交表单时,会显示一个错误,说需要帐户同意。虽然在未给予同意的情况下提交表单时这很好,但在真正同意时并不是理想的结果。
同意时,提交的值将转换为Consent,然后将保留值true。但是由于转换是在验证提交的值之前完成的,因此会显示前面提到的错误。出现这种情况是因为在表单中添加的accountConsent 字段有一个Constraint 集,即IsTrue。因此,IsTrueValidator 会验证 Consent(而不是实际提交的值)。
显然,
IsTrueValidator无法知道 Pimcore 的Consent类。
问题
所有这些都给我留下了一个问题:如何正确地将IsTrue-constraint 与我的ConsentDataTransformer 结合起来?
【问题讨论】:
-
在问题中提供过少信息和过多信息之间找到适当的平衡可能是一项挑战。我承认我迷失了你的细节。我建议尝试通过仅使用约束字段创建实体/表单来隔离问题。我认为view transformer 可能会有所帮助。但这真的只是一个猜测。
-
感谢您的反馈 - 非常感谢。我将创建一个更简单的用例,然后重新表述我的问题。不过,我可能需要一段时间才能找到时间这样做。我还会检查视图转换器是否有帮助,但视图转换器不会导致完全相同的问题,因为在转换之后进行了验证?
-
在这个网站上有点不受欢迎,但是创建一个新项目,添加重新创建问题所需的最低限度,然后将整个事情检查到 github 可以更容易解决这样的问题。只是一个想法。
-
我试图尽可能地简化问题 - 希望现在更清楚我想要实现的目标。我还需要一些时间来尝试
addViewTransformer方法而不是addModelTransformer方法。 -
使用
addViewTransformer会导致类似的问题,因为CheckboxType在底层添加了另一个视图转换器,即BooleanToStringTransformer。这个转换器期望传递的值是string类型。由于提交的值已经转换为Consent,所以会抛出TransformationFailedException,导致表单无效。