16

I can't seem to wrap my head around how I would go about adding a file upload to a DataFixture. I'm trying to upload an image for the dummy content my fixtures load. This seems like something that would be useful to know.

Elnur Abdurrakhimov
  • 44,533
  • 10
  • 148
  • 133
ornj
  • 720
  • 7
  • 16

4 Answers4

21

Although this question has been asked 1 year ago it appears that there is not a lot of information out there on how to upload a file via doctrine data fixture. I could only find this post.

I've been looking and I've taken a slightly different approach than ornj's. (Might have to do with Symfony's updates.)

I first had to

use Symfony\Component\HttpFoundation\File\UploadedFile;

and then used copy() to copy the image because as ornj said it will move it.

copy($art1->getFixturesPath() . '01.jpg', $art1->getFixturesPath() . '01-copy.jpg');

Then create and add the file by using:

$file = new UploadedFile($art1->getFixturesPath() . '01-copy.jpg', 'Image1', null, null, null, true);

$art1->setFile($file);

$manager->persist($art1);

If I did not set the last parameter to ''true'' in the ''UploadedFile'' constructor as it throws an unknown error when running ''doctrine:fixtures:load''. This parameter is "Whether the test mode is active". Seeing it's a fixture it makes sense to set to test mode.

The method ''getFixturesPath()'' just retrieves the path where my sample images are stored:

// Entity file
public function getFixturesPath()
{
    return $this->getAbsolutePath() . 'web/uploads/art/fixtures/';
}

The ''getAbsolutePath()'' method has been taken from Doctrine File Uploads.

The full working code: Entity:

<?php
//src/User/MyBundle/Entity/Art.php

namespace User/MyBundle/Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\Validator\Constraints as Assert;

/**
 * 
 * Art Entity
 * 
 * @ORM\Entity(repositoryClass="User\MyBundle\Entity\Repository\ArtRepository")
 * @ORM\Table(name="art")
 * @ORM\HasLifecycleCallbacks
 */
class Art
{
    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;

    /**
     * @ORM\Column(type="string", length=100)
     */
    protected $title;

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

    /**
     * @Assert\File(maxSize="6000000")
     */
    private $file;

    private $temp;

    public function getAbsolutePath()
    {
        return null === $this->path ? null : $this->getUploadRootDir() . '/' . $this->path;
    }

    public function getWebPath()
    {
        return null === $this->path ? null : $this->getUploadDir() . '/' . $this->path;
    }

    protected function getUploadRootDir()
    {
        // the absolute directory path where uploaded
        // documents should be saved
        return __DIR__ . '/../../../../web/' . $this->getUploadDir();
    }

    protected function getUploadDir()
    {
        // get rid of the __DIR__ so it doesn't screw up
        // when displaying uploaded doc/image in the view.
        return 'uploads/art';
    }

    public function getFixturesPath()
    {
        return $this->getAbsolutePath() . 'web/uploads/art/fixtures/';
    }

    /**
     * Sets file.
     *
     * @param UploadedFile $file
     */
    public function setFile(UploadedFile $file = null)
    {
        $this->file = $file;
        // check if we have an old image path
        if (isset($this->path)) {
            // store the old name to delete after the update
            $this->temp = $this->path;
            $this->path = null;
        } else {
            $this->path = 'initial';
        }
    }

    /**
     * Get file.
     *
     * @return UploadedFile
     */
    public function getFile()
    {
        return $this->file;
    }

    /**
     * @ORM\PrePersist()
     * @ORM\PreUpdate()
     */
    public function preUpload()
    {
        if (null !== $this->getFile()) {
            // do whatever you want to generate a unique filename
            $filename = sha1(uniqid(mt_rand(), true));
            $this->path = $filename . '.' . $this->getFile()->guessExtension();
        }
    }

    /**
     * @ORM\PostPersist()
     * @ORM\PostUpdate()
     */
    public function upload()
    {
        // the file property can be empty if the field is not required
        if (null === $this->getFile()) {
            return;
    }

        // if there is an error 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->getFile()->move($this->getUploadRootDir(), $this->path);

        // check if we have an old image
        if (isset($this->temp)) {
            // delete the old image
            unlink($this->getUploadRootDir() . '/' . $this->temp);
            // clear the temp image path
            $this->temp = null;
        }

        $this->file = null;
    }

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

Fixture:

<?php
// src/User/MyBundle/DataFixtures/ORM/ArtFixtures.php

namespace User\MyBundle\DataFixtures\ORM;

use Doctrine\Common\DataFixtures\AbstractFixture;
use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
use Doctrine\Common\Persistence\ObjectManager;
use Fredzz\LotwBundle\Entity\Art;
use Symfony\Component\HttpFoundation\File\UploadedFile;

class ArtFixtures extends AbstractFixture implements OrderedFixtureInterface
{
    public function load(ObjectManager $manager)
    {
        $art1 = new Art();
        $art1->setTitle('MyTitle');
        $art1->setDescription('My description');

        copy($art1->getFixturesPath() . '01.jpg', $art1->getFixturesPath() . '01-copy.jpg');
        $file = new UploadedFile($art1->getFixturesPath() . '01-copy.jpg', 'Image1', null, null, null, true);
        $art1->setFile($file);

        $art1->setUser($manager->merge($this->getReference('user-1')));

        $manager->persist($art1);
        $manager->flush();
    }
}

Hope this helps someone! Sorry if something is wrong. I'm still learning :)

Darryl Hein
  • 142,451
  • 95
  • 218
  • 261
Fred John
  • 443
  • 3
  • 9
  • 1
    You including too much logic on your entity class! All this helpers(get absolute, get fixtures..) should be implemented in some helper service.. or at the `FixtureData` class – Almog Baku Oct 12 '13 at 23:28
  • 1
    This just saved me - the most important trick is to set UploadedFile's $testMode to true. – Maerlyn Apr 30 '14 at 10:57
9

I've found the answer to my question. I need to use the class Symfony\Component\HttpFoundation\File\File to create a File. Symfony will physically move the file and not create a copy so you need to either have a new file for each fixture, use use copy() to create a copy of the file that can be moved instead.

$image = new Image();
$file = new File('path/to/file.jpg');
$image->file = $file;
$om->persist($image);

Something like that.

Gottlieb Notschnabel
  • 9,408
  • 18
  • 74
  • 116
ornj
  • 720
  • 7
  • 16
  • Image is my entity that handles the file upload, http://symfony.com/doc/current/cookbook/doctrine/file_uploads.html – ornj Sep 07 '12 at 13:55
  • 5
    **Use `UploadedFile` instead of `File`!**, some bundles(like VichUploader) verify the file is uploaded.. (It actually took me one hour to figure out why VichUploader won't upload my fixture file to gaufrette.) – Almog Baku Oct 12 '13 at 23:26
  • 1
    It does not work. The working solution is in @Fred Duarte's anwser. – ioleo Mar 16 '15 at 18:37
0

The image you want to use should be located in your "Web" folder and you should only use the file pointer string (i.e. "/web/images/test.png") in your Data Fixture.

You should generally avoid storing images in your database.

Ken Hannel
  • 2,718
  • 17
  • 20
  • Of course. What I am trying to do is load a file through an entity and allow Doctrine to manage the file. Using [this article](http://symfony.com/doc/current/cookbook/doctrine/file_uploads.html) as an example, I'm trying to build on this to allow temporary files to be uploaded by a data fixture. – ornj Sep 07 '12 at 13:32
0

i created a FileUpload class for PHP 5.3+

How to use?:

Documentation

from RFC 3023 (XML Media Types):

The top-level media type "text" has some restrictions on MIME entities and they are described in [RFC2045] and [RFC2046]. In particular, the UTF-16 family, UCS-4, and UTF-32 are not allowed (except over HTTP[RFC2616], which uses a MIME-like mechanism).

Allow only yaml file to upload:

<?php
$file = new FileUpload\FileUpload();
$file->setInput( "file" );
$FileUpload->setAllowedMimeTypes(array(
    "text/x-yaml", //RFC 3023
    "application/x-yaml", // Ruby on Rails
    "text/plain",//Possible option( only text plain )
    "text/yaml",//Possible option
    "text/x-yaml",//Possible option
    "application/yaml",//Possible option
));
$file->setDestinationDirectory("/var/www/html/myapp/");
$file->save();
if ($file->getStatus()) {
    echo "Okay";
}
?>

Sample with all mime types:

<?php
$file = new FileUpload\FileUpload();
$file->setInput( "file" );
$file->save();
if ($file->getStatus()) {
    echo "is Upload!";
}
?>
<html>
    <head>
        <title>FileUpload Example</title>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    </head>
    <body>
        <form method="post" action="" enctype="multipart/form-data">
            <input type="file" name="file" />
            <input type="submit" value="Upload now!" />
        </form>
    </body>
</html>

GitHub: https://github.com/olaferlandsen/FileUpload-for-PHP

Olaf Erlandsen
  • 5,817
  • 9
  • 41
  • 73