Skip to content

Narrow return type of $form->getData() after calling $form->isValid() #407

Open
@ruudk

Description

@ruudk

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.

  1. When calling $form->getData() before submitting the form, it returns TData |null. After the form has been submitted and validated, it is still TData|null. At this point I expect it to be TData only.

  2. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions