Search code examples
phpformssymfonyentity

Loading an entity into a field depending on the choice in another field Symfony PRE_SET_DATA Event Listener


Hi i got my OrderType form.

Here is my code:

public function buildForm(FormBuilderInterface $builder, array $options)
{
   $builder
              ->add('client', EntityType::class, array(
              'data' => $options['client'],
              'mapped' => false,
              'attr' => ['class' => 'chosen-select','data-placeholder'=>'Wybierz klienta'],
              'class' => UserDetails::class,
              'choice_label' => function ($client) {
              return  ''.$firma.' '.$client->getJson()["client"]["imie"] .' '. $client->getJson()["client"]["nazwisko"].'';
               },
              'label' => 'Wybierz klienta'))


               ->add('save', SubmitType::class, [
               'label' => 'Zapisz',
               'attr' => ['class' => 'btn btn-primary pull-right']]) ;

    $builder->addEventListener( 
              FormEvents::PRE_SET_DATA,
                function (FormEvent $event) {
                       $form = $event->getForm();
                       $client =$form->get('client')->getData();
                       $shipping = null === $client ? array() : $client->getClientsShippings()->getJson()["clients_shippings"]["name"];

           $form->add('shipping', EntityType::class, array(
               'class' => ClientsShippings::class,
               'placeholder' => '',
               'choices' => $shipping));
             }
           );
}

There are no errors, but nothing happens either. I do not know how to achieve that, after selecting the EventListener client, PRE_SET_DATA loaded the shipping addresses assigned to the client in the ClientsShippings entity depending on the chosen client. I read this one a few times on symfony.com, but I can not completely understand these events.

Could someone help me run it, let me have an example of how it works. Maybe I think about it completely in a different way.


  public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
                ->add('client', EntityType::class, array(
    'data' => $options['client'],
    'mapped' => false,
    'attr' => ['class' => 'chosen-select','data-placeholder'=>'Wybierz klienta'],
    'class' => UserDetails::class,
    'choice_label' => function ($client) {
      if(isset($client->getJson()["client"]["firma"]))
      {
        $firma = $client->getJson()["client"]["imie"];
        }
        else {
          $firma = "";
          }
    return  ''.$firma.' '.$client->getJson()["client"]["imie"] .' '. $client->getJson()["client"]["nazwisko"].'';
      },
    'label' => 'Wybierz klienta'

                ))

        ->add('product', EntityType::class, array(
    'data' => $options['product'],
    'mapped' => false,
    'multiple' => true,
    'class' => Products::class,
    'attr' => ['class' => 'chosen-select','data-placeholder'=>'Wybierz produkt'],
    'choice_label' => function ($product) {
        return  ''.$product->getJson()["products"]["name"] .' | Stan Magazynowy: '.$product->getJson()["products"]["stock"].'';
      },
  'label' => 'Wybierz produkty'

        ))

            ->add('save', SubmitType::class, [
            'label' => 'Zapisz',
            'attr' => ['class' => 'btn btn-primary pull-right']])
        ;

    $builder->get('client')->addEventListener(
      FormEvents::POST_SUBMIT,
      function (FormEvent $event)
      {
        $form = $event->getForm();

        $form->getParent()->add('shipping', EntityType::class, [
          'class' => ClientsShippings::class,
          'placeholder' => 'Wybierz adres dostawy',
          'choices' => $form->getData()->getclientsShippings()
        ]);
      }
    );


    }

Solution

  • A Symfony form has 2 main usages:

    1. Creating a new entity (Form::submit() is called)
    2. Editing an existing entity (Form::setData() and Form::Submit() are called)

    Note: In both cases, Form::handleRequest() is called before Form::submit() to check if the request can be submitted or not.

    We have 2 main category of form events:

    • events during pre-populating forms (used only for edit entity form)
    • events during form submission (used for both new and edit entity forms)

    1. Pre-populating the form with model data

    PRE_SET_DATA

    The PRE_SET_DATA event, gets the value of data that you're starting with (Order object in your case), and gets dispatched right before the form is populated with the data of entity object.

    You may use this event to:

    • Modify the form data during population
    • Add or Remove form fields

    POST_SET_DATA

    This event is fired right after the Form::setData() method is called and can be use to:

    • Read the data, after pre-populating the form

    Both POST_SET_DATA and POST_SET_DATA events are dispatched during pre-populating form data.

    2. Form Submission

    PRE_SUBMIT

    This is fired when Form::submit() or Form::handleRequest() are called. This event can be used to:

    • Change the data captured from the request
    • Add or remove fields before submitting the data

    SUBMIT

    This event is dispatched after PRE_SUBMIT and during Form::submit (after the model and view data normalization, and before denormalization). You may use this event to:

    • Change the data after normalization.

    POST_SUBMIT this event is dispatched after the Form::submit(). You may use this event to:

    • Fetch the data after denormalization.

    Answering your question

    You should use 2 event listeners, one for PRE_SET_DATA and one for POST_SUBMIT:

    OrderType

    class OrderType extends AbstractType
    {
        public function buildForm(FormBuilderInterface $builder, array $options)
        {
            $builder
                ->add('client', EntityType::class, array(
                    'class' => UserDetails::class
                ))
                ->add('save', SubmitType::class)
            ;
        
            $modifier = function (FormInterface $form, Client $client = null) {
                $shippings = null === $client ? array() : $client->getClientsShippings();
    
                $form->add('shipping', EntityType::class, array(
                    'class' => ClientsShippings::class,
                    'choices' => $shippings,
                ));
            };
    
            $builder->addEventListener(
                FormEvents::PRE_SET_DATA,
                function (FormEvent $event) use ($modifier) {
                    $orderData = $event->getData();
                    $modifier($event->getForm(), $orderData->getClient());
                }
            );
    
            $builder->get('client')->addEventListener(
                FormEvents::POST_SUBMIT,
                function (FormEvent $event) use ($modifier) {
                    $client = $event->getForm()->getData();
                    $modifier($event->getForm()->getParent(), $client);
                }
            );
        }
    }
    

    You should also make an AJAX call (some JavaScript) to update the shipping list, according to the selected client.