如何将有效约束与可空嵌入表单相结合

时间:2015-03-23 09:03:33

标签: validation symfony symfony-forms

我想结合验证嵌入表单的能力(如果包含在请求有效负载中),如果不包括则跳过此验证。

我的用例如下。

我向控制器发送了以下json,包括资源Campaign和子资源文章。

{
   "campaign": {
       "name": "New campaign",
       "article": {
           "title": "How to develope faster",
           "summary": "This is a <b>summary</b>"
       }
   }
}

这是由Symfony表单处理,并进行以下验证:

AppBundle\Document\Campaign:
  properties:
    name:
        - NotBlank:
            message: 'campaign.validation.name.not_blank'
        - Length:
            min: 1
            max: 300
            minMessage: 'campaign.validation.name.length.min'
            maxMessage: 'campaign.validation.name.length.max'
    article:
        - Valid: ~

AppBundle\Document\Article:
  properties:
    title:
        - NotBlank:
            message: 'article.validation.title.not_blank'
        - Length:
            min: 20
            max: 300
            minMessage: 'article.validation.title.length.min'
            maxMessage: 'article.validation.title.length.max'

表格看起来像:

class CampaignFormType extends AbstractType
{
    /**
     * {@inheritdoc}
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $transformer = new FieldToModelTransformer($options['manager'], 'slug');

        $builder
            ->add('name')
            ->add('article', new ArticleFormType());

        $builder->add(
            $builder->create('brand', 'text', [
                'invalid_message' => 'campaign.validation.brand.invalid'
            ])->addModelTransformer($transformer)
        );
    }

    /**
     * {@inheritdoc}
     */
    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'AppBundle\Document\Campaign',
            'csrf_protection'   => false
        ));
    }

    /**
     * @return string
     */
    public function getName()
    {
        return 'campaign';
    }
}

class ArticleFormType extends AbstractType
{
    /**
     * {@inheritdoc}
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('title', 'text');
    }

    /**
     * {@inheritdoc}
     */
    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'AppBundle\Document\Article',
            'csrf_protection'   => false,
        ));
    }

    /**
     * {@inheritdoc}
     */
    public function getName()
    {
        return 'article';
    }
}

所以问题是如果我在子表单中设置有效约束,验证系统总是在子资源上传递验证,即使它没有被传递。所以这个请求会失败:

{
    "campaign": {
        "name": "New campaign"
    }
}

我想达成以下行为:

  • 如果文章有效负载有数据然后验证它
  • 如果文章为空或null,则不验证子资源

最干净的方法是如何做到的?

谢谢

1 个答案:

答案 0 :(得分:0)

所以我发现了正在发生的事情。

我的控制器是创建表单,提交请求等的标准控制器......如果文章不在json中,它不会自动自动填充,因此在表单提交之前它在请求中为空。

我有3个用于测试的数据集(场景1):

测试1:

{
    "campaign": {
        "name": "New campaign",
        "startDate": "2012-01-01",
        "endDate": "2012-02-01"
    }
}

测试2:

{
    "campaign": {
        "article": {
            "title": ""
        }
    }
}

测试3:

{
    "campaign": {
        "article": {
            "title": "",
            "summary": "Whatever valid value"
        }
    }
}

不改变形式或验证,结果如下:

  • 测试1:对文章启用验证并在article.title
  • 上抛出非空白
  • 测试2:对文章启用验证并在article.title
  • 上抛出非空白
  • 测试3:对文章启用验证并在article.title
  • 上抛出非空白

关键是修改表单字段文章并向其添加所需的选项false:

public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $transformer = new FieldToModelTransformer($options['manager'], 'slug');

        $builder
            ->add('name', 'text')
            ->add('article', new ArticleFormType(), [
                'required' => false
            ]);

        $builder->add(
            $builder->create('brand', 'text', [
                'invalid_message' => 'campaign.validation.brand.invalid'
            ])->addModelTransformer($transformer)
        );
    }

这样执行相同的树测试结果是(场景2):

  • 测试1:文章验证未开启
  • 测试2:即使将标题传递给空或空
  • ,对文章的验证也没有开启
  • 测试3:对文章启用验证并在article.title
  • 上抛出非空白

所以,总结一下,我需要在嵌入的表单上设置 required false 是可选的但是如果我传递一个属性等于empty或null的嵌入对象,它会被自动清理。

我预计方案2的测试2会打开标题验证,但这不是由于清除了null属性。

我的意思是,我期待这个结果:

  • 测试1:文章验证未开启
  • 测试2:文章开启验证并在article.title上投放非空白
  • 测试3:对文章启用验证并在article.title
  • 上抛出非空白

这种行为确实有意义,但我没想到它是默认的,所以也许有关约束的文档可能更明确,或者我对这个假设是错误的。