Search code examples
symfonytwighidden-field

multiple not mapped hidden input symfony2


I have an article entity. When I create an article, I want to be able to add zero to n images to it. To do so, I upload files with a jQuery plugin in a temporary directory, and add an <input type=hidden> to my form, with the file's full path as value. It works fine when there is no validation error. But when there is, I got an error saying:

An exception has been thrown during the rendering of a template ("Notice: Array to string conversion") in form_div_layout.html.twig at line 13

And I'm pretty sure it's because it tries to do this :

ErrorHandler ->handleError ('8', 'Array to string conversion', '/var/www/project/app/cache/dev/twig/47/a4/ac9a00176739f843e919f5f89883191930038a6e0aaa218869e4966ab7c6.php', '175', array('context' => array('value' => array('njm4uqsa9y6@54edfdaedd0736w3wstk.jpeg'),...

Here is my formType :

public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder
        ->add('title')
        ->add('content')
        ->add('maxVote')
        ->add('expireAt', 'datetime', array(
                'widget' => 'single_text',
                'input' => 'string',
                'format' => 'dd-mm-yyyy',
            )
        )
        ->add('category', 'entity', array(
                'class' => 'AppBundle:StoryCategory',
                'required' => true
            )
        )
        ->add('answers', 'collection', array(
                'type' => new AnswerType()
            )
        )
        ->add('visibility', 'choice', array(
                'choices'   => array(1 => "label.public", 2 => "label.semiprivate", 3 => "label.private"),
                'expanded' => false,
                'multiple' => false
            )
        )
        ->add('attachment', 'hidden', array(
                'mapped' => false,
                'required' => false
            )
        )
        ->add('save', 'submit')
    ;
}

Do you have any idea what is happening, or what should I do ?

Edit:

The form is empty. I create a new instance of Story

$story = new Story();
$form = $this->createForm(new StoryType(), $story);

And when I submit the form I do

$form->handleRequest($request);

Edit 2: Story.php

<?php

namespace AppBundle\Entity;

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

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

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

    /**
     * @var string
     *
     * @ORM\Column(name="content", type="text")
     * @Assert\NotBlank()
     */
    private $content;

    /**
     * @var string
     *
     * @ORM\Column(name="token", type="string", length=70, unique=true)
     */
    private $token;

    /**
     * @var \DateTime
     *
     * @ORM\Column(name="created_at", type="datetime")
     */
    private $createdAt;

    /**
     * @var \DateTime
     *
     * @ORM\Column(name="updated_at", type="datetime")
     */
    private $updatedAt;

    /**
     * @var \DateTime
     *
     * @ORM\Column(name="expire_at", type="datetime")
     * @Assert\NotBlank()
     */
    private $expireAt;

    /**
     * @var string
     *
     * @ORM\Column(name="author_ip", type="string", length=50, nullable=true)
     */
    private $authorIp;

    /**
     * @var integer
     *
     * @ORM\Column(name="max_vote", type="integer", nullable=true)
     * @Assert\Type(type="integer")
     * @Assert\Range(
     *      min = 0,
     *      max = 20000,
     *      minMessage = "validation.story.maxvote.min",
     *      maxMessage = "validation.story.maxvote.max"
     * )
     */
    private $maxVote;

    /**
     * @var integer
     *
     * @ORM\Column(name="nb_vote", type="integer")
     */
    private $nbVote;

    /**
     * @var integer
     * 1 = public, 2 = semi-private, 3 = private (only invited members)
     *
     * @ORM\Column(name="visibility", type="integer")
     *
     */
    private $visibility;

    /**
     * @var boolean
     *
     * @ORM\Column(name="is_active", type="boolean")
     */
    private $isActive;

    /**
    * @var StoryCategory
    *
    * @ORM\ManyToOne(targetEntity="StoryCategory", inversedBy="stories")
    * @ORM\JoinColumns({
    *   @ORM\JoinColumn(name="category_id", referencedColumnName="id")
    * })
    * @Assert\NotNull()
    */
    private $category;

    /**
    * @ORM\OneToMany(targetEntity="Answer", mappedBy="story", cascade={"persist", "remove"})
    * @Assert\Count(
    *      min = "2",
    *      max = "4",
    *      minMessage = "validation.story.answer.min",
    *      maxMessage = "validation.story.answer.max",
    *      groups={"premium"}
    * )
    * @Assert\Count(
    *      min = "2",
    *      max = "2",
    *      minMessage = "validation.story.answer.min",
    *      maxMessage = "validation.story.answer.max",
    * )
    */
    private $answers;

    /**
    * @ORM\OneToMany(targetEntity="Attachment", mappedBy="story", cascade={"persist", "remove"})
    */
    private $attachments;

    /**
    * @var User
    *
    * @ORM\ManyToOne(targetEntity="User", inversedBy="stories")
    * @ORM\JoinColumns({
    *   @ORM\JoinColumn(name="user_id", referencedColumnName="id")
    * })
    */
    private $author;


    public function __construct()
    {
        $datetime = new \DateTime();
        $this->createdAt = $datetime;
        $this->updatedAt = $datetime;
        $this->token     = base_convert(time(), 10, 36).\AppBundle\Library\StringHelper::randomString();
        $this->isActive  = false;
        $this->nbVote    = 0;
    }
}

Twig view :

{% extends '::base.html.twig' %}

{% block basecontent %}
    {{ form_errors(form) }}
    {{ form_start(form, {attr: {id: 'story-form', novalidate: 'novalidate'}}) }}
    {{ form_row(form.title) }}
    {{ form_row(form.content) }}
    {{ form_row(form.category) }}
    <h3>Les choix de réponses possibles</h3>
    {% for answer in form.answers %}
        {{ form_widget(answer) }}
    {% endfor %}
    {{ form_row(form.visibility) }}
    {{ form_row(form.expireAt) }}
    {% for attachement in form.attachment %}
        {{ form_row(attachment) }}
    {% endfor %}
    {{ form_end(form) }}

    {# upload zone #}
    <div class="upload-block">
        <form action='{{ path('story_file_upload') }}' id="dropzone" class="dropzone">
        </form>
    </div>

{% endblock basecontent %}

This is what I add with javascript :

$("#story-form").append('<input type="hidden" name="story_form[attachment][]" value="'+response.filename+'"/>');

So I tried without it being an array (I removed the ending [] in the name), and it works. So I assume the problem comes from that, I have to tell the formType that it's an array. But how ?

Thank you


Solution

  • I couldn't make it work, so I removed the "attachments" field from the formType. When I upload a file, I then add using javascript an <input type="hidden"> with a name that does not match the fields from the formType. For example, the fields generated by Symfony are named story_form['name']. I just named mine attachments[]. That way Symfony doesn't tell me that there is an extra field. In my controller I get the values with $request->request->get('attachments'). There probably is a better way to do it, but I haven't found it.