Search code examples
phpformssymfonyentitysymfony-forms

EntityType Form SYmfony add as Entity instead of int


I'm here because I can't find a solution to my problem. I have a form in Symfony 6 and one of the value is id_client and reffer to another entity, Client (relation ManyToOne).

I tested few methods to make the field be a select choice of all the clients (I show the name of the client). And every of them works but when I submit the form this value is add as the whole entity and not just the id. This is a problem because I end with this:

Expected argument of type "int", "App\\Entity\\Client" given at property path "id_client".

In my form it looks like this:

<?php

namespace App\Form;

use App\Entity\Client;
use App\Entity\Group;
use App\Repository\ClientRepository;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
class Group1Type extends AbstractType
{
    private $clientRepository;

    public function __construct(ClientRepository $clientRepository)
    {
        $this->clientRepository = $clientRepository;
        $this->clients = $clientRepository->findAll();
    }

    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder
            ->add('name', TextType::class, [
                'attr' => [
                    'class' => 'form-control'
                ],
                'label' => 'Name: '
            ])
            ->add('can_display', CheckboxType::class, [
                'label' => 'Can display : ',
                'attr' => [
                    'class' => 'my-3 mx-2'
                ]
            ])
            ->add('id_client', EntityType::class, [
                'class' => Client::class,
                // 'choices' => $this->clientRepository->findAllNameAlphabetical(),
                // 'query_builder' => function (ClientRepository $client) {
                //     return $client->findAllNameAlphabetical();
                // },
                'choice_label' => 'name',
                'expanded' => false,
                'multiple' => false,
                'attr' => [
                    'class' => 'form-control'
                ]
            ])
        ;
    }

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

The twig:

<section class="container my-3">
    <div class="row">
        <div class="col">
            {{ form_start(form) }}
                {{ form_row(form.name) }}
                {{ form_row(form.can_display) }}
                {{ form_row(form.id_client) }}
                <button class="btn btn-primary my-3">{{ button_label|default('Save') }}</button>
            {{ form_end(form) }}
        </div>
    </div>
</section>

The controller (I have the same result if I let it as it was at the beginning):

#[Route('/new', name: 'app_group_new', methods: ['GET', 'POST'])]
    public function new(Request $request, GroupRepository $groupRepository): Response
    {
        $group = new Group();
        $form = $this->createForm(Group1Type::class, $group);
        $form->handleRequest($request);
        // $group->id_client = $group->id_client->id;
        
        if ($form->isSubmitted()) {
            // dd('submit');
            // if(gettype($group->id_client)=="Client"){
                // dd($group);
                if($form->isValid()){
                    dd('valid');
                    $groupRepository->save($group, true);
                    $this->addFlash('success', 'The creation went successfully.');
                    return $this->redirectToRoute('app_group_index', [], Response::HTTP_SEE_OTHER);
                // }
            }
        }

        return $this->renderForm('group/new.html.twig', [
            'group' => $group,
            'form' => $form,
        ]);
    }

My entity:

    #[ORM\Column]
    private ?int $id_client = null;

Solution

  • In your entity, do not name properties with id_*. The client-Property in the Group(?) Entity should be named $client, not $id_client.

    Then, in your Form, name that field excactly like the property in the Group-Entity. Doctrine (as a DBAL should do) does that behind-the-scene relating of the object by the actual id for you.

    Group1Type.php

        ->add('client', EntityType::class, [
            'class' => Client::class,
            // ...
        ])
    

    And your Group-Entity (or Group1?)

    class Group
    {
        // ...
    
        #[ORM\ManyToOne(targetEntity: Client::class, inversedBy: 'groups')]
        #[ORM\JoinColumn(nullable: false)]
        private $client;
    
        // ...
    }
    

    It is totallfy fine and correct that the Form it self submits and handles the Entity-Instance of the selected client. You almost never handle $id values. Thats one of the features of doctrine!

    See the Symfony documentation for more information on how to use EntityType fields.


    Note: From the naming you gave, I suspect you have the wrong relation, maybe it should be OneToMany? (Can you tell us how Group and Client is related? Does 1 Group have multiple clients or vice versa?)


    Sidenote: In rare cases, when you really need a Number related to an Entity (e.g. a textinput where user can enter a customer number which then translates to a real customer entity) you can use DataTransformer to translate such values. But this is not what you want here!