1

Implemented downloading files over the documentation. But when i decided to use 'multiple', issue an error

"Catchable Fatal Error: Argument 1 passed to Kulabuhov\MainBundle\Entity\Photo::setFile() must be an instance of Symfony\Component\HttpFoundation\File\UploadedFile, array given," 

. I understand that instead of Object passed an array. But how to implement multiple file uploads without breaking anything you wrote in the documentation?

$builder
            ->add('file','file',['multiple'=>true])
            ->add('article')
        ;
sjagr
  • 15,983
  • 5
  • 40
  • 67
volya pers
  • 71
  • 6

2 Answers2

1

You've provided very little code as to how you've followed the file upload feature with Doctrine which would be helpful because there are a dozen ways to approach how the file naming convention is performed, plus you haven't really even shown your own attempt at modifying the entity you're working with the handle multiple file uploading, but I'll give this a shot for you. This is assuming you've followed all of the instructions to the very end.

When you followed the instructions, you probably created a setFile function as something like:

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';
    }
}

but of course, you are submitting an array of UploadedFile objects to the setFile function which doesn't match the UploadedFile argument that it needs. You also are only storing a single string for the path, so you have to fix that too.

So first you should change your $path variable type from a string to some type of array. Assuming you don't need much data associated with each file and the filenames will have no commas in them ever, let's just stick with a simple_array:

/**
 * @ORM\Column(name="paths", type="simple_array")
 */
private $paths = array();

And now you need an array declared for the file attribute:

/**
 * @Assert\File(maxSize="60000000")
 */
private $files = array();

Now you have to change your setFile function to handle the array input (let's rename to setFiles for consistency):

public function setFiles($files)
{
    $this->files = $files;
    // In this scenario, we'll delete the old batch of uploads, so store it in $temp
    if (!empty($this->paths)) {
        $this->temp = $this->getAbsolutePaths();
    }
    $this->paths = array();
}

And getFiles:

public function getFiles()
{
    return $this->files;
}

Now your new upload() function:

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

    // Now we have to iterate through each file object
    foreach ($this->getFiles() as $file) {
        // Change $tempPath to whatever random filename strategy you use
        $tempPath = sha1(uniqid(mt_rand(), true)) . '.' . $file->guessExtension();
        $file->move(
            $this->getUploadRootDir(),
            $tempPath
        );
        // Now add it to the paths array
        $this->paths[] = $tempPath;
    }

    // Need to delete all of the old files
    foreach ($this->temp as $del) {
        if (is_file($del)) {
            unlink($del);
        }
    }

    // Reset the $files var
    $files = array();
}

and then for deletions, remove all files entirely:

/**
 * @ORM\PostRemove()
 */
public function removeUpload()
{
    foreach ($this->getAbsolutePaths() as $path) {
        if (is_file($path)) {
            unlink($path);
        }
    }
}

For when you're working with the object, you're now handling an array of paths, so you have to update your web and absolute path functions as well:

public function getAbsolutePaths()
{
    $out = array();
    foreach ($this->getPaths() as $path) {
        $out[] = $this->getUploadRootDir().'/'.$this->path;
    }
    return $out;
}

public function getWebPath()
{
    $out = array();
    foreach ($this->getPaths() as $path) {
        $out[] = '/' . $this->getUploadDir() . '/' . $this->path;
    }
}

and voila... once you update your builder to go to files instead of file, it should be able to handle the multiple file uploads correctly. Even though the above code is all untested, this should give you plenty to work with.

sjagr
  • 15,983
  • 5
  • 40
  • 67
  • Did as you wrote, but got this error `The form's view data is expected to be an instance of class Symfony\Component\HttpFoundation\File\File, but is a(n) array. You can avoid this error by setting the "data_class" option to null or by adding a view transformer that transforms a(n) array to an instance of Symfony\Component\HttpFoundation\File\File.` – volya pers Oct 24 '14 at 19:47
0

The problem isn't with your form, it's with your model. The setFile() method on Photo is meant to receive an UploadedFile, not an array. If the photo is meant to have multiple files, declare the relationship as OneToMany and implement a setFiles() function that accepts an array:

protected $files;

function setFiles(array $files)
{
    $this->files = $files;

    return $this;
}
ChrisMoll
  • 365
  • 1
  • 12