我知道在这里问了类似的问题,但答案并不能让我满意。
有以下内容:
foo
,bar
,baz
,bat
,x
DTO
(非实体,虚拟对象,无注释)FormBuilder
在某些使用表单的地方,我想在字段foo
,bar
,baz
中添加单个验证程序。此验证器只能获取这些字段的值或整个传播的DTO。它应该有权访问DI Container以调用将根据数据库检查数据的服务。
现在我正考虑两种解决方案中的一种:
问题是我无法弄清楚如何处理这两种情况。
最后,我想强调一点,我不想使用验证组和注释 - 两者都会为DTO添加额外的依赖关系和逻辑。
答案 0 :(得分:1)
找到答案。简而言之,您可以查看This Matt Daum post。
以下是完整示例,不仅介绍如何创建自定义表单验证器,还介绍如何将服务和额外数据注入表单(因为这是我的情况)。
如果您想要简单的配方,请直接到底部。
我们有DTO:
class MyFormDTO
{
/** @var string */
private $name;
/** @var string */
private $surname;
/** @var string */
private $phone;
/** getters and setters ommited */
}
现在,在表单中定义依赖项。前两个是服务,最后一个(Calendar
)是控制器需要的一些额外数据。
class MyForm extends AbstractType
{
(fields hidden)
/**
* @param Sender $sender
* @param TranslatorInterface $translator
* @param Calendar $calendar
*/
public function __construct(Sender $sender, TranslatorInterface $translator, Calendar $calendar)
{
$this->translator = $translator;
$this->sender = $sender;
$this->calendar = $calendar;
}
}
现在有两种方法 - 如果您只需要表单中的服务,则可以将表单定义为服务。如果您和我一样需要额外的数据,则需要编写表单工厂服务:
class MyFormFactory
{
(fields hidden)
/**
* @param Sender $sender
* @param TranslatorInterface $translator
*/
public function __construct(Sender $sender, TranslatorInterface $translator)
{
$this->sender = $sender;
$this->translator = $translator;
}
/**
* @param Calendar $calendar
*
* @return MyForm
*/
public function getMyForm(Calendar $calendar)
{
return new MyForm($this->sender, $this->translator, $calendar);
}
}
让我们将此工厂定义为具有正确依赖关系的服务:
mybundle.form.myform_factory:
class: MyBundle\Service\FormFactory\MyFormFactory
arguments: [ @text_message.sender, @translator ]
如何在控制器中获取表单?很容易:
class MyController extends Controller
{
/**
* @ParamConverter("calendar", options={"mapping"={"calendarId":"id"}})
*
* @param Request $request
* @param Calendar $calendar
*
* @return Response
* @throws Exception
*/
public function myAction(Request $request, Calendar $calendar)
{
$formDTO = new MyFormDto();
$myForm = $this->get('mybundle.form.myform_factory')->getMyForm($calendar);
$form = $this->createForm($myForm, $formDTO);
(handling post hidden)
}
}
现在是最重要的部分 - 我们已将服务正确地注入到表单中。如何使用它们并验证所选数据?像这样:
class MyForm extends AbstractType
{
(fields hidden, constructor shown in previous example)
/**
* @param FormBuilderInterface $builder
* @param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
/** @var MyFormDTO $myDTO */
$myDTO = $options['data'];
(build form as usual, using services and data from $options and $this->calendar injected by controller and factory)
}
/**
* @param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
parent::setDefaultOptions($resolver);
$resolver->setDefaults([
'csrf_protection' => true,
'constraints' => [
new Callback(function (MyFormDTO $data, ExecutionContextInterface $context) //notice that we have access to fully propageted DTO here
{
//use injected service
$isValid = $this->sender->validateSomething($data->getSurname(), $data->getPhone());
if (false === $isValid)
{
$context
->buildViolation($this->translator->trans('wrong_surname_phone_pair'))
->addViolation();
}
return $isValid;
})
],
]);
}
}