Search code examples
phpsymfonysonata-adminvichuploaderbundlegaufrette

Vich and gaufrette are not saving files in sonata admin


I'm trying to make an upload linked to an entity in Sonata Admin using Vich.

All the configuration is done but the file does not upload, and I cannot find the error.

The problem is that when y try to upload the file, every thing seem to work fine, Sonata persists the data in all the data base fields, and the file is uploaded to /tmp folder in teh sistem,also, sonata prints the tmp route in the patch field in database. But the file never gets to the folder setted in gaufrette and neither generates the unique name.

Here is the code:

The admin Class:

<?php

namespace DownloadFileAdminBundle\Admin;

use Sonata\AdminBundle\Admin\AbstractAdmin;
use Sonata\AdminBundle\Admin\Admin;
use Sonata\AdminBundle\Datagrid\DatagridMapper;
use Sonata\AdminBundle\Datagrid\ListMapper;
use Sonata\AdminBundle\Form\FormMapper;

class DownloadFileAdmin extends Admin
{
    const FILE_MAX_SIZE = 2 * 1024 * 1024; // 2 megas

    /**
     * @param FormMapper $formMapper
     */
    protected function configureFormFields(FormMapper $formMapper)
    {
        $fileOptions = array(
            'label' => 'Archivo',
            'required' => true,
            'vich_file_object' => 'downloadfile',
            'vich_file_property' => 'downloadFile',
            'vich_allow_delete' => true,
            'attr' => array(
                'data-max-size' => self::FILE_MAX_SIZE,
                'data-max-size-error' => 'El tamaño del archivo no puede ser mayor de 2 megas'
            )
        );

        $formMapper
            ->add('slug', null, array('label' => 'Slug'))
            ->add('title', null, array('label' => 'Título'))
            ->add('description', null, array('label' => 'Descripción'))
            ->add('roles')
            ->add('path', 'DownloadFileAdminBundle\Form\Extension\VichFileObjectType', $fileOptions)
        ;

    }

    /**
     * @param ListMapper $listMapper
     */
    protected function configureListFields(ListMapper $listMapper)
    {
        $listMapper
            ->add('id')
            ->add('slug')
            ->add('title')
            ->add('description')
            ->add('path')
            ->add('roles')
            ->add('_action', null, array(
                'actions' => array(
                    'show' => array(),
                    'edit' => array(),
                    'delete' => array(),
                )
            ))
        ;
    }

}

Here is the entity, with the not persistent fieln and the path field, witch is wher eI want tostore the file path:

/**
     * NOTE: This is not a mapped field of entity metadata, just a simple property.
     * @Vich\UploadableField(mapping="download_file", fileNameProperty="path")
     * @var File
     */
    private $downloadFile;

    /**
     * @ORM\Column(type="string")
     */
    protected $path;

    public function getDownloadFile()
    {
        return $this->downloadFile;
    }

    /**
     * @param File|\Symfony\Component\HttpFoundation\File\UploadedFile $file
     *
     * @return File
     */
    public function setDownloadFile(File $file = null)
    {
        $this->downloadFile = $file;
        return $this;
    }

    /**
     * @return mixed
     */
    public function getPath()
    {
        return $this->path;
    }

    /**
     * @param mixed $path
     */
    public function setPath($path)
    {
        $this->path = $path;
    }

The services os admin.yml

services:
    sonata.admin.file:
        class: DownloadFileAdminBundle\Admin\DownloadFileAdmin
        arguments: [~, Opos\DownloadFileBundle\Entity\DownloadFile, SonataAdminBundle:CRUD]
        tags:
            - { name: sonata.admin, manager_type: orm, group: "Files", label: "Archivo" }

and services.yml:

services:
    download_file_admin_bundle.vich_file_object_type:
        class: DownloadFileAdminBundle\Form\Extension\VichFileObjectType
        arguments: [ "@doctrine.orm.entity_manager" ]
        tags:
            - { name: "form.type", alias: "vich_file_object" }

And last vich and gaufrette configuration:

vich_uploader:
    db_driver: orm
    storage:   gaufrette

    mappings:
        question_image:
            uri_prefix:         ~ 
            upload_destination: questions_image_fs
            namer:              vich_uploader.namer_uniqid
        download_file:
            uri_prefix:         ~
            upload_destination: download_file_fs
            namer:              vich_uploader.namer_uniqid

knp_gaufrette:
    stream_wrapper: ~

    adapters:
        questions_image_adapter:
            local:
                directory: %kernel.root_dir%/../web/images/questions
        download_file_adapter:
            local:
                directory: %kernel.root_dir%/../web/files/download

    filesystems:
        questions_image_fs:
            adapter:    questions_image_adapter
        download_file_fs:
            adapter:    download_file_adapter

Solution

  • VichUploaderBundle relies on Doctrine Events like pre persist/update to trgger its upload functionality. When you open existing entity in the admin section and upload new file without changing anything else, doctrine wouldn't dispatch lifecycle events as none of doctrine specific fields are changed.

    So whenever new file object passed to the entity you need to update some doctrine specific field value, like updatedAt. Modify setDownloadFile of the entity to:

    /**
     * @param File|\Symfony\Component\HttpFoundation\File\UploadedFile $file
     *
     * @return File
     */
    public function setDownloadFile(File $file = null)
    {
        $this->downloadFile = $file;
    
        if ($file) {
            $this->updatedAt = new \DateTimeImmutable();
        }
    
        return $this;
    }
    

    Also you need to add updatedAt field and it's mapping in case you didnt.

    Take a look at the example on VichUploaderBundle documentation page: https://github.com/dustin10/VichUploaderBundle/blob/master/Resources/doc/usage.md#step-2-link-the-upload-mapping-to-an-entity

    UPDATE

    Also you need to define form field on downloadFile property instead of path