【问题标题】:Symfony Form Type constraint of string validates Integer value字符串的 Symfony 表单类型约束验证整数值
【发布时间】:2018-06-10 19:38:13
【问题描述】:

我有一个包含三个字段的简单实体:一个自动生成的 ID、一个字符串和一个整数。我为后两者设置了约束,整数字段对应的约束完美运行。如果我发送的整数不在范围内,或者我应该发送除整数以外的任何内容,则会返回错误。

但是,字符串字段不会发生这种情况。如果我发送一个整数,表单将正确验证。为什么会这样?

我用于测试的代码是:

curl -H "Content-Type: application/json" -X POST -d '{"fieldOne": 9000, "fieldTwo": 5}' http://localhost:8000/foos

FooController.php

<?php

namespace AppBundle\Controller;

use AppBundle\Entity\FooBar;
use AppBundle\Form\FooBarType;

use FOS\RestBundle\Controller\FOSRestController;
use FOS\RestBundle\Request\ParamFetcher;
use FOS\RestBundle\View\View;

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

class FooController extends FOSRestController
{

    public function getFooAction()
    {
        $view = new View();

        $data = array(
            'foo' => 'bar'
        );

        $view->setStatusCode(Response::HTTP_OK);
        $view->setData($data);

        return $view;
    }

    public function postFooAction(Request $request)
    {
        $foobar = new FooBar();
        $form = $this->createForm(FooBarType::class, $foobar);

        $data = json_decode($request->getContent(), true);
        $form->submit($data);
        if ($form->isValid()) {

            //$foobar = $form->getData();

            $response = new Response('All good');
            $response->setStatusCode(Response::HTTP_OK);
            return $response;
        }
        return View::create($form, Response::HTTP_BAD_REQUEST);
    }

}

FooBarType.php

<?php

namespace AppBundle\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;

class FooBarType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('fieldOne')
            ->add('fieldTwo')
        ;
    }
}

FooBar 实体

<?php

namespace AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;

/**
 * FooBar
 *
 * @ORM\Table(name="foo_bar")
 * @ORM\Entity(repositoryClass="AppBundle\Repository\FooBarRepository")
 */
class FooBar
{
    /**
     * @var int
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @var string
     *
     * @Assert\Type("string")
     * @ORM\Column(name="field_one", type="string", length=255)
     */
    private $fieldOne;

    /**
     * @var int
     *
     * @Assert\Range(
     *      min = 1,
     *      max = 10,
     *      minMessage = "You must be at least {{ limit }}cm tall to enter",
     *      maxMessage = "You cannot be taller than {{ limit }}cm to enter"
     * )
     * @ORM\Column(name="field_two", type="int")
     */
    private $fieldTwo;

    /**
     * Get id
     *
     * @return int
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * Set fieldOne
     *
     * @param string $fieldOne
     *
     * @return FooBar
     */
    public function setFieldOne($fieldOne)
    {
        $this->fieldOne = $fieldOne;

        return $this;
    }

    /**
     * Get fieldOne
     *
     * @return string
     */
    public function getFieldOne()
    {
        return $this->fieldOne;
    }

    /**
     * Set fieldTwo
     *
     * @param int $fieldTwo
     *
     * @return FooBar
     */
    public function setFieldTwo($fieldTwo)
    {
        $this->fieldTwo = $fieldTwo;

        return $this;
    }

    /**
     * Get fieldOne
     *
     * @return string
     */
    public function getFieldTwo()
    {
        return $this->fieldTwo;
    }
}

【问题讨论】:

    标签: forms symfony validation fosrestbundle


    【解决方案1】:

    在此处对表单的提交方法进行类型转换时出现问题:Symfony\Component\Form\Form::submit()

    if (false === $submittedData) {
        $submittedData = null;
    } elseif (is_scalar($submittedData)) {
        $submittedData = (string) $submittedData;
    }
    

    原因是为了保持与标准 HTTP 请求的兼容性,始终以字符串形式提交值,并允许 Symfony 的 Form\Types 将值转换为所需的类型。

    请记住,父表单是复合的,每个字段都是子表单元素,这会导致执行类型转换。

    例如:

    $builder->add('a', Form\TextType::class, [
        'constraints' => [
           new Assert\Type(['type' => 'string'])
        ]
    ]);
    
    $form = $builder->getForm();
    $form->submit(['a' => 1]);
    dump($form->getData);
    dump($form->isValid());
    

    结果:

     array:1 [▼
      "a" => "1"
     ]
    
     true
    

    在这种情况下,TextType 不会将值转换为任何值,但提交已经将值类型转换为字符串。

    但是,当使用 new Assert\Type(['type' =&gt; 'int']) 约束并且字段类型不是 Form\IntegerType 时,它会验证失败。

    而范围约束仅测试满足最小值和最大值的数值,这也适用于数字字符串。

    例如:https://3v4l.org/ErQrC

    $min = 1;
    $max = 10;
    $a = "5";
    var_dump($a < $max && $a > $min); //true
    

    对比:https://3v4l.org/8D3N0

    $min = 1;
    $max = 10;
    $a = "c";
    var_dump($a < $max && $a > $min); //false
    

    要解决此问题,您可以创建自己的 constraint expression,假设您不需要数值。

    class FooBar
    {
    
        //...
    
        /**
         * @var string
         *
         * @Assert\Type("string")
         * @Assert\Expression("this.isNotNumeric()")
         * @ORM\Column(name="field_one", type="string", length=255)
         */
        private $fieldOne;
    
        /**
         * @return bool
         */
        public function isNotNumeric()
        {
           return !is_numeric($this->fieldOne);
        }
    }
    

    【讨论】:

    • 感谢您的彻底澄清和您花费的时间来编写它。
    猜你喜欢
    • 1970-01-01
    • 2017-03-10
    • 2017-08-16
    • 1970-01-01
    • 2018-02-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-30
    相关资源
    最近更新 更多