I am using symfony 3.1 with regular html forms to make it easier to do front-end styling. I am continuously getting the following two errors. the first is;
The CSRF token is invalid. Please try to resubmit the form.
and the second is
This form should not contain extra fields.
The first is because I don't know how to correctly use the csrf token with plain html forms. My form looks like this:
FormType Class:
class TypeFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name')
->add('isActive', CheckboxType::class)
->add('descriptor');
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => 'AppBundle\Entity\Type',
'csrf_protection' => true,
//'allow_extra_fields' => true
]);
}
public function getName()
{
return 'app_bundle_type_form_type';
}
public function getBlockPrefix()
{
// removes the need for the form[$name] requirement for form inputs
return '';
}
}
html form:
<form method="post">
<fieldset class="form-group">
<label for="name">Name</label>
<input type="text" class="form-control" id="name" name="name">
</fieldset>
<fieldset class="form-group">
<label for="descriptor">Descriptor</label>
<select class="form-control c-select" id="descriptor" name="descriptor">
<option value="Award">Award</option>
<option value="Donation">Donation</option>
<option value="Event">Event</option>
</select>
</fieldset>
<fieldset class="checkbox">
<label class="" for="isActive">
<input type="checkbox" id="isActive" name="isActive" checked> Active</label>
</fieldset>
<input type="hidden" name="_csrf_token" value="{{ csrf_token("app_bundle_type_form_type") }}">
<button type="submit" class="btn btn-primary-outline pull-right">Submit</button>
</form>
Controller:
/**
* #Route("/admin/types/new", name="admin_types_new")
*/
public function newAction(Request $request)
{
$form = $this->createForm(TypeFormType::class);
$form->handleRequest($request);
if($form->isSubmitted() && $form->isValid()) {
dump($form->getData());die;
}
return $this->render('admin/type/new.html.twig');
}
Can anyone tell me what I am doing wrong?
There is better (and working) approach to form rendering in Symfony:
In your controller:
/**
* #Route("/admin/types/new", name="admin_types_new")
*/
public function newAction(Request $request)
{
$form = $this->createForm(TypeFormType::class);
// ...
return $this->render('admin/type/new.html.twig', array('form' => $form->createView());
}
In your template:
{{ form_start(form, {'action': path('admin_types_new'), 'method': 'POST'}) }}
{{ form_widget(form) }}
<input type="submit" />
{{ form_end(form) }}
Please try this because it is usually a better approach: you don't have to care whether your html which you put by hand is correct and all ids and attributes are present or if csrf is generated properly. All is done by Symfony in this case, you just need to modify underlying FormType class and twig template to shape fields of your form.
More on the topic:
http://symfony.com/doc/current/book/forms.html
http://symfony.com/doc/current/reference/forms/twig_reference.html
http://symfony.com/doc/current/cookbook/form/form_customization.html
If anyone is interested in my fix:
<input type="hidden" name="{{ form._token.vars.full_name }}" value="{{ form._token.vars.value }}" />
It seems that you need name your token field as '_token'
Related
In my case, i would like to add for each application OneToMany links of documentation with the same form who contain two inputs, one for the name of the documentation and the second is for the link address documentation, the problem is that it work perfectly only for the first application in the list, but if i try to add a name and link address for the second application on the list or even the third .., when I submit my form, Symfony does not save the information in the database (without error message).
By the way i'm using modals to open the form.
As a beginner and in my first steps with symfony, i tried to read the documentation but i couldn't find a solution to resolve my problem, please find below the related code, thanks!
index.html.twig
{%for applications in applications %}
.....
<div class="tab-pane fade" id="dropdown-kv-21-1{{applications.id}}">
<p>Ajout de liens</p>
{{ form_start(form) }}
<div class="form_group">
<label for="{{form.nom.vars.id}}">Nom</label>
<input type="text" class="form_control" id="{{form.nom.vars.id}}"name={{form.nom.vars.full_name}}" value="{{form.nom.vars.value}}">
{{form_errors(form.nom)}}
{% do form.nom.setRendered %}
</div>
<div class="form_group">
<label for="{{form.lien.vars.id}}">Lien</label>
<input type="text" class="form_control" id="{{form.lien.vars.id}}" name="{{form.lien.vars.full_name}}" value="{{form.lien.vars.value}}">
{{form_errors(form.lien)}}
{% do form.lien.setRendered %}
</div>
<div class="form_group">
<select id="{{form.application.vars.id}}" class="form-control" name="{{form.application.vars.full_name}}" >
<option value="{{applications.id}}">{{applications.id}}</option>
</select>
{{form_errors(form.application)}}
{% do form.application.setRendered %}
</div>
<input type="submit" class="btn btn-success" value="Ajouter" style="transform: translate(27em);"id="carto_cartographiebundle_liendocapp_ajouter"name="carto_cartographiebundle_liendocapp[ajouter]">
{% do form.ajouter.setRendered %}
{{form_end(form)}}
{%endfor%}
AcceuilController
public function indexAction(Request $request)
{
$em = $this->getDoctrine()->getManager();
$applications = $em->getRepository('CartoBundle:Application')->findAll();
if($request->isMethod('POST')){
$nomApp = $request->get('nomApp');
$applications = $em->getRepository('CartoBundle:Application')->findBy(array("nomApp"=>$nomApp));
}
$lienDocApp = new LienDocApp();
$form=$this->createForm(LienDocAppType::class, $lienDocApp);
$form->handleRequest($request);
if ($form->isSubmitted()&& $form->isValid()){
$em = $this->getDoctrine()->getManager();
$em->persist($lienDocApp);
$em->flush();
}
return $this->render('CartoBundle:Accueil:index.html.twig', array(
'applications'=>$applications,
'lienDocApp' => $lienDocApp,
'form' => $form->createView(),
));
}
LienDocAppType
class LienDocAppType extends AbstractType{
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('nom')
->add('lien')
->add('application')
->add('ajouter', SubmitType::class);
}/**
* {#inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Carto\cartographieBundle\Entity\LienDocApp'
));
}
/**
* {#inheritdoc}
*/
public function getBlockPrefix()
{
return 'carto_cartographiebundle_liendocapp';
}
}
i finally found the solution for my problem and i'm posting it here maybe it will be helpful form some one,
actually there is a hidde, input named _token, all i did is changing the Id of this input with the id of the chosen application
<input type="hidden" id="{{ form._token.vars.id }}" name="{{form._token.vars.full_name}}" value="{{ form._token.vars.value }}">
will be :
<input type="hidden" id="{{ applications.id }}" name="{{form._token.vars.full_name}}" value="{{ form._token.vars.value }}">
cheers !
I'm building a get started procedure. It is really very simple: just one field for the email and a submit button.
HOW DOES THE PROCEDURE WORKS
I simply have one controller with two methods: indexAction() and endAction()
The indexAction simply set the route using annotations and displays the twig template with an handmade form:
namespace AppBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use Symfony\Component\HttpFoundation\Request;
class GetStartedController extends Controller
{
/**
* #Route("getstarted")
* #Template()
*/
public function indexAction()
{
return array(
// ...
);
}
/**
* #Route("getstarted/end", name="getStartedEnd")
* #Template()
*/
public function endAction(Request $request)
{
// ... other code
As you can see, currently the method really does nothing as the form is handmade directly in the twig template.
Here is the code to render the form (handmade in the twig template):
{% extends "::base.html.twig" %}
{% block title %}AppBundle:GetStarted:index{% endblock %}
{% block body %}
<form id="getStarted" action="{{ path('getStartedEnd') }}" method="post">
<div class="form-group">
<input type="email" name="userEmail" class="form-control input-lg" placeholder="Enter your e-mail (es.: your.name#example.com">
</div>
<button type="submit" name="submit" class="btn btn-default btn-lg">Submit</button>
</form>
{% endblock %}
I know Symfony 2 can handle the creation of the form and its rendering on the page, but this is a very simple form and I know it never will be more complicate, so an handmade one is the simplest and fastest solution.
Now, what's the problem? The problem is I don't understand how to get the form and its submitted values in my controller, in the other method, endAction().
The form has as action the path to the other method, endAction() where, in my intentions, I will retrieve the submitted values and do some stuff with them.
Following the instructions in paragraph Using a Form without a Class, i came up with the following code for the method endAction() that, in my intentions, will retrieve the submitted email and do something with it - will create a user, obviously - (note the exit to simply print the results of the if)
/**
* #Route("getstarted/end", name="getStartedEnd")
* #Template()
*/
public function endAction(Request $request)
{
$form = $this->createFormBuilder()
->add('userEmail', 'email')
->add('submit', 'submit')
->getForm();
$form->handleRequest($request);
if ($form->isValid()) {
$data = $form->getData();
print_r($data);
} else {
echo 'no data submitted';
}
exit;
return array(
// ...
);
}
The problem is that if ($form->isValid()) { ever returns false and I ever obtain as result the printing of echo 'no data submitted';
Clearly there is something wrong with my implementation of the form handling, but... What?
This is my question: what am I doing wrong? Why the form isn't "intercepted" in the controller?
You have two things to do:
First, edit your Form template and use the correct names (name="form[userEmail]" instead of name="userEmail"):
<form name="form" method="post" action="{{ path('getStartedEnd') }}">
<div id="form">
<div>
<label for="form_userEmail" class="required">User email</label>
<input type="email" id="form_userEmail" name="form[userEmail]" required="required"/>
</div>
<div>
<button type="submit" id="form_submit" name="form[submit]">Submit</button>
</div>
</div>
</form>
And second disable the csrf protection of the form:
/**
* #Route("getstarted/end", name="getStartedEnd")
* #Template()
*/
public function endAction(Request $request)
{
$data = array();
$form = $this->createFormBuilder($data, array(
'csrf_protection' => false,
))
->add('userEmail', 'email')
->add('submit', 'submit')
->getForm();
$form->handleRequest($request);
if ($form->isValid()) {
$data = $form->getData();
print_r($data);
} else {
echo 'no data submitted';
}
exit;
return array(
// ...
);
}
But recommend strongly not to render the form by hand!
It's not more than:
/**
* #Route("getstarted")
* #Template()
*/
public function indexAction()
{
$data = array();
$form = $this->createFormBuilder($data, array(
'action' => $this->generateUrl('getStartedEnd'),
))
->add('userEmail', 'email')
->add('submit', 'submit')
->getForm();
return array(
'form' => $form->createView(),
);
}
and in your template:
{# just this line #}
{{ form(form) }}
I'm trying to do a simple form. The bind between the controller and the view is done. But I can't receive the form data when the user submit.
public function addAction()
{
$router = $this->get('router');
$request = $this->get('request');
$ret = 'not set';
$title = 'not set';
if ($request->getMethod() == 'POST') {
$pictures = $request->files->get('pictures');
$title = $request->request->get('title');
$ret = $this->get('my_project_blog.post_service')
->create($title, $subtitle, $description, $pictures);
}
return $this->render('MyProjectBlogBundle:Default:add.html.twig', array('err' => $ret, 'title' => $title));
}
Now the add.twig.html
<form enctype="multipart/form-data" action="{{ path('my_project_blog_add') }}" method="POST" id="contactform">
<fieldset id="contact_form">
<label for="title">
<input type="text" name="title" id="name" placeholder="Enter A Title">
</label>
<label for="file">
<input name="pictures[]" type='file' multiple='multiple' ></input>
</label>
<input type="submit" class="submit btn btn-default btn-black" id="submit" value="Submit">
</fieldset>
</form>
The result before submit :
Pictures: not set.
Title: not set
After submit :
Pictures: Error pictures count == 0.
Title:
Is there any particular reason, why you are not using Symfony form component?
Not sure how it's possible, but maybe you got wrong request service from container. You should use Request Stack service or add $request as parameter of you action. Just tested following code and everything works correctly.
public function addAction(Request $request)
{
if ($request->getMethod() == 'POST') {
$title = $request->request->get('title');
$files = $request->files->all();
}
}
As xurshid29 mentioned in comment, symfony gives you the opportunity to define forms easier and handle response easier.
// In controller
public function addAction(Request $request)
{
$form = $this->createFormBuilder()
->add('title')
->add('pictures', 'file', [
'multiple' => true, // Since symfony 2.5
])
->add('Submit', 'submit')
->getForm();
$form->handleRequest($request);
if ($form->isValid()) {
// Process data from $form->getData()
}
return $this->render('MyProjectBlogBundle:Default:add.html.twig', [
'form' => $form->createView()
]);
}
// In view
{% block content %}
{{ form(form) }}
{% endblock %}
I have some troubles with my Symfony Form.
In my first controller, I render a form like this:
$annonce = new Annonce();
$formBuilder = $this->createFormBuilder($annonce);
$formBuilder
->add('title', 'text')
->add('valider', 'submit');
$form = $formBuilder->getForm();
$request = $this->get('request');
if ($request->getMethod() == 'POST') {
$form->bind($request);
$response = $this->forward('ProjectMainBundle:Commande:new', array(
'annonce' => $form["title"]
));
return $response;
}
And I print the form in the view like this:
<form method="post" {{ form_enctype(form) }}>
{{ form_row(form.title, {'attr': {'value': entity.mytext }}) }}
<input type="submit" class="btn btn-primary" />
</form>
That's give:
<form method="post">
<div>
<label class="required" for="form_title">Title</label>
<input id="form_title" type="text" value="11" required="required" name="form[title]">
</div>
<input class="btn btn-primary" type="submit">
</form>
So when I submit it, I got the warning
"An exception has been thrown during the rendering of a template ("Catchable Fatal Error: Object of class Symfony\Component\Form\Form could not be converted to string"
I simply want to redirect to the controller "newAction" (that's work), but with the value of the input of my form !
<input id="form_title" type="text" value="11" required="required" name="form[title]">
It seems to I return the form object with
$response = $this->forward('ProjectMainBundle:Commande:new', array(
'annonce' => $form["title"]
));
But how return just the value of the input of the form ?
Big thanks for help
Try calling $annonce->getTitle() instead of using $form["title"].
After calling "$form->bind($request)" your $annonce object should have the title entered in the form.
I'm using Symfony2 forms with silex..
I create my form like this:
$form = $app->form()
->add('foo', new FooType())
// ->add(...)
->add('save', 'submit')
->getForm();
FooType:
<?php
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Symfony\Component\Validator\Constraints as Assert;
class FooType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('bar', 'text', ['constraints' => new Assert\NotBlank()]);
$builder->add('baz', 'text', ['required' => false]);
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults([
'error_bubbling' => false,
]);
}
public function getName()
{
return 'foo';
}
}
If the "bar" field is empty the error always is mapped to the "foo" base field. Why? And how can I map the error to the field itself ("bar")?
Edit:
The form is rendered like this (Twig):
{{ form(form) }}
Result after submit with empty fields:
<form name="form" method="post" action="">
<div>
<label class="required">Foo</label>
<ul>
<li>This value should not be blank.</li>
</ul>
<div id="form_foo">
<div>
<label for="form_foo_bar" class="required">Bar</label>
<input type="text" id="form_foo_bar" name="form[foo][bar]" required="required">
</div>
<div>
<label for="form_foo_baz">Baz</label>
<input type="text" id="form_foo_baz" name="form[foo][baz]">
</div>
</div>
</div>
<div>
<button type="submit" id="form_save" name="form[save]">Save</button>
</div>
<input type="hidden" id="form__token" name="form[_token]" value="faebf2a0b0a05ddb8e5f40d58c7a3bf3d49522d4">
</form>