Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Symfony 2 Form collection field with type file

Tags:

php

twig

symfony

I want to upload multiple files with POST request (without Ajax). Can I use Symfony 2's form collection field with type file like this:

Code in Entity:

public $pictures;

public function __construct()
{
    $this->pictures = new \Doctrine\Common\Collections\ArrayCollection();
}

Code in Form Class:

$builder->add('pictures', 'collection', array(
        'type' => 'file',
        'required' => false,
        'attr' => array(
            'multiple' => 'multiple'
        )
));

Code in Twig:

{% for picture in form.pictures %}
    <td>
        {{ form_widget(picture) }}
    </td>
{% endfor %}

I tried, but it doesn't seem to work. It is not showing any errors, but it is not showing the input file either. Any ideas?

like image 824
Sukhrob Avatar asked Sep 13 '12 16:09

Sukhrob


2 Answers

To actually render input types you will need to set the allow_add option in the collection to true and use the form prototype of the collection, javascript and a button to add file fields.

An example based in the documentation (Collection- adding and removing)

The form:

<form action="..." method="POST" {{ form_enctype(form) }}>
{# ... #}

{# store the prototype on the data-prototype attribute #}
<ul id="image-container" class="collection-container" data-prototype="{{ form_widget(form.images.vars.prototype) | e }}">
{% for imageField in form.images%}
    <li>
        {{ form_widget(imageField) }}
    </li>
{% endfor %}
</ul>

<a href="#" class="collection-add" data-collection="image-container">Add image</a>

</form>

The script:

<script type="text/javascript">
  var imageCount;

  jQuery(document).ready(function() {
    $(document).on('click', '.collection-add', function () {
        var $collectionContainer = $('#' + $(this).data('collection'));
        if(!imageCount){imageCount = $collectionContainer.children().length;}
        var prototype = $collectionContainer.attr('data-prototype');
        var item = prototype.replace(/__name__/g, imageCount);
        $collectionContainer.append(item);
        imageCount++;
    });
  })
</script>

This is just an idea, there is still plenty to do depending on your needs. If this wasn't what you were looking for, maybe you could call the add button click as a workaround.

like image 105
Luis Avatar answered Oct 14 '22 14:10

Luis


If you want to show multiple input fields, no, it won't work. A collection type requires you to supply some data before rendering the fields. I've already tried it, and came up creating a separate entity (e.g. File) and and adding relationship to my target entity.

example:

class File
{
    // properties

    public $file; // this holds an UploadedFile object

    // getters, setters
}

FileType:

....

public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder
        ->add('file', 'file', [
            'required' => false
        ])
    ;
}

Product:

class Product
{
    // properties

    private $images; // ManyToMany relationship

    // setters, getters
}

ProductType:

->add('images', 'collection', [
    'type' => 'YOUR_FILE_TYPE_NAME',
    'by_reference' => false,
    'required' => false
])

Product contoller:

public function someAction()
{
    ...

    $image = new File();
    $product->addImage($image);

}

I know this solution can be overkill and creates extra tables, but it works.

like image 38
xurshid29 Avatar answered Oct 14 '22 14:10

xurshid29