4

I know this question has been asked already a couple of times, but there hasn't been an answer that actually helped me solving my problem.

I've got three EventSubscribers for three Dropdowns who are dependent on each other.

So in my FormType I say:

  public function buildForm(FormBuilderInterface $builder, array $options)
{
  // solution showmethecode
  $pathToAgencies = 'agencies';
  //
  $builder
    ->addEventSubscriber(new AddChannel1Subscriber($pathToAgencies))
    ->addEventSubscriber(new AddChannel3Subscriber($pathToAgencies))
    ->addEventSubscriber(new AddAgencySubscriber($pathToAgencies));

}

and one of my EventSubscribers looks like that:

    ...
...

        public static function getSubscribedEvents() {
            return array(
              FormEvents::PRE_SET_DATA  => 'preSetData',
              FormEvents::PRE_SUBMIT    => 'preSubmit'
            );
          }

          private function addChannel1Form($form, $channel1s = null) {
            $formOptions = array(
              'class' => 'AppBundle:Channel1',
                'property' => 'name',
                'label' => 'label.channel1s',
                'empty_value' => 'label.select_channel1s',
                'mapped' => false,
                'expanded' => false,
                'translation_domain' => 'UploadProfile',
                'multiple' => true,
                'required' => false,
                'attr' => array(
                  'class' => 'channel1s'
                ),
            );

            if ($channel1s){
              $formOptions['data'] = $channel1s;
            }
            $form->add('channel1s', 'entity', $formOptions);
          }

          public function preSetData(FormEvent $event) {
            $data = $event->getData();
            $form = $event->getForm();

                if (null === $data) {
                    return;
                }

            $accessor = PropertyAccess::createPropertyAccessor();
            $agency = $accessor->getValue($data, $this->pathToAgency);
            $channel1s = ($agency) ? $agency->getChannel3s()->getChannel1s() : null;
            $this->addChannel1Form($form, $channel1s);
          }

          public function preSubmit(FormEvent $event) {
            $form = $event->getForm();
            $this->addChannel1Form($form);
          }
    ...

Now I'm getting the error "Attempted to call an undefined method named "getChannel3s" of class "Doctrine\Common\Collections\ArrayCollection"." and (I think) this is because my $data in my preSetData is NULL but I don't know why it's null. Am I looking at the wrong spot or where is my mistake here?

halfer
  • 19,824
  • 17
  • 99
  • 186
sonja
  • 924
  • 1
  • 21
  • 52

2 Answers2

4

preSetData is executed before the original data (which shall be modified if given) is bound to the form ( which is then stored in $options['data']).

The "data" in preSetData is the one you provide to createForm($type, $data = null, array $options = array()).

So before this is set -> the form obviously doesn't have any data and the event-data isn't set either. That's why $data is null inside your listener's onPreSetData method.

You're using the wrong event. Use preSubmit and build your logic around the data submitted by the user ($event->getData()). This will solve your issue.

Quick overview:

  • onPreSubmit:
    • $form->get('someButton')->isClicked() returns false
    • $event->getForm()->getData() returns $options['data'] if any or $options['empty_data']
    • $event->getData returns the submitted data (array)
    • you can use setData()
    • you can add/remove fields
  • onSubmit:
    • You can't use setData() here as data was already bound to the form
    • $form->isSubmitted() still returns false
    • $form->get('someButton')->isClicked() returns true
    • You can still add/remove fields
  • onPostSubmit:
    • $form->isSubmitted() returns true
    • "You cannot remove children from a submitted form"
    • "You cannot add children to a submitted form"
    • $form->get('someButton')->isClicked() returns true
Nicolai Fröhlich
  • 51,330
  • 11
  • 126
  • 130
  • Thank you for your detailed explanation, that makes totally sense to me! But I think I didn't get exactly what I should change. I already have a preSubmit function (see code from my question). So would you recommend copying the code from my preSetData to the preSubmit and just don't use the preSetData at all? Sorry for my misunderstanding! – sonja Aug 24 '17 at 12:41
  • Exactly. You can merge those two methods. – Nicolai Fröhlich Aug 24 '17 at 12:43
  • Ok thanks! I changed it in all three subscribers but now I get the error "Neither the property "channel1s" nor one of the methods "channel1s()", "getchannel1s()"/"ischannel1s()" or "__call()" exist and have public access in class "Symfony\Component\Form\FormView"." So apparently my twig template doesn't get the code from my subscribers.. In my FormType I add them with **$builder->addEventSubscriber...** and then I add the fields (e.g. channel1s) like in the code above with **$form->add('channel1s'...) do you have any ideas how I could get that working, too? :) – sonja Aug 24 '17 at 13:10
  • And one more question. I got my code from that page: http://showmethecode.es/php/symfony/symfony2-4-dependent-forms/ and here, the preSetData function is being used and apparently working, too. I don't yet see the difference, why in my case I don't use it. Do you know why? – sonja Aug 24 '17 at 15:46
  • brilliant and concise, you saved my evening : I was trying to get data in an embedded form, and none of the provided examples where working ;-)) – Kojo Sep 02 '19 at 16:34
  • Thank you @NicolaiFröhlich , your answer got me where i needed to be :) – Bossman Jun 22 '21 at 22:37
0

In the preSetData declaration you get the bad class. Try this :

public function preSetData(GenericEvent $event)

Add the next use :

use Symfony\Component\EventDispatcher\GenericEvent;
Athos
  • 181
  • 6