防止Php/Symfony进行类型转换';正确';价值

防止Php/Symfony进行类型转换';正确';价值,php,symfony,casting,Php,Symfony,Casting,我有一个Symfony(4.3)表单和一些验证规则 在我的App\Entity\Objectif类中: /** * @ORM\Column(type="float", options={"default" : 0}) * @Assert\Type("float") */ private $budget; public function buildForm(FormBuilderInterface $builder, array $options) { $builder ->add

我有一个Symfony(4.3)表单和一些验证规则

在我的
App\Entity\Objectif
类中:

/**
* @ORM\Column(type="float", options={"default" : 0})
* @Assert\Type("float")
*/
private $budget;
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
    ->add('budget')
    ->add('userQuantity')
    /* some other parameters */
;
}
public function generateMenu(Request $request)
{
    $form = $this->createForm(ObjectifType::class);
    $data = json_decode($request->getContent(),true);
    $form->submit($data);

    if ($form->isValid()) {
    /* do some stuff with data */
    } else {
      return $this->json('some error message');
    }
}
在我的
App\Form\ObjectifType
类中:

/**
* @ORM\Column(type="float", options={"default" : 0})
* @Assert\Type("float")
*/
private $budget;
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
    ->add('budget')
    ->add('userQuantity')
    /* some other parameters */
;
}
public function generateMenu(Request $request)
{
    $form = $this->createForm(ObjectifType::class);
    $data = json_decode($request->getContent(),true);
    $form->submit($data);

    if ($form->isValid()) {
    /* do some stuff with data */
    } else {
      return $this->json('some error message');
    }
}
在我的
App\Controller\ObjectifController
类中:

/**
* @ORM\Column(type="float", options={"default" : 0})
* @Assert\Type("float")
*/
private $budget;
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
    ->add('budget')
    ->add('userQuantity')
    /* some other parameters */
;
}
public function generateMenu(Request $request)
{
    $form = $this->createForm(ObjectifType::class);
    $data = json_decode($request->getContent(),true);
    $form->submit($data);

    if ($form->isValid()) {
    /* do some stuff with data */
    } else {
      return $this->json('some error message');
    }
}
我的Symfony应用程序是一个API,因此我从前端接收Json格式的数据

我的目标是确保最终用户作为
$budget
发送的值为浮动类型

问题:验证过程不适用于值“true”

如果最终用户以
$budget
的形式发送字符串,则该过程将正常工作,验证将失败

如果最终用户将值“true”作为
$budget
发送,则该值将隐式类型转换为“1”,因此验证成功,但不会发生这种情况

在这种情况下,如何强制PHP或Symfony不隐式地将“true”转换为“1”

多谢各位


测试(可选读数)

出于测试目的,我在我的
App\Entity\Objectif
类中放置了一个Callbak验证器(),其唯一目的是在验证表单时输出
$budget
属性的类型:

// App\Entity\Objectif.php

/**
* @Assert\Callback
*/
public function validate(ExecutionContextInterface $context, $payload)
{

dump('Actual value is : ' . $this->budget);

if (!is_float($this->budget)) {
    dump('Value is NOT FLOAT');
    $context->buildViolation('This is not a float type.')
            ->atPath('budget')
            ->addViolation();
    exit;
  } else {
    dump('Value is FLOAT'); 
    exit;
  }
}
如果我使用API测试软件发送'true'作为'budget'键(默认值):

我总是得到这些输出:

Objectif.php on line 193:
"Actual value is : 1"
Objectif.php on line 202:
"Value is FLOAT"
我怀疑这是一个Symfony问题,因为当我使用PHP CLI时:

php > var_dump(is_float(true));
bool(false)
我得到了正确的结果


顺便说一句,“false”值会自动转换为“null”,这与我的验证目的无关,但我找不到是否必要。

如果不进一步调查,我无法告诉您表单组件更改为1的位置和原因,但您可以在提交表单之前使用DataTransferObject并根据该值进行验证

class ObjectifDto
{
    /**
     * @Assert\Type("float")
     * @var float
     */
    public $budget;

    public static function fromRequestData($data): self
    {
        $objectifDto= new self();
        $objectifDto->budget = $data['budget'] ?? 0;

        return $objectifDto;
    }
}
在控制器中:

public function generateMenu(Request $request, ValidatorInterface $validator)
{
    $data = json_decode($request->getContent(),true);
    $dto = ObjectifDto::fromRequestData($data);
    $errors = $validator->validate($dto);

    //return errors or go on with the form or persist manually
}

在没有进一步调查的情况下,我无法告诉您表单组件更改为1的位置和原因,但您可以在提交表单之前使用DataTransferObject并根据其进行验证

class ObjectifDto
{
    /**
     * @Assert\Type("float")
     * @var float
     */
    public $budget;

    public static function fromRequestData($data): self
    {
        $objectifDto= new self();
        $objectifDto->budget = $data['budget'] ?? 0;

        return $objectifDto;
    }
}
在控制器中:

public function generateMenu(Request $request, ValidatorInterface $validator)
{
    $data = json_decode($request->getContent(),true);
    $dto = ObjectifDto::fromRequestData($data);
    $errors = $validator->validate($dto);

    //return errors or go on with the form or persist manually
}

因此,经过一些研究,我发现实现
FormInterface
的类的
submit()
方法确实将每个标量值(即整型、浮点型、字符串或布尔型)强制转换为字符串(将“false”值转换为“null”):

这与验证过程无关(在我的例子中,验证过程是正确设置的)

这解释了为什么我将“true”值转换为“1”

有人知道为什么symfony的代码是这样设计的吗?我不明白。

我试图通过使用更常规的
handleRequest()
方法来摆脱控制器中的
submit()
方法。但它根本不会改变任何东西,因为
handleRequest
在内部调用
submit()
(请参阅HttpFoundationRequestHandler类中的
handleRequest()
)。所以“true”仍然被铸造为“1”

因此,我最终使用了Chris的解决方案(参见上文)。它工作得很好。所有的功劳都归于他:

public function generateMenu(
    Request $request,
    ValidatorInterface $validator
)
{
    $data = json_decode(
        strip_tags($request->getContent()),
        true);

    $dto = ObjectifDto::fromRequestData($data);
    $errors = $validator->validate($dto);

    if (count($errors) > 0) {
        $data = [];
        $data['code status'] = 400;
        foreach ($errors as $error) {
            $data['errors'][$error->getPropertyPath()] = $error->getMessage();
        }
        return $this->json($data, 400);
    } 

    // everything is fine. keep going
}
我认为不使用Symfony的表单验证过程是一个好习惯,至少在处理API和JSON时是这样

我觉得“raw”(可以这么说)与DataTransfertObject相结合,提供了更多的控制。
最重要的是,无论出于何种原因,它都不会自动转换您的值(这将彻底破坏您的验证过程).

因此,经过一些研究,我发现实现
FormInterface
的类的
submit()
方法确实转换了每个标量值(即整数、浮点、字符串或布尔值)要设置字符串(将“false”值设置为“null”):

这与验证过程无关(在我的例子中,验证过程是正确设置的)

这解释了为什么我将“true”值转换为“1”

有人知道为什么symfony的代码是这样设计的吗?我不明白。

我试图通过使用更常规的
handleRequest()
方法来摆脱控制器中的
submit()
方法。但它根本不会改变任何东西,因为
handleRequest
在内部调用
submit()
(请参阅HttpFoundationRequestHandler类中的
handleRequest()
)。所以“true”仍然被铸造为“1”

因此,我最终使用了Chris的解决方案(参见上文)。它工作得很好。所有的功劳都归于他:

public function generateMenu(
    Request $request,
    ValidatorInterface $validator
)
{
    $data = json_decode(
        strip_tags($request->getContent()),
        true);

    $dto = ObjectifDto::fromRequestData($data);
    $errors = $validator->validate($dto);

    if (count($errors) > 0) {
        $data = [];
        $data['code status'] = 400;
        foreach ($errors as $error) {
            $data['errors'][$error->getPropertyPath()] = $error->getMessage();
        }
        return $this->json($data, 400);
    } 

    // everything is fine. keep going
}
我认为不使用Symfony的表单验证过程是一个好习惯,至少在处理API和JSON时是这样

我觉得“raw”(可以这么说)与DataTransfertObject相结合,提供了更多的控制。
最重要的是,它不会出于任何原因自动广播您的值(这将彻底破坏您的验证过程).

谢谢@Chris。我会尝试你的建议并给出反馈。谢谢你@Chris。我会尝试你的建议并给出反馈。