Search code examples
symfonysymfony4

Form validation in symfony 4 doesn't work


I have created an Entity Testme and generated the crud file with bin/console make:crud and try to add a validation rule to the form but it doesn't work:

My Entity:

<?php

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;

/**
 * @ORM\Entity(repositoryClass="App\Repository\TestmeRepository")
 */
class Testme
{
 /**
 * @ORM\Id()
 * @ORM\GeneratedValue()
 * @ORM\Column(type="integer")
 */
private $id;

/**
 * @Assert\NotBlank()
 * @ORM\Column(type="string", length=255)
 */
private $name;

/**
 * @ORM\Column(type="string", length=255, nullable=true)
 */
private $description;

....

The form:

<?php

namespace App\Form;

use App\Entity\Testme;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class TestmeType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder
        ->add('name')
        ->add('description')
    ;
}

public function configureOptions(OptionsResolver $resolver)
{
    $resolver->setDefaults([
        'data_class' => Testme::class,
    ]);
}
}

The controller action:

/**
 * @Route("/{id}/edit", name="testme_edit", methods="GET|POST")
 */
public function edit(Request $request, Testme $testme): Response
{
    $form = $this->createForm(TestmeType::class, $testme);
    $form->handleRequest($request);

    if ($form->isSubmitted() && $form->isValid()) {
        $this->getDoctrine()->getManager()->flush();

        return $this->redirectToRoute('testme_edit', ['id' => $testme->getId()]);
    }

    return $this->render('testme/edit.html.twig', [
        'testme' => $testme,
        'form' => $form->createView(),
    ]);
}

If the name field is blank, the validation error is correctly show when creating a new entity, but when I edit it and empty the name field, I get an InvalidArgumentException on the $form->handleRequest($request); line:

Expected argument of type "string", "NULL" given.

What am I missing?

EDIT: all the code have been generated by the make:Entity and make:crud commands, I only added the @Assert\NotBlank() on the $name property.


Solution

  • Form and posted data binding is applied before any validation. If you typehint the setters/adders you'll run in this kind of scenarios.

    From my POV you should not modify your "model" code in order to satisfy third party code (form component, here) as you can run into possible issues like wrong usage of a method (if a value need to be there, the only possible way to ensure a "not null" requirement is to avoid accepting null; validator itself will do nothing as soon as you "forget" to invoke valid on the object and, even in that case, you still have a valid and flushable object).

    Instead you should bind DTOs or ValueObjects to your form. These objects can accept any kind of value you want (basically the value you expect and null). Then you can perform validation and, for instance, with a "command" (not a symfony one, a "command" in the DDD "concept") you can populate and perform other "data passage" or manipulation to real entities.

    Even if ste's answer will make it work, is conceptually wrong as expose your code to "domain risks" explained above in this answer.