web-dev-qa-db-fra.com

Téléchargement de fichiers multiples avec Symfony2

J'essaie de télécharger plusieurs fichiers via un formulaire, mais je ne peux télécharger qu'un seul fichier à la fois, le dernier que je marque dans le navigateur. Existe-t-il un moyen de télécharger plus d'images avec Symfony2 en utilisant un formulaire simple? Voici le modèle twig du formulaire que j'utilise pour pouvoir marquer plus d'un fichier:

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

Aucune classe supplémentaire nécessaire (sauf le service gallery_manger mais le problème que vous décrivez se produit avant ...)

Je ne sais pas vraiment ce qui ne va pas. Vérifiez votre modèle (peut-être un mauvais type de code ... ou le nom attr ne correspond pas)

essayez d'abord de faire un seul téléchargement de fichier, consultez la documentation:

  1. type de champ de fichier
  2. Comment gérer les téléchargements de fichiers avec Doctrine

Une fois que cela fonctionne, vous devez éditer certaines lignes.

  1. ajouter un attribut multiple de fichier d'entrée.
  2. append [] à la fin de l'attribut de nom de fichier d'entrée (le mien est create ... [] car mon nom de classe de formulaire est create, si votre est createType, ce sera createType ... [])
  3. init $ files sous forme de tableau.

Copiez/collez votre code ici.

12
thierry

OK problème de liaison résolu (erreur de syntaxe enctype): je vais vous donner le code que j'utilise. peut-être que cela aidera ...

J'ai une Galerie Entité

class Gallery
{
    protected $id;
    protected $name;
    protected $description;
    private $urlName;
    public $files; // the array which will contain the array of Uploadedfiles

    // GETTERS & SETTERS ...

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

    public function __construct() {
        $files = array();
    }
}

J'ai une classe form qui génère le formulaire

class Create extends AbstractType {

    public function buildForm(FormBuilder $builder, array $options) {

        $builder->add('name','text',array(
            "label" => "Name",
            "required" => TRUE,
        ));
        $builder->add('description','textarea',array(
            "label" => "Description",
            "required" => FALSE,
        ));
        $builder->add('files','file',array(
            "label" => "Fichiers",
            "required" => FALSE,
            "attr" => array(
                "accept" => "image/*",
                "multiple" => "multiple",
            )
        ));
    }
}

Maintenant dans le contrôleur

class GalleryController extends Controller
{
    public function createAction() {
        $gallery = new Gallery();
        $form = $this->createForm(new Create(), $gallery);
        // Altering the input field name attribute
        $formView = $form->createView();
        $formView->getChild('files')->set('full_name', 'create[files][]');

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

            // print "<pre>".print_r($gallery->getFiles(),1)."</pre>";
            if($form->isValid())
            {
                // Do what you want with your files
                $this->get('gallery_manager')->save($gallery);
                return $this->redirect($this->generateUrl("_gallery_overview"));
            }
        }

        return $this->render("GalleryBundle:Admin:create.html.twig", array("form" => $formView));
    }
}

J'espère que cette aide ...

NB: Si quelqu'un connaît un meilleur moyen de modifier cet attribut f * * name, peut-être dans la classe FormView ou en déclarant un nouveau type de champ, n'hésitez pas à nous montrer votre méthode ...

13
thierry

Toutes les suggestions que j'ai trouvées ici sont des solutions de contournement pour la situation réelle.

Pour pouvoir avoir plusieurs pièces jointes, vous devez utiliser la collection de formulaires.

Citation de la documentation:

Dans cette entrée, vous apprendrez à créer un formulaire qui incorpore une collection de nombreux autres formulaires. Cela pourrait être utile, par exemple, si vous aviez une classe Task et que vous vouliez modifier/créer/supprimer de nombreux objets Tag liés à cette tâche, directement dans le même formulaire.

http://symfony.com/doc/2.0/cookbook/form/form_collections.html

Exemple de cas: vous disposez d'un document dont la forme est spécifiée par DocumentType. Le document doit avoir plusieurs pièces jointes, que vous pouvez avoir en définissant le formulaire AttachmentType et en l'ajoutant en tant que collection au formulaire DocumentType.

7
Nikola Petkanski

Pour sf> 2.2: dans votre classe de type de formulaire, ajoutez cette méthode remplacée:

public function finishView(FormView $view, FormInterface $form, array $options) {
    $view->vars['form']->children['files']->vars['full_name'] .= '[]';
}
4
Prometee

utilisez cette méthode:

$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();

puis vous ajoutez [] au nom de votre entrée via jQuery:

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

code jQuery:

 <script>
  $(document).ready(function() {
    $('#form_attachments').attr('name',$('#form_attachments').attr('name')+"[]");
  });
</script>
3

Vous devez modifier l'attribut de nom de fichier d'entrée qui doit mapper un tableau.

<input type="file" name="name[]" multiple />
2
thierry

Voici un exemple simple pour télécharger plusieurs fichiers. J'ai un problème similaire avec les fichiers de téléchargement.

https://github.com/marekz/example_symfony_multiply_files_example

Pour symfony 3. *

Premièrement: les deux forment une déclinaison:

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


}

Maintenant, mes entités:

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

Finalement, contrôleur Symfony:

/**
 * 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(),
    ));
}

Notez que j'essaie de faire la même chose dans sf2 en utilisant cette syntaxe:

Dans le contrôleur:

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 Sera un tableau de fichiers téléchargés ...

Appeler $form->createView() pour modifier l'attribut de nom n'est certainement pas le meilleur moyen/moyen le plus propre de le faire, mais c'est le seul que j'ai trouvé qui permet à la fonctionnalité csrf de fonctionner, car la modification de l'attribut de nom dans un twig le rend invalide ...

Maintenant, j'ai toujours un problème en utilisant une classe de formulaire qui génère le formulaire, je ne sais pas pourquoi lors de la liaison du formulaire, les données et l'objet attachés au formulaire, mon tableau de fichiers téléchargés est transformé en tableau de (fichier) nom ?? ?

2
thierry

Symfony a introduit l'option 'multiple' pour le type de champ de fichier dans symfony 2.5

$builder->add('file', 'file', array('multiple' => TRUE));
1
Khaled Attia

Les méthodes getChild et set() ont été supprimées dans la version 2.3. Au lieu de cela, vous devriez utiliser children[] et vars propriétés

avant:

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

après:

$formView->children['files']->vars = array_replace($formView->children['files']->vars, array('full_name', 'form[files][]'));
1
ZuyRzuuf

Que se passerait-il s'il y avait des erreurs de validation? Symfony Form republiera-t-il le champ de téléchargement de plusieurs fichiers. Parce que je l'ai essayé et je pense qu'à cette fin, vous devez utiliser une collection de champs de fichiers. Ensuite, la forme symfony doit rendre tous les champs déjà ajoutés correctement.

0
setty