Description
Every time I work with forms in Symfony and PHPStan, I'm struggling with making sure PHPStan properly understands what's going on.
After the form has been submitted and validated, I usually end up with doing a lot of assertions to get the typing right.
Let's say we have the following form:
/**
* @extends AbstractType<array{firstName: string|null, lastName: string|null}>
*/
class UserFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options) : void
{
$builder->add('firstName', TextType::class, [
'required' => true,
'constraints' => [new Length(['min' => 3])],
]);
$builder->add('lastName', TextType::class, [
'required' => true,
'constraints' => [new Length(['min' => 3])],
]);
}
}
And we use it like this:
class Controller extends AbstractController
{
public function addAction(Request $request) : array
{
$form = $this->createForm(UserFormType::class);
$form->handleRequest($request);
assertType('array{firstName: string|null, lastName: string|null}|null', $form->getData());
if ($form->isSubmitted() && $form->isValid()) {
$data = $form->getData();
assertType('array{firstName: string, lastName: string}', $data);
}
return [
'form' => $form->createView(),
];
}
public function editAction(Request $request) : array
{
$form = $this->createForm(UserFormType::class, [
'firstName' => 'Ruud',
'lastName' => 'Kamphuis',
]);
$form->handleRequest($request);
assertType('array{firstName: string, lastName: string}|null', $form->getData());
if ($form->isSubmitted() && $form->isValid()) {
$data = $form->getData();
assertType('array{firstName: string, lastName: string}', $data);
}
return [
'form' => $form->createView(),
];
}
}
A few things can be improved to make it easier to work with forms.
-
When calling
$form->getData()
before submitting the form, it returnsTData |null
. After the form has been submitted and validated, it is stillTData|null
. At this point I expect it to beTData
only. -
Currently, we can only configure
TData
on the form. This type should always support the empty states, so this will have a lot of nulls most of the time. What if we introduce a second template TValidatedData, that can returned when calling$form->getData()
after the form was submitted and validated?
Would the above be possible? And does it make sense? If so, I could give it a try.