Загрузка нескольких файлов с помощью Symfony2
Я пытаюсь загрузить несколько файлов через форму, но я могу загружать только один файл за раз, последний, который я отмечаю в браузере. Есть ли способ загрузить больше изображений с помощью Symfony2 с помощью простой формы?
Вот твиг-шаблон формы, которую я использую, чтобы отметить несколько файлов:
{{ form_widget(form.post_image, { 'attr': {'multiple': 'multiple' }}) }}
Ответы
Ответ 1
Никаких дополнительных классов не требуется (кроме службы gallery_manger, но проблема, которую вы описываете, происходит до...)
Я действительно не знаю, что случилось. Проверьте свой шаблон (возможно, неправильно enctype... или имя attr missmatching)
сначала попробуйте сделать одну загрузку файла, проверьте документацию:
Как только он работает, вам нужно отредактировать некоторые строки.
- добавить несколько атрибутов входного файла.
- добавить [] в конце атрибута имени входного файла (мой
create... [], потому что мое имя класса формы создается, если ваш
createType будет createType... [])
- init $files в качестве массива.
Скопируйте/вставьте здесь свой код.
Ответ 2
Проблема с привязкой к окну решена (синтаксическая ошибка enctype): я дам вам код, который я использую. возможно, это поможет...
У меня есть Галерея Сущность
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();
}
}
У меня есть класс 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",
)
));
}
}
Теперь в контроллере
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));
}
}
Надеюсь, что эта помощь...
NB: Если кто-то знает лучший способ изменить этот атрибут имени f * *, возможно, в классе FormView или объявив новый тип поля, не стесняйтесь показать нам свой метод...
Ответ 3
Все предложения, которые я нашел здесь, обходные пути для реальной ситуации.
Чтобы иметь возможность иметь несколько вложений, вы должны использовать коллекцию форм.
Цитата из документации:
В этой записи вы узнаете, как создать форму, в которую вставляется коллекция многих других форм. Это может быть полезно, например, если у вас был класс Task, и вы хотели отредактировать/создать/удалить многие объекты Tag, связанные с этой задачей, прямо внутри одной и той же формы.
http://symfony.com/doc/2.0/cookbook/form/form_collections.html
Пример: у вас есть документ, форма которого указана в DocumentType. Документ должен иметь несколько вложений, которые вы можете получить, указав форму AttachmentType и добавив ее в виде коллекции в форму DocumentType.
Ответ 4
используйте этот метод:
$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();
то вы добавляете [] к имени вашего ввода через jQuery:
<input id="form_attachments" name="form[attachments]" required="required" multiple="multiple" type="file">
Код jQuery:
<script>
$(document).ready(function() {
$('#form_attachments').attr('name',$('#form_attachments').attr('name')+"[]");
});
</script>
Ответ 5
Для sf > 2.2:
В форме типа type добавьте этот метод с переопределением:
public function finishView(FormView $view, FormInterface $form, array $options) {
$view->vars['form']->children['files']->vars['full_name'] .= '[]';
}
Ответ 6
Обратите внимание, что я пытаюсь сделать то же самое в sf2, используя этот синтаксис:
В контроллере:
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
будет массивом загруженных файлов...
Вызов $form->createView()
, чтобы изменить атрибут name, конечно, не самый лучший способ/самый чистый способ сделать это, но он единственный, который я нашел, который поддерживает работу csrf, потому что изменение атрибута имени в шаблоне веточки делает его недействительным...
Теперь у меня все еще есть проблема с использованием класса формы, который генерирует форму, я не знаю, почему при связывании данных формы и объекта, прикрепленных к форме, мой массив загруженных файлов преобразуется в массив (файл) имя???
Ответ 7
Вот простой пример для загрузки нескольких файлов. У меня есть аналогичная проблема с загрузкой файлов.
https://github.com/marekz/example_symfony_multiply_files_example
Для symfony 3. *
Во-первых: обе формы 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';
}
}
Теперь мои объекты:
<?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;
}
}
Окончательный, 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
Вам нужно изменить атрибут имени входного файла, который должен отображать массив.
<input type="file" name="name[]" multiple />
Ответ 9
Методы getChild
и set()
были удалены в 2.3.
Вместо этого вы должны использовать свойства children[]
и vars
перед:
$formView->getChild('files')->set('full_name', 'form[files][]');
после
$formView->children['files']->vars = array_replace($formView->children['files']->vars, array('full_name', 'form[files][]'));
Ответ 10
Symfony представила параметр 'multiple' для типа поля файла в symfony 2.5
$builder->add('file', 'file', array('multiple' => TRUE));
Ответ 11
Что произойдет, если будут ошибки проверки? Будет ли Symfony Form повторно загружать поле для загрузки нескольких файлов. Потому что я попробовал это, и я думаю, для этого вам нужно использовать коллекцию полей файлов. Чем должна быть форма symfony, все поля должны быть добавлены правильно.