Question

I've been trying to submit a form which adds a Question object into the db.

But everytime I do, the error "The CSRF token is invalid. Please try to resubmit the form" shows up.

On my form's content field, I've attached this plugin which is an editor same as Stack Overflow's.

In my form's tag field, I've attached this one for tag autocompletion.

Here's my controller code:

/**
 * Creates a new Question entity.
 *
 * @Route("/ask", name="question_create")
 * @Method("POST")
 * @Template("VerySoftAskMeBundle:Question:ask.html.twig")
 */
public function createAction(Request $request) {
    $entity = new Question();
    $form = $this->createCreateForm($entity);
    $tags = $this->getDoctrine()->getRepository('VerySoftAskMeBundle:Tag')->findAll();
    date_default_timezone_set('Asia/Manila');
    $entity->setDateOfPost(new \DateTime());

    $entity->setOwner($this->getUser());

        $form->handleRequest($request);

        if ($form->isValid()) {
            $em = $this->getDoctrine()->getManager();
            $em->persist($entity);
            $em->flush();

            return $this->redirect($this->generateUrl('question_show', array('id' => $entity->getId())));
        }


    return array(
        'entity' => $entity,
        'form' => $form->createView(),
        'tags' => $tags
    );
}

/**
 * Creates a form to create a Question entity.
 *
 * @param Question $entity The entity
 *
 * @return Form The form
 */
private function createCreateForm(Question $entity) {
    $form = $this->createForm(new QuestionType(), $entity, array(
        'action' => $this->generateUrl('question_create'),
        'method' => 'POST',
        'em' => $this->getDoctrine()->getEntityManager()
    ));

    $form->add('submit', 'submit', array('label' => 'Ask'));

    return $form;
}

/**
     * 
     * @Route("/ask", name="ask")
     * @Security( "has_role( 'ROLE_USER' )" )
     * @Method("GET")
     * @Template
     */
    public function askAction() {

        $tags = $this->getDoctrine()->getRepository('VerySoftAskMeBundle:Tag')->findAll();
        $entity = new Question();
        $form = $this->createCreateForm($entity);

        return array(
            'entity' => $entity,
            'form' => $form->createView(),
            'tags' => $tags
        );
    }

I've made a Data Transformer for my tag field which turns the input tags into tag objects.

class TagTransFormer implements DataTransformerInterface {

/**
 * @var ObjectManager
 */
private $om;

/**
 * @param ObjectManager $om
 */
public function __construct(ObjectManager $om) {
    $this->om = $om;
}

/**
 * Transforms an object (issue) to a string (number).
 *
 * @return ArrayCollection
 */
public function transform($tags) {

    return $tags;
}

/**
 * Transforms a string (number) to an object (issue).
 *
 * @param  string $number
 *
 * @return ArrayCollection
 *
 * @throws TransformationFailedException if object (issue) is not found.
 */
public function reverseTransform($ids) {

    $tags = array();

    if (!$ids) {
        return null;
    }

    $repo = $this->om
            ->getRepository('VerySoftAskMeBundle:Tag');

    $idsArray = explode(",", $ids);
    foreach ($idsArray as $id) {
        $tags[] = $repo->findOneByName($id);
    }
    return $tags;
}
}

Here's my form class:

class QuestionType extends AbstractType {

/**
 * @param FormBuilderInterface $builder
 * @param array $options
 */
public function buildForm(FormBuilderInterface $builder, array $options) {

    $entityManager = $options['em'];
    $transformer = new TagTransFormer($entityManager);

    $builder
            ->add('title', 'text')
            ->add('content', 'textarea')
            ->add($builder->create('tags', 'text')
                    ->addModelTransformer($transformer)
    );
}

/**
 * @param OptionsResolverInterface $resolver
 */
public function setDefaultOptions(OptionsResolverInterface $resolver) {
    $resolver->setDefaults(array(
                'data_class' => 'VerySoft\AskMeBundle\Entity\Question'
            ))
            ->setRequired(array(
                'em',
            ))
            ->setAllowedTypes(array(
                'em' => 'Doctrine\Common\Persistence\ObjectManager',
    ));
}

/**
 * @return string
 */
public function getName() {
    return 'verysoft_askmebundle_question';
}
}

My Twig Template:

<div id="askDiv" style="padding-bottom: 90px;">
        {{ form_start(form, { 'attr' : { 'novalidate' : 'novalidate', 'class' : 'col-md-offset-3 form-control-static col-md-7' } }) }}
<div class="col-lg-12" style="padding: 0px; margin-bottom: 30px;">
    <span class="askLabels col-lg-1 text-left">{{ form_label(form.title) }}</span>
            {{form_widget(form.title, { 'attr' : { 'class' : 'form-control col-lg-11' } })}}
</div>
        {{ form_widget(form.content, { 'attr' : { 'class' : 'col-lg-12' } }) }}
<div class="col-lg-12" style="padding: 0px; margin-top: 20px;">
    <label class="col-lg-1 text-left askLabels" for="tagField">Tags</label>

    <div class="col-lg-8">
        {{ form_widget(form.tags) }}
    </div>
    {% if app.user.reputation >= 100 %}
    <a id="addTag" title="Add New Tag" data-toggle="tooltip modal" data-placement="left" class="col-lg-3" href="#"><i class="fa fa-plus-circle"></i></a>
    <div id="mymodal" class="modal fade bs-example-modal-lg" tabindex="-1" role="dialog" aria-labelledby="myLargeModalLabel" aria-hidden="true">
        <div class="modal-dialog">
            <div class="modal-content">
                <div class="modal-header">
                    <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
                    <h4 class="modal-title" id="myModalLabel">Add New Tag</h4>
                </div>
                <div class="modal-body">
                    <label for="tagName">Tag Name: </label>
                    <input id="tagName" class="form-control" type="text"/>
                </div>
                <div class="modal-footer">
                    <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
                    <button type="button" class="btn btn-primary">Add Tag</button>
                </div>
            </div>
        </div>
    </div>

    {% endif %}


</div>
<div style="margin-top: 20px; ">
        {{ form_widget(form.submit, { 'attr' : { 'class' : 'col-md-offset-4 col-md-4  btn btn-primary' } }) }}
</div>

<p>
    title error{{ form_errors(form.title) }}
</p>
<p>
    content error{{ form_errors(form.content) }}
</p>
<p>
    tag error{{ form_errors(form.tags) }}
</p>
<p>
    form error{{ form_errors(form) }}
</p>

Scripts:

$(document).ready(function(){
$("textarea").pagedownBootstrap();

    var zeTags = ["{{ tags|join('", "')|raw }}"];
    $('#verysoft_askmebundle_question_tags').tagit({
        availableTags: zeTags,
        tagLimit: 5,
        beforeTagAdded: function(event, ui) {
            if ($.inArray(ui.tagLabel, zeTags) == -1)
                return false;
        }
    });
});
Was it helpful?

Solution

You missed

{{ form_rest(form) }}

Symfony2 has a mechanism that helps to prevent cross-site scripting: they generate a CSRF token that have to be used for form validation. Here, in your example, you're not displaying (so not submitting) it with form_rest(form). Basically form_rest(form) will "render" every field that you didn't render before but that is contained into the form object that you've passed to your view. CSRF token is one of those values.

OTHER TIPS

For newer versions of Symonfy, e.g. 2.4+ you would use the newer form_end(form), which automatically renders all fields not rendered as well as the CSRF token.

Per the documentation:

form_end() - Renders the end tag of the form and any fields that have not yet been rendered. This is useful for rendering hidden fields and taking advantage of the automatic CSRF Protection.
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top