craue / CraueFormFlowBundle

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

Modify FormData in FormFlow event and saveStepData after that #113

Open rvanlaak opened 10 years ago

rvanlaak commented 10 years ago

I've used all different types of events, but with none of them it is possible to use the FormData from the current request, do something with that data, and save the new data to the storage.

My use case is that I want to save a UploadedFile to the database, and store the id of that new Entity into the FormFlow by using saveStepData().

The problem is, that this data gets overwritten if the controller calls $flow->saveCurrentStepData($form);

Since no event are triggered after the above call, the data from the storage always will get overwritten by the formData.

In other words: in what way is it possible to add extra info to the storage, after the form has been submitted and validated? Idea to dispatch two new events, called PRE_STORAGE_DATA and POST_STORAGE_DATA?

craue commented 10 years ago

File uploads are a tough topic. What exactly do you mean by

store the id of that new Entity into the FormFlow by using saveStepData()

? If you add the id (as an attribute) to the data object used for the flow, saving the data should work properly. #49 could also be interesting in this context.

rvanlaak commented 10 years ago

I did saw #49 indeed, and already tried to set the formData before and after validation. When I alter the value, the form isn't valid anymore with as a result an Exception. This makes it impossible to modify the data type.

Any reasons why it should not be favorable to add two extra events? I can't imagine some...

craue commented 10 years ago

I'm not sure if the way you're trying to deal with the data is the right one, or if adding more events would serve as some kind of workaround only.

Taking a look at code I'm using in an app to handle uploads is essentially what's done by https://github.com/craue/CraueFormFlowBundle/issues/29#issue-5089782 – saving additional data (separated from the data the flow stores itself) in onPostValidate, reading it in onPreBind, and cleaning up by overriding reset. It's quite old, though, and I'd rather use onPostBindSavedData instead of onPreBind. Would that work in your case?

rvanlaak commented 10 years ago

Thanks for mentioning that issue, but that one is about skipping steps and resetting the form right? The thing I try to archieve is saving extra data to the session, but the problem still stays the same.

I use the following snippet in the last possible event to add extra data to the session:

/**
 * Store UploadedFile to disk and persist to database
 */
public function saveFiles(PostValidateEvent $event)
{
    $formData = $event->getFormData();
    $dataId = $this->getId() . '_data_' . $this->getInstanceId();
    $data = $this->storage->get($dataId);

    // Handle all files from the flow
    foreach ($formData as $fieldKey => $fieldFile) {
        if ($fieldFile instanceof UploadedFile) {

            $diskFile = new Document();
            $diskFile->setFile($fieldFile);

            $this->em->persist($diskFile);
            $this->em->flush();

            $data[$this->getCurrentStep()][$fieldKey] = $diskFile->getId();
        }
    }

    $this->storage->set($dataId, $data);
}

This works great, the data of the form is overwritten by the id of the file I saved to disk. The $entityManager is injected in the Flow by DI. But now the problem occurs; the Controller class still has to call

$flow->saveCurrentStepData($form);

In other words, saving extra data to the session works great but thereafter it gets overwritten by the regular save process of the flow. In other words; I could call the first snippet above in the controller after the saveCurrentStepData() is called.

The Flow has no other events after that call, so if I'm right this makes it impossible to add data to the session in the Flow itself since it always will get overwritten. So, this has to be handled in the Controller.

rvanlaak commented 10 years ago

I did get it functioning correctly now by extending saveCurrentStepData(). The following snippet made it possible for me to save the files, and registering to the events is not needed anymore:

/**
 * Override saving of data, and save files to disk
 * {@inheritDoc}
 */
public function saveCurrentStepData(FormInterface $form) 
{
    parent::saveCurrentStepData($form);
    $this->saveFiles();
}