web-dev-qa-db-fra.com

Symfony 4 gérant le champ de formulaire DateType null

J'ai un formulaire dans Symfony 4 où j'implémente le DateType comme champ de texte

->add('DateOfBirth', DateType::class, array(
    'required' => false,
    'widget' => 'single_text',
    'empty_data' =
))

cependant, le champ est facultatif à remplir pour le formulaire. Lorsque je soumets le formulaire avec une valeur vide pour ce champ, je reçois l'erreur:

Argument attendu de type "DateTimeInterface", "NULL" donné.

erreur sur la ligne

$form->handleRequest($request);

Il semble que je doive transmettre une valeur par défaut (c'est-à-dire - 1/1/1990) car null n'est pas une valeur DateTime valide? Quelle est la bonne façon de gérer cela? J'ai regardé la page this et essayé différentes choses (c'est-à-dire - changer le type d'entrée) mais rien ne semble résoudre ce problème.

Merci d'avance!

Mon entité:

<?php

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;
use NoProtocol\Encryption\MySQL\AES\Crypter;

/**
 * @ORM\Entity(repositoryClass="App\Repository\PatientsRepository")
 * @ORM\Table(name="Patients")
 */
class PatientSearch
{
    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @ORM\Column(type="integer")
     */
    private $PatientId;

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

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

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

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

    /**
     * @ORM\Column(type="date", nullable=true)
     */
    private $DateOfBirth;

    /**
     * @ORM\Column(type="string", length=50)
     */
    private $LastName;

    /**
     * @ORM\Column(type="string", length=50)
     */
    private $FirstName;

    public function getId(): ?int
    {
        return $this->id;
    }

    public function getPatientId(): ?int
    {
        return $this->PatientId;
    }

    public function setPatientId(int $PatientId): self
    {
        $this->PatientId = $PatientId;

        return $this;
    }

    public function getAddress1(): ?string
    {
        return $this->Address1;
    }

    public function setAddress1(string $Address1): self
    {
        $this->Address1 = $Address1;

        return $this;
    }

    public function getAddress2(): ?string
    {
        return $this->Address2;
    }

    public function setAddress2(string $Address2): self
    {
        $this->Address2 = $Address2;

        return $this;
    }

    public function getCity(): ?string
    {
        return $this->City;
    }

    public function setCity(string $City): self
    {
        $this->City = $City;

        return $this;
    }

    public function getState(): ?string
    {
        return $this->State;
    }

    public function setState(string $State): self
    {
        $this->State = $State;

        return $this;
    }

    public function getDateOfBirth(): ?\DateTimeInterface
    {
        return $this->DateOfBirth;
    }

    public function setDateOfBirth(\DateTimeInterface $DateOfBirth): self
    {
        $this->DateOfBirth = $DateOfBirth;

        return $this;
    }

    public function getLastName(): ?string
    {
        return $this->LastName;
    }

    public function setLastName(string $LastName): self
    {
        $this->LastName = $LastName;

        return $this;
    }

    public function getFirstName(): ?string
    {
        return $this->FirstName;
    }

    public function setFirstName(string $FirstName): self
    {
        $this->FirstName = $FirstName;

        return $this;
    }
}

Type:

<?php

namespace App\Form;

use App\Entity\PatientSearch;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\DateType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class PatientSearchType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('FirstName', TextType::class, array('required' => false))
            ->add('LastName', TextType::class, array('required' => false))
            ->add('DateOfBirth', DateType::class, array(
                'required' => false,
                'widget' => 'single_text',
                'empty_data'  => '',
                ))
        ;
    }

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

Manette:

<?php

namespace App\Controller;

use App\Form\PatientSearchType;
use App\Entity\PatientSearch;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\HttpFoundation\Request;

class PatientSearchController extends AbstractController
{
    /**
     * @Route("/patient/search", name="patient_search")
     */
    public function index(Request $request)
    {
        $form = $this->createForm(PatientSearchType::class);

        $form->handleRequest($request);

        dump($form);
        dump($form->isValid());

        $search = null;
        if ($form->isSubmitted() && $form->isValid()) {
            $searchFormData = $form->getData();
            dump($searchFormData);
            $search = $this->getDoctrine()
                ->getRepository(PatientSearch::class)
                ->findBy(array(
                    'LastName' => $searchFormData->getLastName(),
                    'FirstName' => $searchFormData->getFirstName(),
                    'DateOfBirth' => $searchFormData->getDateOfBirth(),
                    ));

        }

        return $this->render('patient_search/index.html.twig', [
            'search_form' => $form->createView(),
            'search' => $search
        ]);
    }
}

Dump d'erreur (après avoir défini 'empty_data' => '1/1/2000)':

FormErrorIterator {#542 ▼
  -form: Form {#507 ▼
    -config: FormBuilder {#508 ▶}
    -parent: null
    -children: OrderedHashMap {#509 ▼
      -elements: array:3 [▼
        "FirstName" => Form {#510 ▶}
        "LastName" => Form {#513 ▶}
        "DateOfBirth" => Form {#516 ▼
          -config: FormBuilder {#517 ▶}
          -parent: Form {#507}
          -children: OrderedHashMap {#518 ▶}
          -errors: array:1 [▼
            0 => FormError {#825 ▼
              #messageTemplate: "This value is not valid."
              #messageParameters: array:1 [▼
                "{{ value }}" => "1/1/2000"
              ]
              #messagePluralization: null
              -message: "This value is not valid."
              -cause: ConstraintViolation {#794 ▼
                -message: "This value is not valid."
                -messageTemplate: "This value is not valid."
                -parameters: array:1 [▶]
                -plural: null
                -root: Form {#507}
                -propertyPath: "children[DateOfBirth]"
                -invalidValue: "1/1/2000"
                -constraint: Form {#532 ▶}
                -code: "1dafa156-89e1-4736-b832-419c2e501fca"
                -cause: TransformationFailedException {#520 ▶}
              }
              -Origin: Form {#516}
            }
          ]
          -submitted: true
          -clickedButton: null
          -modelData: null
          -normData: null
          -viewData: "1/1/2000"
          -extraData: []
          -transformationFailure: TransformationFailedException {#520 ▼
            #message: "Unable to reverse value for property path "DateOfBirth": Date parsing failed: U_PARSE_ERROR"
            #code: 0
            #file: "/mnt/c/Users/ElementZero/source/php/phleb-manager/vendor/symfony/form/Form.php"
            #line: 1137
            -previous: TransformationFailedException {#523 ▶}
            trace: {▶}
          }
          -defaultDataSet: true
          -lockSetData: false
        }
      ]
      -orderedKeys: array:3 [▶]
      -managedCursors: []
    }
    -errors: []
    -submitted: true
    -clickedButton: null
    -modelData: PatientSearch {#501 ▶}
    -normData: PatientSearch {#501 ▶}
    -viewData: PatientSearch {#501 ▶}
    -extraData: []
    -transformationFailure: null
    -defaultDataSet: true
    -lockSetData: false
  }
  -errors: []
}
8
Element Zero

J'ai fini par utiliser un TextType pour faire cela car c'était plus facile et il ne semblait pas y avoir de solution avec l'utilisation d'un DateType. En fait, il semble que ce soit un problème en cours de révision sur les problèmes de github Symfony (ticket ici ) car le DateTimeType/DateType ne semble pas fonctionner correctement avec les empty_data attribut.

L'astuce ici est lorsque vous obtenez le champ, c'est un type date, mais lorsque vous le définissez, le type est un string qui tente de se convertir en datetime et s'il échoue alors le champ n'est tout simplement pas défini. Le champ lui-même est toujours un type de données date.

Type de fichier

->add('DateOfBirth', TextType::class, array(
    'required' => false,
    'empty_data' => null,
    'attr' => array(
        'placeholder' => 'mm/dd/yyyy'
    )))

Fichier Twig

{{ form_widget(search_form.DateOfBirth) }}

Fichier d'entité

/**
 * @ORM\Column(type="date", nullable=true)
 */
private $DateOfBirth;

...

public function getDateOfBirth(): ?\DateTimeInterface
{
    return $this->DateOfBirth;
}

public function setDateOfBirth(string $DateOfBirth): self
{
    try {

        $this->DateOfBirth = new \DateTime($DateOfBirth);
    }
    catch(\Exception $e) {
       //Do Nothing
    }

    return $this;
}
2
Element Zero

Utilisez le empty_data option pour ce faire.

$builder
     ->add('someDate', \Symfony\Component\Form\Extension\Core\Type\DateType::class, [
        'widget' => 'single_text',
        'required' => false,
        'empty_data' => '',
     ])
;
4
Fabien Papet