1

I want to make a form with a select field that displays different options. Those options are a list of NomenclatureDTO object field $libNom set as:

class NomenclatureDTO
{
    public int $idNom;
    public string $libNom;
    public string $dtMiseEnPlace;
    public string $dtObsolescence;
}

Here is the class where the form applies to:

class AvisDTO
{
    public string $dtAvis;
    public string $dtEmission;
    public string $idActeurAuteur;
    public string $nomAuteur;
    public string $prenomAuteur;
    public bool $boEstTacite;
    public ArrayCollection $documents;
    public string $idConsultation;
    public array $idsPieces;
    public int $nomNatureAvisRendu;
    public int $nomTypeAvis;
    public string $txAvis;
    public string $txFondementAvis;
    public string $txHypotheses;
    public string $txQualiteAuteur;

    public function __construct()
    {
        $this->documents = new ArrayCollection();
    }    
}

I want to display libNom in the select options, but I only need the idNom on submit.

So, my form looks like:

<?php

namespace App\Form;

use App\Entity\DTO\AvisDTO;
use App\Entity\DTO\DataFormMapper\DataAvisType;
use App\Entity\DTO\NomenclatureDTO;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\ChoiceList\ChoiceList;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class AvisType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        // dd($options['dataPostAvis']->nomNatureAvisRendu);      L21: SCREENSHOT
        $builder
            ->add('nomAuteur', TextType::class, [
                'label' => "Nom",
            ])
            ->add('prenomAuteur', TextType::class, [
                'label' => "Prénom"
            ])
            ->add('boEstTacite', ChoiceType::class, [
                'required' => true,
                'label' => "L'avis est-il tacite ?",
                'label_attr' => [
                    'class' => "font-weight-bold"
                ],
                'expanded' => true,
                'multiple' => false,
                'choices' => [     
                    'Oui' => true,
                    'Non' => false,                     
                ],                      
            ])          
            ->add('nomNatureAvisRendu', ChoiceType::class, [
                'label' => "Nature de l'avis rendu",
                'required' => false,                
                'choices' => $options['dataPostAvis']->nomNatureAvisRendu,
                'choice_label' => function(?NomenclatureDTO $choice) {                    
                    return $choice->libNom;
                },
                'choice_value' => function(?NomenclatureDTO $choice) {  
                    // dd($choice);                              L50: SCREENSHOT
                    // dd(gettype($choice->idNom), $choice->idNom);L51: SCREENSHOT
                    return $choice->idNom;
                },
            ])
            ->add('nomTypeAvis', ChoiceType::class, [
                'label' => "Type d'avis",
                'required' => false,
                'choices' => $options['dataPostAvis']->nomTypeAvis,
                'choice_label' => function($choice) {                   
                    return $choice->libNom;
                },                              
                'choice_value' => ChoiceList::value($this, 'idNom'),
            ])
            ->add('txAvis', TextareaType::class, [
                'required' => true,
                'attr' => [
                    'placeholder' => "Avis favorable avec prescriptions. \nPremière prescription : Les volets doivent être en bois"
                ]
            ])
            ->add('txFondementAvis', TextareaType::class, [
                'attr' => [
                    'placeholder' => "L'avis de l'ABF est rendu en application de l'article R. 425-30 du Code de l'urbanisme."
                ]
            ])
            ->add('txHypotheses', TextareaType::class, [
                'attr' => [
                    'placeholder' => "Dans l'hypothèse où la puissance électrique nécessaire est de x alors le coût de raccordement est de y"
                ]
            ])
            ->add('txQualiteAuteur', TextareaType::class, [
                'attr' => [
                    'placeholder' => "Qualité"
                ]
            ])

            ->add('Envoyer', SubmitType::class, [
                'row_attr' => [
                    'class' => 'row justify-content-end'
                ],
            ]);
    }

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

Screenshot L21:

Screenshot L21

Screenshot L50:

Screenshot L50

Screenshot L51:

Screenshot L51

I get this error before the page actually show the form:

Notice: Trying to get property 'idNom' of non-object

Which makes no sense to me since the dump L51 explicitly says $choice is a NomenclatureDTO object...

EDIT:

I've try to set:

->add('nomNatureAvisRendu', ChoiceType::class, [
    'label' => "Nature de l'avis rendu",
    'required' => false,                
    'choices' => $options['dataPostAvis']->nomNatureAvisRendu,
    'choice_label' => function(?NomenclatureDTO $choice) {                    
        return $choice ? $choice->idNom : null;
    },
    'choice_value' => static function(?NomenclatureDTO $choice): int {  
        return $choice->idNom ?? 0;     
    },
])

Which returns on submit:

Typed property App\Entity\DTO\AvisDTO::$nomNatureAvisRendu must be int, App\Entity\DTO\NomenclatureDTO used

Dhia Djobbi
  • 1,176
  • 2
  • 15
  • 35
Zabon
  • 241
  • 2
  • 18

2 Answers2

2

The choices may be correct, but when using dd($choice) you're validating one choice only. Because the field isn't required, perhaps there's also a null-value passed to you callback-functions? At any case, your anonymous methods use ?NomenclatureDTO, which allow null values. I guess you could use return $choice?->idNom; (php8) or something like return $choice ? $choice->idNom : null;

Another thing that might be worth checking is the data of the property in the mapped object, which also might be null. I think that shouldn't cause a problem once the choices can handle a null value.

Kevin Driessen
  • 356
  • 3
  • 9
  • I tried with "return $choice ? $choice->idNom : null;" but I still get the same error: (Typed property App\Entity\DTO\AvisDTO::$nomTypeAvis must be int, App\Entity\DTO\NomenclatureDTO used) And my datas are all fulfilled for fields $idNom and $libNom... – Zabon Aug 03 '21 at 14:54
  • To me that looks like an entirely different error. That error is about the `nomTypeAvis` and not about the `idNom` property of the `nomNatureAvisRendu`. So it seems at least that one is resolved ;) I guess you can resolve your new error if you use a similar approach (use a callback instead of `ChoiceList::value`). Alternatively, you could probably just use the propertyname there (see "This can be a callable or a property path" in https://symfony.com/doc/current/reference/forms/types/choice.html#choice-value) – Kevin Driessen Aug 03 '21 at 18:08
  • If I set the both properties with return "return $choice ? $choice->idNom : null;", I still have the error: "Typed property App\Entity\DTO\AvisDTO::$nomNatureAvisRendu must be int, App\Entity\DTO\NomenclatureDTO used". – Zabon Aug 04 '21 at 07:32
0

So the final answer was to set this in AvisDTO class:

    public NomenclatureDTO $nomNatureAvisRendu;
    public NomenclatureDTO $nomTypeAvis;

You need to map the entire object instead of just one field...

Zabon
  • 241
  • 2
  • 18