我有一个简单的 symfony 表单,用于创建
ThingEntity
。
我有一个
CreateThingType
和一个 CreateThing
DTO:
DTO
namespace App\DataTransfer;
use App\Validator\UniqueNameForParent;
use Symfony\Component\Validator\Constraints\GreaterThan;
use Symfony\Component\Validator\Constraints\Length;
use Symfony\Component\Validator\Constraints\NotBlank;
#[UniqueNameForParent]
readonly class CreateThing
{
function __construct(
#[GreaterThan(0)]
public int $parent,
#[NotBlank]
#[Length(min: 3, max: 6)]
public string $name,
)
{
}
}
类型:
namespace App\Form\Type;
use App\DataTransfer\CreateThing;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class CreateThingType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('name', TextType::class)
->add('parent', IntegerType::class)
->add('save', SubmitType::class);
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => CreateThing::class,
]);
}
}
还有一个简单的控制器来处理这个事情:
namespace App\Controller;
use App\Entity\ThingEntity;
use App\Form\Type\CreateThingType;
use App\ThingRepository;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
#[Route('/create')]
class CreateController extends AbstractController
{
function __construct(private readonly ThingRepository $thingRepository) {}
public function __invoke(Request $request): Response
{
$form = $this->createForm(CreateThingType::class);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$thing = $form->getData();
$thingEntity = new ThingEntity(
id: null,
parent: $thing->parent,
name: $thing->name
);
$this->thingRepository->save($thingEntity);
return $this->redirectToRoute('app_thing_list');
}
return $this->render('create.html.twig', [ 'form' => $form ]);
}
}
未提交表单时,表单正确显示:
但是当我提交表单时,出现错误,如下所示:
函数 App\DataTransfer\CreateThing::__construct() 的参数太少,0 传入 /U/a/symfony-form-dto-shared-repro/vendor/symfony/form/Extension/Core/Type/FormType.php在第 134 行,正好是预期的 2 行
我可以通过删除
readonly
关键字并使参数/属性可选,通过使用空默认值来使其工作(我不希望这样做,因为我希望 DTO 清楚“一切都是必需的” “当我在其他地方重复使用它时)
是否可以在 Symfony 表单中使用只读 DTO?怎么办?
PropertyAccessor
无法了解你的构造函数。您可以更改变量名称,或者在构造函数中以不同的方式应用它们。
据我所知,symfony 使用
PropertyAccessor
组件通过使用 setter 或设置属性(如果属性是公共的)来将属性设置为 data_class。其中任何一个都无法将您的 Dto 设置为只读。
我也不认为这对于 Symfony Form 来说是不可能的,因为您可以设置默认值,这些默认值可以通过使用 setter 或使用由 PropertyAccessor 组件完成的 Dto 的公共属性来由您的表单覆盖.
简而言之,跳过只读部分和构造函数,并使用 getter 和 setter 创建简单的模型。