craue / CraueFormFlowBundle

Multi-step forms for your Symfony project.
MIT License
736 stars 118 forks source link

Event Listeners fire twice #329

Closed ryanm352 closed 4 years ago

ryanm352 commented 5 years ago

Adding event listeners to the $builder forces event listeners to fire twice in multistep forms.

switch ( $this->step ) {
  case '4':
  $builder->add( 'cobjDetails', CollectionType::class,
                        [
                            'label'        => false,
                            'entry_type'   => COBJType::class,
                            'allow_add'    => true,
                            'allow_delete' => true,
                            'by_reference' => false,
                            'prototype'    => true,
                            'delete_empty' => true,
                            'required'     => true,
                            'attr'         => [ 'data-show-required' => false ]
                        ]
                )
->addEventListener( FormEvents::PRE_SUBMIT, function ( FormEvent $event ) {
      dump( 'post submit' );
});
break;
}

in this case, on step 4, "post submit" displays twice

craue commented 5 years ago

I'm unable to reproduce this, only by calling $flow->bind(...) twice explicitly.

ryanm352 commented 5 years ago

strange.. I'm only calling bind once in my controller, too. I should clarify: When I go to step 5, that's when the event for step 4 fires twice (pre_submit event). I was under the impression that attaching a listener as I have done in step 4 would only fire the listener for step 4 since it shouldn't be attached in step 5.

Should I be binding and validating my entity at a certain step in FormFlowEvents::POST_VALIDATE?

public function startTravelVoucherFormAction( TravelVoucherFormFlow $flow, FormErrors $form_errors ) {

        $this->formErrors = $form_errors;

        $flow->bind( new TravelVoucher() );
        // form of the current step
        $form = $flow->createForm();
        if ( $flow->isValid( $form ) ) {

            $flow->saveCurrentStepData( $form );

            if ( $flow->nextStep() ) {
                // form for the next step
                $form = $flow->createForm();
            } else {
                // last step
                if ($flow->getLastStepNumber() === $flow->getCurrentStepNumber()) {
                    $page1File = Helper::buildPage(1, $flow);
                    //$file = new File($page1File);

                    $response = new Response();
                    $response->headers->set('Cache-Control', 'private');
                    $response->headers->set('Content-type', 'application/pdf');
                    $response->headers->set('Content-Disposition', 'attachment; filename="test.pdf";');
                    $response->headers->set('Content-length', strlen($page1File));
                    $response->setContent($page1File);

                    return $response;

                    return $this->file($page1File);

                    exit;
                }
                dump( 'here - submit' );
                dump($form);
                exit;
                // flow finished
                // write to database
                //$em = $this->getDoctrine()->getManager();
                //$em->persist( $formData );
                //$em->flush();

                $flow->reset(); // remove step data from the session

                return $this->redirect( $this->generateUrl( 'home' ) ); // redirect when done
            }
        }

        $sr_errors = $this->getFormErrors( $flow, $form );

        return $this->render( './Sections/Travel/TravelVoucher/travelVoucherForm.html.twig', array(
            'form'       => $form->createView(),
            'flow'       => $flow,
            'formData'   => $form->getViewData(),
            'page_title' => 'Travel Voucher',
            'sr_errors'  => $sr_errors
        ) );
    }
craue commented 5 years ago

Does your service setup for flows make use of Symfony's autoconfiguration feature? Because while trying that, I now notice exactly what you describe: all events occur twice. I'm not sure yet about the source of this issue, whether it's the bundle or Symfony.

ryanm352 commented 5 years ago

Yes, autowire and autoconfigure are both set to true.

craue commented 5 years ago

Then removing this method (given in the README) from your flow should solve the issue:

public function setEventDispatcher(EventDispatcherInterface $dispatcher) {
    parent::setEventDispatcher($dispatcher);
    $dispatcher->addSubscriber($this);
}

Or, to work with and without autoconfiguration, replace it by this:

public function setEventDispatcher(EventDispatcherInterface $dispatcher) {
    parent::setEventDispatcher($dispatcher);
    $dispatcher->removeSubscriber($this);
    $dispatcher->addSubscriber($this);
}
craue commented 5 years ago

@er0sion, could you have a look at #333? I'm not working with autoconfiguration on any real project yet.

DarrHeLL commented 2 years ago

Sorry to comment on this old issue,

but i'm using autoconfigure and autowiring and I have the same issue. Events from Symfony type are fired twice and don't find any idea to solve this

Edit : (event is on my step 3) It appear to be fired twice when only when I go back from step 4 to step 3 and the go forward on step 4

image image