将类型化属性(PHP7.4)与Symfony表单一起使用

将类型化属性(PHP7.4)与Symfony表单一起使用,symfony,Symfony,我使用DTO作为Symfony表单类型的数据类。当我在这些DTO中使用类型化属性(PHP7.4)时,有一件事对我不起作用 例如: class ProductDto { /* * @Assert\NotBlank */ public string $title; } 这通常看起来效果很好——如果用户提交的表单标题或描述为空,则验证开始,表单显示验证警告 但在创建表单(例如编辑表单)时添加数据时会出现问题: 最初,表单按预期显示,并使用Foo作为标题的值。当用户

我使用DTO作为Symfony表单类型的
数据类
。当我在这些DTO中使用类型化属性(PHP7.4)时,有一件事对我不起作用

例如:

class ProductDto
{
    /*
     * @Assert\NotBlank
     */
    public string $title;
}
这通常看起来效果很好——如果用户提交的表单标题或描述为空,则验证开始,表单显示验证警告

但在创建表单(例如编辑表单)时添加数据时会出现问题:

最初,表单按预期显示,并使用
Foo
作为
标题的值。当用户清除
标题
输入表单字段并提交表单时,会引发如下异常:

Typed property `App\Form\Dto\ProductDto::$title` must be string, null used
据我所知,这是因为在
Form->handleRequest()
期间,标题在之前设置为“Foo”后被设置为空字符串(或null),对吗


有解决此问题的方法吗?

由于PHP7.4引入了属性的类型暗示,因此为所有属性提供有效值尤其重要,以便所有属性都具有与其声明的类型匹配的值

从未分配过的属性没有空值,但它处于未定义状态,这将永远不会与任何声明的类型匹配。未定义!==空

以下是一个例子:


这就是我刚刚想到的:

DTO:

特点:

trait GenericSetterTrait
{
    private function set(string $propertyName, $value): void
    {
        if ($value === null) {
            unset($this->{$propertyName});
        } else {
            $this->{$propertyName} = $value;
        }
    }
}

似乎有效。你怎么认为?有异议吗?

一种方法是将
公共字符串更改为
公共?字符串,使其可为空,另一种方法是将$title的默认值添加为空字符串
公共字符串$title='
,另一种方法是添加构造函数并在其中设置属性值。@Slavian Thx。我认为
不是最优的,对吗?空字符串确实是一个选项。您将如何处理例如
public Foo$Foo?这真的取决于上下文,如果我有一个属性,它是一个类,就像你给出的例子一样,我会想这个属性应该是null还是应该总是类,不管怎样。这实际上取决于您想要实现什么。表单字段上的
'empty\u data'=>'
work@Jakumi
'empty_data'=>'
似乎与
@Assert\NotBlank
和字符串属性结合使用。但它不是一个选项,例如,
public Foo$Foo。谢谢,但是“在初始化之前不能被访问”不是我目前的问题。我认为这是某种哲学,在幕后可能是
$this->createForm(ProductFormType::class,$productDto)将影响空到
$title
use GenericSetterTrait;

/**
 * @Assert\NotBlank
 */
public string $title;

public function setTitle(?string $title): void
{
    $this->set('title', $title);
}

/**
 * @Assert\NotNull
 */
public Foo $foo;

public function setFoo(?Foo $foo): void
{
    $this->set('foo', $foo);
}
trait GenericSetterTrait
{
    private function set(string $propertyName, $value): void
    {
        if ($value === null) {
            unset($this->{$propertyName});
        } else {
            $this->{$propertyName} = $value;
        }
    }
}