4

I researched the How to Handle File Uploads with Doctrine and I don't want to hard-code the __DIR__.'/../../../../web/'.$this->getUploadDir(); path because maybe in future I will change the web/ directory. How to do it more flexible? I found this but it doesn't answer the question how to do it more flexible from inside the Entity

Community
  • 1
  • 1
Mikhail
  • 2,542
  • 4
  • 29
  • 40

4 Answers4

6

You shouldn't use entity class as a form model here. It's simply not suitable for that job. If the entity has the path property, the only valid values it can stores are: null (in case lack of the file) and string representing the path to the file.

  1. Create a separate class, that's gonna be a model for your form:

    class MyFormModel {
         /** @Assert\File */
         private $file;
    
         /** @Assert\Valid */
         private $entity;
    
         // constructor, getters, setters, other methods
    }
    
  2. In your form handler (separate object configured through DIC; recommended) or the controller:

    ...
    if ($form->isValid()) {
        /** @var \Symfony\Component\HttpFoundation\File\UploadedFile */
        $file   = $form->getData()->getFile();
    
        /** @var \Your\Entity\Class */
        $entity = $form->getData()->getEntity();
    
        // move the file
        // $path = '/path/to/the/moved/file';
    
        $entity->setPath($path);
    
        $someEntityManager->persist($entity);
    
        return ...;
    }
    ...
    

Inside form handler/controller you can access any dependencies/properties from DIC (including path to the upload directory).


The tutorial you've linked works, but it's an example of bad design. The entities should not be aware of file upload.

Crozin
  • 43,890
  • 13
  • 88
  • 135
  • I understood. Thanks. But how to use a lifecycle callbacks to manage a deletion of the file with your code? – Mikhail Oct 15 '12 at 10:57
  • Based on previous experience I suggest you store absolute path (or at least relative to webroot path) as well.. – Jovan Perovic Oct 15 '12 at 11:05
  • Create a listener, that subscribes `preRemove` (or `preDelete`) event (see [how to](http://symfony.com/doc/current/cookbook/doctrine/event_listeners_subscribers.html)) and unlink the file there. – Crozin Oct 15 '12 at 11:13
1

To access the root directory from outside the controller you can simply inject '%kernel.root_dir%' as an argument in your services configuration.

service_name:
    class: Namespace\Bundle\etc
    arguments: ['%kernel.root_dir%']

Then you can get the web root in the class constructor:

public function __construct($rootDir)
{
    $this->webRoot = realpath($rootDir . '/../web');
}
redjam13
  • 1,016
  • 9
  • 11
-1

You can use a variable in your parameters.yml. Like this you'll can change path when you want.

for example :

# app/config/parameters.yml
# Upload directories
upload_avatar_dir:        /uploads/avatars
upload_content_dir:       /uploads/content
upload_product_offer_dir: /uploads/product-offer
...
Benjamin Lazarecki
  • 2,950
  • 1
  • 20
  • 27
-1

I handled this by creating an abstract class that Entities may extend if they are handling file uploads as described in the Symfony Documentation. I created the files array so I could create a copy of the existing file path in the set methods so it could be deleted off the file system on a successful update or delete without defining any additional properties in the Entity proper.

use Symfony\Component\HttpFoundation\File\File;

abstract class FileUploadEntity
{
    private $files;

    public function __set($name, File $value)
    {
        $this->files[$name] = $value;
    }

    public function __get($name)
    {
        if (!is_array($this->files)) $this->files = array();

        if (!array_key_exists($name, $this->files)) {
            return null;
        }
        return $this->files[$name];
    }

    public function getUploadRootDirectory()
    {
        return $this->getWebDirectory() . $this->getUploadDirectory();
    }

    public function getWebDirectory()
    {
        return __DIR__ . "/../../../../web/";
    }

    public function getUploadDirectory()
    {
        $year = date("Y");
        $month= date("m");

        return "images/uploads/$year/$month/";
    }

    public function getEncodedFilename($name)
    {
        return sha1($name . uniqid(mt_rand(), true));
    }

    // this should be a PrePersist method
    abstract public function processImages();

    // This should be defined as a Doctrine PreUpdate Method
    abstract public function checkImages();

    // this should be a PostPersist method
    abstract public function upload();

    // this should be a PostUpdate method and delete old files
    abstract public function checkUpload();

    // This should be a PostRemove method and delete files
    abstract public function deleteFile();
}