Symfony2 - 验证多个字段,DTO,无注释

时间:2015-09-23 09:11:14

标签: forms validation symfony

我知道在这里问了类似的问题,但答案并不能让我满意。

有以下内容:

  1. 包含字段foobarbazbatx
  2. 的标准表单
  3. DTO非实体,虚拟对象,无注释
  4. 使用FormBuilder
  5. 附加的某些字段的约束
  6. 表格用于多个地方。
  7. 在某些使用表单的地方,我想在字段foobarbaz中添加单个验证程序。此验证器只能获取这些字段的值或整个传播的DTO。它应该有权访问DI Container以调用将根据数据库检查数据的服务。

    现在我正考虑两种解决方案中的一种:

    1. 在需要它的控制器中添加额外约束(声音脏)
    2. 添加额外字段以形成构造函数/ DTO(不是布尔值,但业务逻辑告诉是否需要额外验证)并向表单添加额外约束。
    3. 问题是我无法弄清楚如何处理这两种情况。

      最后,我想强调一点,我不想使用验证组和注释 - 两者都会为DTO添加额外的依赖关系和逻辑。

1 个答案:

答案 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;
                })
            ],
        ]);
    }
}