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