Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Symfony 2 - Best practice to upload an image on Amazon S3

I have a form in which I have a file field to upload an image. I need to upload this image on Amazon S3. Building this step by step I started to upload the image on the local disk and it's now working.

The upload is occurring inside my entity Page as it's recommended to test the success of the upload before to save the entity. I ended up with this chunk of code

    /**
     * @ORM\Column(name="objectThumbnail", type="string", length=255, nullable=true)
     */
    protected $objectThumbnail;

    /**
     * This is not a column in the database but it's mapping a field from the form
     *
     * @Assert\File(maxSize="600000000")
     */
    public $file;

    ...

    /**
     * @ORM\PrePersist()
     * @ORM\PreUpdate()
     */
    public function preUpload()
    {
        if (null !== $this->file) {
            // generate a unique filename
            $this->objectThumbnail = $this->getGuid().'.'.$this->file->guessExtension();
        }
    }

    /**
     * @ORM\PostPersist()
     * @ORM\PostUpdate()
     */
    public function upload()
    {
        if (null === $this->file) {
            return;
        }

        // if there is an error when moving the file, an exception will
        // be automatically thrown by move(). This will properly prevent
        // the entity from being persisted to the database on error
        $this->file->move($this->getUploadRootDir(), $this->objectThumbnail);

        unset($this->file);
    }

    /**
     * @ORM\PostRemove()
     */
    public function removeUpload()
    {
        if ($file = $this->getAbsolutePath()) {
            unlink($file);
        }
    }

That's perfect, it's working like a charm. Just Symfony (2.1.7) screaming because of the public scope of the file attribute, nothing major.

Now I need to integrate the S3 layer. For that I think I'll be using Gaufrette and StreamWrapper.

Now I'm struggling to find what is the best way to do it. How do I get access to the Filesystem service in the Entity? Is it really clean to do it like this? It feels a bit awkward to me to process the upload of the image on S3 in the Entity.

How would you recommend to do it ?

Cheers,

Maxime

like image 615
maxwell2022 Avatar asked Jan 15 '23 05:01

maxwell2022


1 Answers

using service in your entity is not a good pratice. I would recommand to create a form handler that performs validation, persistance and upload in this example

i wrote an example that fits your needs

//...
//inside a creation controller action
//...
//create a new page entity
$page = new Page();

//get your page form class
$form = $this->createForm(new PageForm(), $page);

//call your form handler
$formHandler = new PageFormHandler(
        $form,
        $this->get('request'),
        $this->get('your.gaufrette.filesystem'),
        $this->get('your.page.manager')
);

//form is valid ?
if ($formHandler->process()) {         
    //flash message, redirection etc
}

The handler class

namespace YourCompagnyBundle\Form\Handler;

use YourCompagnyBundle\Entity\Page;
use Symfony\Component\Form\Form;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Gaufrette\Filesystem;


class PageFormHandler
{
    protected $form;
    protected $request;
    protected $pageManager;
    protected $filesystem;

    public function __construct(Form $form, Request $request, Filesystem $filesystem, $pageManager)
    {
        $this->form    = $form;
        $this->request = $request;
        $this->filesystem = $filesystem;
        $this->pageManager = $pageManager;
    }

    public function process()
    {
        if( $this->request->getMethod() == 'POST' )
        {
            $this->form->bind($this->request);

            if( $this->form->isValid() )
            {
               $this->onSuccess($this->form->getData());               

                return true;
            }
        }

        return false;
    }

    function onSuccess(Page $page)
    {
        //has uploaded file ?
        if($page->getFile() instanceof UploadedFile){
            //do some logic with gaufrette filesystem
           //if page is already managed in database (update), delete previous file


        }
        //storage
        $this->pageManager->save($page);
    }

}

Hope this helped

like image 74
Julien Rollin Avatar answered Jan 21 '23 11:01

Julien Rollin