Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Symfony2 Forms: Show already uploaded files when form is invalid

I'm building a complex file upload form. This form exists of normal data and a set of 4 filetypes with multiple file uploads possible per type.

  • ReportForm
    • Attachments
      • Reports (collection of UploadedFile Entities)
      • Photos (collection of UploadedFile Entities)

The UploadedFile entity has a filename, description etc..

Use case: Now, I'm submitting the form with 4 Photos and 2 Reports, all 6 files are ok. But there are some other errors in the form. But I want to show the uploaded files again so that the user doesn't have to reupload them again. These files are already persisted, so when I come back to the same page with a GET, files are shown correctly.

What I already did/tried:

  • Before I bind the request I clone the existing uploaded files
  • After binding the form I upload all new files (if any) and persist them
  • After that I re-add existing uploaded files (from the cloned object). I created form inputs with the existing file id's in, so I can recognise the existing items.

That's all working fine, BUT since I change my Report and Attachments AFTER the bind the data shown after a post can't be changed. I can't do setData($report) on a submitted form.

So existing files aren't show anymore as the form data is still the old report object (from the post).

When I do a normal GET of the page, the $report is retrieved from the database and is shown correctly. But after a POST with already uploaded files, the database data is correct but the form view doesn't know anything about changed data (after the bind).

Any ideas? Or a better way to do this?

like image 445
jayv Avatar asked Oct 17 '13 09:10

jayv


2 Answers

I've made solution below. You can adapt it for your needs. But you need additional command that cleans up old files in uploads/tmp directory. And you need two parameters under parameters section in config:

parameters:
    uploads_dir: %kernel.root_dir%/../web/uploads
    uploads_tmp_dir: %uploads_dir%/tmp

The form:

public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder
        ->add('file',ImageFileType::TYPE_NAME,array(
            'fileField' => 'file',
            'pathField' => 'path',
            'required' => false,
            'label' => 'makeswapping.form.single_image',
            'imgclass' => 'tumbnail imgbox full-width',
            'constraints' => array(
                new Assert\Image(array(
                    'minWidth' => '440'
                    ))
            )
        ));

    $container = $this->container;

    $builder->addEventListener(
        FormEvents::PRE_SUBMIT,
        function (FormEvent $event) use ($container) {

            $form = $event->getForm();
            $data = $event->getData();

            $uploadsTmpDir = $container->getParameter('uploads_tmp_dir');
            if (isset($data['file'])) {
                $filename = sprintf('%s.%s', uniqid('tmp_'), $data['file']->getClientOriginalExtension());
                if (!file_exists($uploadsTmpDir)) {
                    mkdir($uploadsTmpDir, 0755, true);
                }
                copy($data['file']->getRealPath(), $uploadsTmpDir . '/' . $filename);
                $form->add('file_hidden', 'hidden', array(
                    'mapped' => false,
                    'required' => false,
                    'empty_data' => $filename,
                ));
            } else if (!isset($data['file']) && isset($data['file_hidden'])) {
                $fileInfo = new \SplFileInfo($uploadsTmpDir . '/' . $data['file_hidden']);

                $mimeTypeGuesser = MimeTypeGuesser::getInstance();
                $uploadedFile = new UploadedFile(
                    $fileInfo->getRealPath(),
                    $fileInfo->getBasename(),
                    $mimeTypeGuesser->guess($fileInfo->getRealPath()),
                    $fileInfo->getSize(),
                    null,
                    true
                );

                $form->add('file_hidden', 'hidden', array(
                    'mapped' => false,
                    'required' => false,
                    'empty_data' => $data['file_hidden'],
                ));

                $form->get('file')->setData($uploadedFile);
                $data['file'] = $uploadedFile;
                $event->setData($data);
            }
        }
    );
}
like image 109
Serhii Smirnov Avatar answered Oct 21 '22 14:10

Serhii Smirnov


Ok, after some days of trail and error I found a matching solution.

Here is what I did, in short.

In the UploadedFile entity I added 2 new fields: file and uniqueId. These fields aren't mapped in ORM as I only need them in the form.

When submitting the form I add newly uploaded file in a specific session var, and assign a unique id to them (because I need to be able to delete them and I needed an identifier). I also upload the files to their new location.

If the form is valid I add this collection I saved in the session to the entity that holds this collection.

If the form is invalid the files are shown because I retrieve this specific collection from the session. Even when I revisit the page with GET, all uploaded files are shown.

If I delete a file in the view I put a list of deleted unique id's in an input field. So after the submit I check which files I should delete from the array.

I think my answer is as unclear as my question :)

like image 3
jayv Avatar answered Oct 21 '22 16:10

jayv