可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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' }}) }}
回答1:
No extra classes needed (except the gallery_manger service but the issue you describe happens before...)
I don't really know what's wrong. Check for your template (maybe wrong enctype... or name attr missmatching)
first try to do a single file upload, check the documentation:
- file Field Type
- How to handle File Uploads with Doctrine
Once it works, you have to edit some lines.
- add input file multiple attribute.
- append [] at the end of the input file name attribute (mine is
create...[] because my form class name is create, if your is
createType it will be createType...[])
- init $files as an array.
Copy/paste your code here.
回答2:
Ok binding issue solved (enctype syntax error) : i'll give you the code i use. maybe it will help...
I have a Gallery Entity
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();
}
}
I have a form class that generate the form
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",
)
));
}
}
Now in the controller
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));
}
}
Hope this help...
NB: If someone know a better way to alter this f** name attribute, maybe in the FormView class or by declaring a new field type, feel free to show us your method...
回答3:
All the suggestions I've found here are workarounds for the real situation.
In order to be able to have multiple attachments, you should use form collection.
Quote from the documentation:
In this entry, you'll learn how to create a form that embeds a collection of many other forms. This could be useful, for example, if you had a Task class and you wanted to edit/create/remove many Tag objects related to that Task, right inside the same form.
http://symfony.com/doc/2.0/cookbook/form/form_collections.html
Example case: You have a document, which form is specified by DocumentType. The document must have multiple attachments, which you can have by defining AttachmentType form and adding it as a collection to the DocumentType form.
回答4:
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:
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'] .= '[]';
}
回答6:
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 ???
回答7:
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(),
));
}
回答8:
You need to alter the input file name attribute which need to map an array.
<input type="file" name="name[]" multiple />
回答9:
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][]'));
回答10:
Symfony introduced 'multiple' option to file field type in symfony 2.5
$builder->add('file', 'file', array('multiple' => TRUE));
回答11:
What would happen if there would be some validation errors? Will Symfony Form repost multiple file upload field. Because I tried it and I think for this purpose you need to use collection of file fields. Than symfony form must render all fields have added before correctly.