1
votes

I have a form to modify an entity which has some children entities. I use a form collection to do so. When I edit this entity, the children collection should appear but it doesn't. The children collection is composed of 2 choice fields and 1 integer field. The integer field is well rendered with the right data but choice fields ask to select an option whereas it should show Matiere and Colle of children entities.

In the code, Colle entity is a child of Matiere Entity. I'm using MaterializeCSS as a framework.

Here's my code :

Child Form :

 $builder
        ->add('matiere', EntityType::class, [
            'class' => 'PACESColleBundle:Matiere',
            'attr' => ['class'=> 'matiere'],
            'choice_label' => 'name',
            'label' => false,
            'required' => false,
            'placeholder' => 'Choisissez une matière',
            'mapped' => false])
        ->add('colleEnfant', EntityType::class, [
            'class' => 'PACESColleBundle:Colle',
            'attr' => ['class' => 'colles'],
            'choice_label' => 'nom',
            'label' => false,
            'group_by' => 'matiere',
            'required' => true,
            'placeholder' => 'choose.colle'])
        ->add('ordre', IntegerType::class,[
            'attr'=>['class'=>'ordre'],
            'required' => true,
            'label' => false]);

Parent Form :

$builder->add('nom', TextType::class,['label' => 'Nom de la colle'])
    ->add('collesEnfants', CollectionType::class,
        ['label' => false,
        'entry_type' => SousColleFormType::class,
        'required' => true,
        'allow_add' => true,
        'allow_delete' => true,
        'by_reference' => false]);

View :

<table id="tableau" class="creneaux"
               data-prototype="{{ form_widget(form.collesEnfants.vars.prototype)|e }}">

   <thead>
       <tr>
          <th>Matière</th>
          <th>Colle</th>
          <th>Ordre</th>
          <th>Supprimer</th>
       </tr>
   </thead>

   <tbody>
       {% for colle in form.collesEnfants %}
          <tr>
             <td>{{ form_row(colle.matiere) }}</td>
             <td>{{ form_row(colle.colleEnfant) }}</td>
             <td>{{ form_row(colle.ordre) }}</td>
             <td><a href="" class="delete_colle_link"><i class="material-icons">delete</i></a></td>
          </tr>
       {% endfor %}
   </tbody>
</table>

<script>
    $(document).ready(function() {
        $('.matiere').material_select();
        $('.colles').material_select()
    });
</script>
3
The field render is ok? You just wonder why existing data not selecting in choice box? - Jeet
Field rendering is ok. As you said, only problem is existing data not selected in choice fields - Kristen Joseph-Delaffon

3 Answers

0
votes

You could add a query in your entity type and check if the field 'nom' exist in your entity.

$builder->add('colleEnfant', EntityType::class, [
    'class'        => 'PACESColleBundle:Colle',
    'attr'         => ['class' => 'colles'],
    'choice_label' => 'nom',
    'label'        => false,
    'required'     => true,
    'placeholder'  => 'choose.colle',
    'query_builder' => function(ColleRepository $repository) {
        $qb = $repository->createQueryBuilder('c');
        // the function returns a QueryBuilder object
        return $qb->groupBy('c.matiere')->orderBy('c.nom', 'ASC');
    }
])

After you need to add an event listener who detect the select modification.

$builder->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) {
    $data = $event->getData();
    $matiere = $this->em->getRepository('PACESColleBundle:Matiere')->find($data['matiere']);
    $form = $event->getForm();

    $form->add('colleEnfant', EntityType::class, [
        'class'        => 'PACESColleBundle:Colle',
        'attr'         => ['class' => 'colles'],
        'choice_label' => 'nom',
        'label'        => false,
        'required'     => true,
        'placeholder'  => 'choose.colle',
        'query_builder' => function(ColleRepository $repository) use ($matiere) {
            $qb = $repository->createQueryBuilder('c');
            // the function returns a QueryBuilder object
            return $qb
                ->where('c.matiere = :matiere')
                ->setParameter('matiere', $matiere)
                ->orderBy('c.nom', 'ASC');
        }
    ]);
});

I hope this going to help you.

0
votes

I ended up doing it with JQuery.

Script :

$(document).ready(function() {
    {% for colleEnfant in collesEnfants %}
         $('#paces_colle_colle_ajoutsupercolle_collesEnfants_{{ loop.index0 }}_matiere option[value="{{ colleEnfant.matiere }}"]').prop('selected', true);
         $('#paces_colle_colle_ajoutsupercolle_collesEnfants_{{ loop.index0 }}_colleEnfant option[value="{{ colleEnfant.colle }}"]').prop('selected', true);
    {% endfor %}
    $('.matiere').material_select();
    $('.colles').material_select();
}

Controller :

$collesEnfantsForm = [];
foreach ($colle->getCollesEnfants() as $colleEnfant) {
     $collesEnfantsForm[] = ['matiere' => $colleEnfant->getMatiere()->getId(), 'colle' => $colleEnfant->getId()];
}
0
votes

it is because ArrayChoiceList::getValuesForChoices() does comparison for objects by reference. As result if you have not same instance of object in data and in list of choices that it won't be selected. Solution is to use "choice_value" options for ChoiceType field. eg.

   $builder->add('type', ChoiceType::class, [
        'choices' => FeeType::getTypes(),
                'choice_label' => 'name',
                'required' => true,
                'choice_value' => static function (?FeeType $feeType) {
                    return $feeType ? $feeType->getId() : '';
                }
            ])