Search code examples
symfony-forms

Edit an entity with Symfony 6 Form System: how can I tweak the entity-loading logic?


TL;DR: how can I use a custom Doctrine query to load the entity from the database witg Symfony 6.3 form system?

Longer version

I've a simple form to UPDATE a phone number in an Opportunity entity:

class OpportunityChangePhoneType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {

        $builder
            ->add("accessKey")
            ->add("mobilePhone")
    }


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

Such entity maps an opportunity database table like this:

id|access_key       |mobile_phone|
--+-----------------+------------+
 1|96783f217481f....|111         |
 2|a967e4017f06f....|555         |
 3|14697aeb49602....|333         |

As you can see, I don't expose the id in the Form, but I use the accessKey instead.

On the updating controller, I have:

$form = $this->createForm(OpportunityChangePhoneType::class);
$form->handleRequest($this->request);

if( $form->isSubmitted() && $form->isValid() ) {

  $opportunity = $form->getData();
  $this->em->getRepository(Opportunity::class)->save($opportunity, true);
}

The problem is that createForm doesn't know how to load the Opportunity entity from the access_key field, so I always get a new entity instead of updating the old one.

How can I provide a custom logic to load the entity properly?


Solution

  • As a stopgap solution, I manually loaded the Opportunity entity and then I passed it to createForm()

    $accessKey = $this->request->get("accessKey");
    // ... check if the accessKey format is valid ...
    $opportunity = $this->em->getRepository(Opportunity::class)->findOneBy(["accessKey" => $accessKey]);
    // ... check if the $opportunity is valid (not expired, deleted, disabled, ...) ...
    $form = $this->createForm(OpportunityChangePhoneType::class, $opportunity);
    ....
    

    It works, but it partially defeats the purpose of the Form System.

    Update: the mighty Ryan Weaver himself confirmed that this is a valid approach.