Multiple file upload with Symfony2

2019-01-08 10:54发布

I'm trying to upload multiple files via a form, but I can only upload one file at a time, the last one I mark in the browser. Is there a way to upload more images with Symfony2 using a simple form? Here is the twig template of the form I'm using to be able to mark more than one file:

{{ form_widget(form.post_image, { 'attr': {'multiple': 'multiple' }}) }} 

11条回答
爷的心禁止访问
2楼-- · 2019-01-08 11:51

Methods getChild and set() were removed in 2.3. Instead of this you should use children[] and vars properties

before:

$formView->getChild('files')->set('full_name', 'form[files][]');

after:

$formView->children['files']->vars = array_replace($formView->children['files']->vars, array('full_name', 'form[files][]'));
查看更多
Ridiculous、
3楼-- · 2019-01-08 11:54

Here is easy example to upload multiple files. I have similar problem with upload files.

https://github.com/marekz/example_symfony_multiply_files_example

For symfony 3.*

First: Both form declatartion:

    <?php

namespace AppBundle\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use AppBundle\Form\FilesType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;

class UserType extends AbstractType
{
    /**
     * {@inheritdoc}
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
                ->add('name')
                ->add('lastName')
                ->add('files', CollectionType::class,array(
                    'entry_type' => FilesType::class,
                    'allow_add' => true,
                    'by_reference' => false,
                ))
                ;
    }

    /**
     * {@inheritdoc}
     */
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'AppBundle\Entity\User'
        ));
    }

    /**
     * {@inheritdoc}
     */
    public function getBlockPrefix()
    {
        return 'appbundle_user';
    }


}


    <?php

namespace AppBundle\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class FilesType extends AbstractType
{
    /**
     * {@inheritdoc}
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('file');
    }

    /**
     * {@inheritdoc}
     */
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'AppBundle\Entity\Files'
        ));
    }

    /**
     * {@inheritdoc}
     */
    public function getBlockPrefix()
    {
        return 'appbundle_files';
    }


}

Now, my entities:

<?php

namespace AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;

/**
 * User
 *
 * @ORM\Table(name="user")
 * @ORM\Entity(repositoryClass="AppBundle\Repository\UserRepository")
 */
class User {

    /**
     * @var int
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @var string
     *
     * @ORM\Column(name="name", type="string", length=255)
     */
    private $name;

    /**
     * @var string
     *
     * @ORM\Column(name="lastName", type="string", length=255)
     */
    private $lastName;

    /**
     * @ORM\ManyToMany(targetEntity="Files", cascade={"persist"})
     */
    private $files;

    function __construct() {
        $this->files = new ArrayCollection();
    }

    /**
     * Get id
     *
     * @return int
     */
    public function getId() {
        return $this->id;
    }

    /**
     * Set name
     *
     * @param string $name
     *
     * @return User
     */
    public function setName($name) {
        $this->name = $name;

        return $this;
    }

    /**
     * Get name
     *
     * @return string
     */
    public function getName() {
        return $this->name;
    }

    /**
     * Set lastName
     *
     * @param string $lastName
     *
     * @return User
     */
    public function setLastName($lastName) {
        $this->lastName = $lastName;

        return $this;
    }

    /**
     * Get lastName
     *
     * @return string
     */
    public function getLastName() {
        return $this->lastName;
    }

    /**
     * Get files
     * 
     * @return ArrayCollection
     */
    function getFiles() {
        return $this->files;
    }

    /**
     * Set files
     * @param type $files
     */
    function setFiles($files) {
        $this->files = $files;
    }
}

    <?php

namespace AppBundle\Entity;

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

/**
 * Files
 *
 * @ORM\Table(name="files")
 * @ORM\Entity(repositoryClass="AppBundle\Repository\FilesRepository")
 */
class Files
{
    /**
     * @var int
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @var string
     *
     * @ORM\Column(name="file", type="string", length=255, unique=true)
     * @Assert\NotBlank(message="Please, upload the product brochure as a PDF file.")
     * @Assert\File(mimeTypes={ "application/pdf" })
     */
    private $file;

    /**
     *
     * @return Files
     */
    function getUser() {
        return $this->user;
    }

    /**
     * Get id
     *
     * @return int
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * Set file
     *
     * @param string $file
     *
     * @return Files
     */
    public function setFile($file)
    {
        $this->file = $file;

        return $this;
    }

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

Finaly, Symfony Controller:

/**
 * Creates a new user entity.
 *
 * @Route("/new", name="user_new")
 * @Method({"GET", "POST"})
 */
public function newAction(Request $request) {
    $user = new User();
    $form = $this->createForm('AppBundle\Form\UserType', $user);
    $form->handleRequest($request);

    if ($form->isSubmitted() && $form->isValid()) {

        $attachments = $user->getFiles();

        if ($attachments) {
            foreach($attachments as $attachment)
            {
                $file = $attachment->getFile();

                var_dump($attachment);
                $filename = md5(uniqid()) . '.' .$file->guessExtension();

                $file->move(
                        $this->getParameter('upload_path'), $filename
                );
                var_dump($filename);
                $attachment->setFile($filename);
            }
        }

        $em = $this->getDoctrine()->getManager();
        $em->persist($user);
        $em->flush();

        return $this->redirectToRoute('user_show', array('id' => $user->getId()));
    }

    return $this->render('user/new.html.twig', array(
                'user' => $user,
                'form' => $form->createView(),
    ));
}
查看更多
Deceive 欺骗
4楼-- · 2019-01-08 11:56

use this methode :

$form = $this->createFormBuilder()
->add('attachments','file', array('required' => true,"attr" => array(
  "multiple" => "multiple",
  )))
->add('save', 'submit', array(
  'attr' => array('class' => 'btn btn-primary btn-block btn-lg'),
  'label' => 'save'
  ))
->getForm();

then you add [] to the name of your input via jQuery :

<input id="form_attachments" name="form[attachments]" required="required" multiple="multiple" type="file">

jQuery code :

 <script>
  $(document).ready(function() {
    $('#form_attachments').attr('name',$('#form_attachments').attr('name')+"[]");
  });
</script>
查看更多
爷的心禁止访问
5楼-- · 2019-01-08 11:56

Note that i try to do the same thing in sf2 using this syntax:

In the controller:

public function stuffAction() {

$form = $this->createFormBuilder()
                ->add('files','file',array(
                    "attr" => array(
                        "accept" => "image/*",
                        "multiple" => "multiple",
                    )
                ))
                ->getForm();

        $formView = $form->createView();
        $formView->getChild('files')->set('full_name', 'form[files][]');

// name param (eg 'form[files][]') need to be the generated name followed by []
// try doing this : $formView->getChild('files')->get('full_name') . '[]'


        $request = $this->getRequest();
        if($request->getMethod() == "POST") {

            $form->bindRequest($request);

            $data = $form->getData();
            $files = $data["files"];

            // do stuff with your files
        }

    }

    return $this->render('Bundle:Dir:index.html.twig',array("form" => $formView));
}

$files will be an array of uploaded files...

Calling $form->createView() to alter the name attribute is certainly not the best way / cleanest way to do it but it's the only one i found that keeps the csrf functionality working, because altering the name attribute in a twig template makes it invalid...

Now I still have an issue using a form class which generate the form, I don't know why during the binding of the form data & object attached to the form my array of uploaded files is transformed in array of (file) name ???

查看更多
forever°为你锁心
6楼-- · 2019-01-08 11:57

For sf > 2.2 : In you form type class, add this overrided method :

public function finishView(FormView $view, FormInterface $form, array $options) {
    $view->vars['form']->children['files']->vars['full_name'] .= '[]';
}
查看更多
登录 后发表回答