Symfony2 collection always empty - forms

I have an issue that I haven't been able to solve. I have 2 entities:
<?php
namespace ...\Entity;
// ...
/**
* Pregunta
*
* #ORM\Table(name="pregunta", indexes={#ORM\Index(name="fk_respuesta_tipo_respuesta1_idx", columns={"tipo_respuesta_id"}))})
* #ORM\Entity
*/
class Pregunta {
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #var \Doctrine\Common\Collections\Collection
*
* #ORM\ManyToMany(targetEntity="...\Entity\Respuesta", mappedBy="pregunta")
*/
private $respuesta;
public function __construct() {
$this->tipoPrueba = new \Doctrine\Common\Collections\ArrayCollection();
$this->respuesta = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Add respuesta
*
* #param ...\Entity\Respuesta $respuesta
* #return Pregunta
*/
public function addRespuesta(...\Entity\Respuesta $respuesta) {
$this->respuesta[] = $respuesta;
return $this;
}
/**
* Remove respuesta
*
* #param ...\Entity\Respuesta $respuesta
*/
public function removeRespuesta(...\Entity\Respuesta $respuesta) {
$this->respuesta->removeElement($respuesta);
}
/**
* Get respuesta
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getRespuesta() {
return $this->respuesta;
}
function setRespuesta(\Doctrine\Common\Collections\Collection $respuesta) {
$this->respuesta = $respuesta;
}
}
Then I have the Respuesta entity:
<?php
class Respuesta {
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="texto_respuesta", type="text", nullable=false)
*/
private $textoRespuesta;
// ...
I have a PreguntaType which has its fields and a collection of RespuestaType:
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder
->add('titulo', 'textarea', array("label" => "Enunciado: ", "required" => true, "attr" => array('class' => 'form-control')))
->add('numeroPagina', 'integer', array("label" => "Página: ", "required" => true, "attr" => array('class' => 'form-control')))
->add('areaConocimiento', 'entity', array('class' => 'UciBaseDatosBundle:AreaConocimiento', 'required' => false, 'attr' => array('style' => 'width: 100%')))
->add('trianguloTalento', 'entity', array('class' => 'UciBaseDatosBundle:TrianguloTalento', 'required' => false, 'attr' => array('style' => 'width: 100%')))
->add('capitulo', 'entity', array('class' => 'UciBaseDatosBundle:Capitulo', 'required' => false, 'attr' => array('style' => 'width: 100%')))
->add('grupoProcesos', 'entity', array('class' => 'UciBaseDatosBundle:GrupoProcesos', 'required' => false, 'attr' => array('style' => 'width: 100%')))
->add('tipoPrueba', 'entity', array('class' => 'UciBaseDatosBundle:TipoPrueba', 'expanded' => true, 'multiple' => true, 'required' => false, 'attr' => array('style' => 'width: 100%')))
->add('libro', 'entity', array('class' => 'UciBaseDatosBundle:Libro', 'required' => false, 'attr' => array('style' => 'width: 100%')))
->add('respuesta', 'collection', array(
'type' => new RespuestaType(),
'prototype' => true,
'allow_add' => true,
'by_reference' => false,
'allow_delete' => true,
'label' => ' '
));
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver) {
$resolver->setDefaults(array(
'data_class' => 'Uci\Bundle\BaseDatosBundle\Entity\Pregunta'
));
}
However, when I debug my form submission, if i set my collection to 'required' => true, it throws this error An invalid form control with name='...[respuesta][Respuesta0][RespuestaField]' is not focusable. On the other hand, If I set it to 'required' => false, my collections' fields are always empty.
This is my TWIG file:
<form action="{{ path('uci_administrador_registrarPregunta', { 'idTipoRespuesta': tipoRespuesta.id }) }}" name="formulario" method="POST" enctype="multipart/form-data">
<h3 class="thin text-center">Registrar una nueva pregunta {{ tipoRespuesta.nombre }}</h3>
<p class="text-center text-muted">{{ tipoRespuesta.explicacion }}</p>
<hr>
{% if error %}
<div style="color:red">{{ error }}</div>
{% endif %}
<div class="row top-margin">
<div class="cols-xs-12 col-sm-10 col-md-8 col-lg-8">
{{ form_row(form.titulo) }}
</div>
</div>
<div class="row top-margin">
<div class="cols-xs-12 col-sm-10 col-md-8 col-lg-8">
{{ form_row(form.numeroPagina) }}
</div>
</div>
<div class="row top-margin">
<div class="cols-xs-12 col-sm-10 col-md-8 col-lg-8">
{{ form_row(form.areaConocimiento) }}
</div>
</div>
<div class="row top-margin">
<div class="cols-xs-12 col-sm-10 col-md-8 col-lg-8">
{{ form_row(form.capitulo) }}
</div>
</div>
<div class="row top-margin">
<div class="cols-xs-12 col-sm-10 col-md-8 col-lg-8">
{{ form_row(form.grupoProcesos) }}
</div>
</div>
<div class="row top-margin">
<div class="cols-xs-12 col-sm-10 col-md-8 col-lg-8">
{{ form_row(form.trianguloTalento) }}
</div>
</div>
<div class="row top-margin">
<div class="cols-xs-12 col-sm-10 col-md-8 col-lg-8">
{{ form_row(form.tipoPrueba) }}
</div>
</div>
<div class="row top-margin">
<div class="cols-xs-12 col-sm-10 col-md-8 col-lg-8">
{{ form_row(form.libro) }}
</div>
</div>
<br>
<hr>
<h3>Respuestas</h3><br>
<div class="respuestas" data-prototype="{{ form_widget(form.respuesta.vars.prototype)|e }}">
{# iterate over each existing tag and render its only field: name #}
{% for respuesta in form.respuesta %}
<div class="row top-margin">
<div class="cols-xs-12 col-sm-10 col-md-8 col-lg-8">
{{ form_row(respuesta.correcta) }}
</div>
</div>
{% endfor %}
</div>
<br><br>
<div class="row">
<div class="col-lg-8">
</div>
<div class="col-lg-4 text-right">
<button class="btn btn-action" type="submit">Registrar</button>
</div>
</div>
{{ form_rest(form) }}
</form>
I use some javascript to add my collection forms:
var $collectionHolder;
// setup an "add a tag" link
var $addTagLink = $('Añadir respuesta');
var $newLinkLi = $('<div></div>').append($addTagLink);
function addTagForm($collectionHolder, $newLinkLi) {
// Get the data-prototype explained earlier
var prototype = $collectionHolder.data('prototype');
// get the new index
var index = $collectionHolder.data('index');
// Replace '__name__' in the prototype's HTML to
// instead be a number based on how many items we have
var newForm = prototype.replace(/__name__/g, 'Respuesta' + index);
// increase the index with one for the next item
$collectionHolder.data('index', $collectionHolder.find(':input').length);
// Display the form in the page in an li, before the "Add a tag" link li
var $newFormLi = $('<div style="background-color:#F6F6F6; border-radius:10px;padding: 25px;border: 5px solid #003c70;margin: 5px;"></div><br>').append(newForm);
$newLinkLi.before($newFormLi);
}
document.ready = function () {
// Get the ul that holds the collection of tags
$collectionHolder = $('div.respuestas');
// add the "add a tag" anchor and li to the tags ul
$collectionHolder.append($newLinkLi);
// count the current form inputs we have (e.g. 2), use that as the new
// index when inserting a new item (e.g. 2)
$collectionHolder.data('index', $collectionHolder.find(':input').length);
$addTagLink.on('click', function (e) {
// prevent the link from creating a "#" on the URL
e.preventDefault();
// add a new tag form (see next code block)
addTagForm($collectionHolder, $newLinkLi);
});
// ...
}
I would really appreciate any help.
Thanks.

My problem was in my javascript. I don´t know why my previous code didn't work but I changed and it worked. My javascript is the following:
var collectionHolder = $('#respuestas');
var prototype = collectionHolder.attr('data-prototype');
var form = prototype.replace(/__name__/g, collectionHolder.children().length); //importante
var removeFormA = $('Borrar');
var newLi = $('<li></li>');
newLi.append(form);
newLi.append(removeFormA);
collectionHolder.append(newLi);
And this is part of my TWIG file:
<ul id="respuestas" data-prototype="{{ form_widget(form.respuesta.vars.prototype)|e }}">
{% for respuesta in form.respuesta %}
<li> {{ form_row(respuesta) }}</li>
{% endfor %}
</ul>
I hope it helps someone else.
Regards.

Are you trying to hide some fields in your form ? It looks like some fields are required but not actually displayed in the page, which prevents the browser from validating the form. Refer to that answer : https://stackoverflow.com/a/28340579/4114297
I wonder why you're only rendering respuesta.correcta in the loop. You might want to render the while respuesta item :
<div class="respuestas" data-prototype="{{ form_widget(form.respuesta.vars.prototype)|e }}">
{# iterate over each existing tag and render its only field: name #}
{% for respuesta in form.respuesta %}
<div class="row top-margin">
<div class="cols-xs-12 col-sm-10 col-md-8 col-lg-8">
{{ form_row(respuesta) }} {# <- Here #}
</div>
</div>
{% endfor %}
</div>
If you need some fields to be hidden and/or pre-filled, you could do that RespuestaType

Related

Symfony Render a Collection Form Type Prototype

I try to render the collection type prototype I create with an embed collection form.
I read the documentation on Symonfy, Github, ... but don't succeed to understand the way to use the block to render the form.
Here I have a form (RegistrationFormType) based on the User entity which has an embed collection form of Adress (AdressFormType).
I succeed to add the button and generate new adress on the form but I would like to have my prototype with the same layout as the one of my first adress (retrieved from DB).
Could you help me to understand what I do wrong please?
Here my RegistrationFormType
<?php
namespace App\Form;
use App\Entity\User;
//use Doctrine\DBAL\Types\TextType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\TelType;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints\IsTrue;
use Symfony\Component\Validator\Constraints\Length;
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Form\CallbackTransformer;
class RegistrationFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('roles',ChoiceType::class,[
'choices'=>[
'Specialist'=>'Role_Specialist',
'Parent'=>'Role_Parent',
'Center'=>'Role_Center',],
'label'=>"Je m'inscris en tant que"])
->add('name')
->add('firstname',TextType::class, [
'label' => 'Firstname',
'row_attr' => [
'id' => 'firstname'
],])
->add('email')
->add('phone',TelType::class)
->add('NISS',TextType::class, [
'label' => 'NISS',
'row_attr' => [
'id' => 'NISS'
]
,
'required'=>'false',
])
->add('BCE',TextType::class, [
'label' => 'BCE',
'row_attr' => [
'id' => 'BCE'
],
'required'=>'false'])
->add('agreeTerms', CheckboxType::class, [
'mapped' => false,
'constraints' => [
new IsTrue([
'message' => 'You should agree to our terms.',
]),
],
])
->add('plainPassword', PasswordType::class, [
// instead of being set onto the object directly,
// this is read and encoded in the controller
'mapped' => false,
'attr' => ['autocomplete' => 'new-password'],
'constraints' => [
new NotBlank([
'message' => 'Please enter a password',
]),
new Length([
'min' => 6,
'minMessage' => 'Your password should be at least {{ limit }} characters',
// max length allowed by Symfony for security reasons
'max' => 4096,
]),
],
])
//imbrication de adress dans le formulaire user afin de retrouver toutes les adresses qui lui sont référées
->add('adress',CollectionType::class,[
'entry_type' => AdressType::class,
'entry_options' => ['label' => false],
'allow_add' => true,
'block_name' => 'adress'
])
;
// Data transformer
$builder->get('roles')
->addModelTransformer(new CallbackTransformer(
function ($rolesArray) {
// transform the array to a string
return count($rolesArray)? $rolesArray[0]: null;
},
function ($rolesString) {
// transform the string back to an array
return [$rolesString];
}
));
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => User::class,
]);
}
}
AdressType
<?php
namespace App\Form;
use App\Entity\Adress;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class AdressType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('type',ChoiceType::class,[
'choices' =>[
'Adresse Principale' => 'Principale',
'Adresse Secondaire' => 'Secondaire'
]
])
->add('housenumber',TextType::class,[
'label'=>'N°'
])
->add('additional_info',TextType::class,[
'label'=>'Apt/Etage/...',
'required'=>false
])
->add('street', TextType::class, [
'label' => 'Rue',
'row_attr' => [
'id' => 'Adress2'
],])
->add('postalcode')
->add('city')
->add('region')
->add('country')
->add('latitude', HiddenType::class)
->add('longitude', HiddenType::class)
// ->add('users')
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Adress::class,
]);
}
}
The twig template
{% extends 'base.html.twig' %}
{% block title %}Register{% endblock %}
{% block body %}
{% for flashError in app.flashes('verify_email_error') %}
<div class="alert alert-danger" role="alert">{{ flashError }}</div>
{% endfor %}
<div class="container">
{{ form_start(registrationForm) }}
<div class="form-group">{{ form_row(registrationForm.roles) }}</div>
<div class="form-row">
<div class="form-group col-md-6">{{ form_row(registrationForm.name) }}</div>
<div class="form-group col-md-6">{{ form_row(registrationForm.firstname) }}</div>
</div>
<div class="form-row">
<div class="form-group col-md-6">{{ form_row(registrationForm.email) }}</div>
<div class="form-group col-md-6">{{ form_row(registrationForm.plainPassword, {
label: 'Password'
}) }}</div>
</div>
<div class="form-row">
<div class="form-group col-md-4">{{ form_row(registrationForm.phone) }}</div>
<div class="form-group col-md-4">{{ form_row(registrationForm.NISS, {label: 'NISS'}) }}</div>
<div class="form-group col-md-4">{{ form_row(registrationForm.BCE, {label: 'BCE'}) }}</div>
</div>
{# #TODO formatter pour qu'on differencie bien les adresses entre elles par block. Cf block en dessous#}
<h3>Adresses</h3>
{% block _registration_form_adress_entry_widget %}
<div class="adress" data-prototype="{{ form_widget(registrationForm.adress.vars.prototype)|e('html_attr') }}">
{% for adress in registrationForm.adress %}
<div class="form-group col-md-4">{{ form_row(adress.type) }}</div>
<div class="form-row">
<div class="form-group col-md-4">{{ form_row(adress.street) }}</div>
<div class="form-group col-md-4">{{ form_row(adress.housenumber) }}</div>
<div class="form-group col-md-4">{{ form_row(adress.additional_info) }}</div>
</div>
<div class="form-row">
<div class="form-group col-md-3">{{ form_row(adress.postalcode) }}</div>
<div class="form-group col-md-3">{{ form_row(adress.city) }}</div>
<div class="form-group col-md-3">{{ form_row(adress.region) }}</div>
<div class="form-group col-md-3">{{ form_row(adress.country) }}</div>
</div>
{% endfor %}
</div>
<button type="button" class="add_item_link" data-collection-holder-class="adress">Add an adress</button>
{{ form_end(registrationForm) }}
{% endblock %}
</div>
<script>
const addFormToCollection = (e) => {
const collectionHolder = document.querySelector('.' + e.currentTarget.dataset.collectionHolderClass);
const item = document.createElement('li');
item.innerHTML = collectionHolder
.dataset
.prototype
.replace(
/__name__/g,
collectionHolder.dataset.index
);
collectionHolder.appendChild(item);
collectionHolder.dataset.index++;
};
document
.querySelectorAll('.add_item_link')
.forEach(btn => btn.addEventListener("click", addFormToCollection));
</script>
{% endblock %}
{#{% block registration_form_adress_entry_row %}#}
{# <div class="form-row">#}
{# <div class="form-group col-md-4">{{ form_row(adress.street) }}</div>#}
{# <div class="form-group col-md-4">{{ form_row(adress.housenumber) }}</div>#}
{# <div class="form-group col-md-4">{{ form_row(adress.additional_info) }}</div>#}
{# </div>#}
{# <div class="form-row">#}
{# <div class="form-group col-md-3">{{ form_row(adress.postalcode) }}</div>#}
{# <div class="form-group col-md-3">{{ form_row(adress.city) }}</div>#}
{# <div class="form-group col-md-3">{{ form_row(adress.region) }}</div>#}
{# <div class="form-group col-md-3">{{ form_row(adress.country) }}</div>#}
{# </div>#}
{#{% endblock %}#}
The code rendering
Thank you :)
You can define a macro that will render both the existing subfields of the form and the prototype string.
{% import _self as formMacros %}
{% macro address(item) %}
... your code formatting the address subform ...
{% endmacro %}
So this line in twig:
<div class="adress" data-prototype="{{ form_widget(registrationForm.adress.vars.prototype)|e('html_attr') }}">
would be
<div class="adress" data-prototype="{{ formMacros.address(registrationForm.adress.vars.prototype)|e('html_attr') }}">
and same thing for the existing fields.

Multiple forms in Symfony. Display works but multiple persist

I have a page with forms in hiddens modals.
Forms come from the same entity with different ids.
Displaying the modal works. For each modal I want to display, datas are ok inside inputs.
The issue is : when I submit the form, every forms (hidden included) are persisted.
//EditServiceType.php
namespace Guillaume\PartnerManagerBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
class EditServiceType extends AbstractType
{
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name', null, array(
'label_attr' => array('class' => 'col-form-label'),
'attr' => array('class' => 'form-control')
))
->add('type')
->add('version')
->add('connection_type', ChoiceType::class, array(
'choices' => array(
'Connected' => 2,
'Local' => 1,
)));
}/**
* {#inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Guillaume\PartnerManagerBundle\Entity\Service'
));
}
/**
* {#inheritdoc}
*/
public function getBlockPrefix()
{
return 'edit_guillaume_partnermanagerbundle_service';
}
}
Part of my Controller
$formEdits = [];
$forms = [];
$serviceFormEdits = [];
foreach($services as $service) {
$serviceFormEdits[$service->getId()] = $em->getRepository('GuillaumePartnerManagerBundle:Service')->find($service);
$forms[$service->getId()] = $this->createForm('Guillaume\PartnerManagerBundle\Form\EditServiceType', $serviceFormEdits[$service->getId()]);
$formEdits[$service->getId()] = $forms[$service->getId()]->createView();
$forms[$service->getId()]->handleRequest($request);
if ($forms[$service->getId()]->isSubmitted() && $forms[$service->getId()]->isValid()) {
$em->persist($serviceFormEdits[$service->getId()]);
$em->flush();
$this->addFlash('notice', 'Service has been edited');
}
}
}
//(...)
return $this->render('GuillaumePartnerManagerBundle:customers:customer.html.twig', [
'formEdits' => $formEdits,
]);
Part of my twig (the modal)
{% for result in results %}
{% form_theme formEdits[result.service.id] 'bootstrap_4_horizontal_layout.html.twig' %}
<div class="modal fade" id="editEnvironment-{{ result.service.id }}" tabindex="-1" role="dialog"
aria-labelledby="exampleModalCenterTitle" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h2 class="modal-title" id="exampleModalLongTitle">{{ 'Edit Environment'|trans }}</h2>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
{{ form_start(formEdits[result.service.id]) }}
<div class="form-group row">
{{ form_widget(formEdits[result.service.id]) }}
</div>
<div class="modal-footer row">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" value="cancel" class="btn btn-secondary" data-dismiss="modal">{{ 'Cancel'|trans }}</button>
<button type="submit" value="save" class="btn btn-primary">{{ 'Validate'|trans }}</button>
</div>
</div>
{{ form_end(formEdits[result.service.id]) }}
</div>
</div>
</div>
</div>
{% endfor %}
Finally I figured it out.
adding 'id' in options resolver
$forms[$service->getId()] = $this->createForm('Guillaume\PartnerManagerBundle\Form\EditServiceType', $serviceFormEdits[$service->getId()], ['id' => $service->getId()]);
adding input hidden in formType
->add('id', HiddenType::class, array(
'data' => $options['id'],
'mapped' => false,
))
and I use this id when submit is valid
$postData = $request->request->get('edit_guillaume_partnermanagerbundle_service');
$postId = $postData['id'];
$forms[$postId]->handleRequest($request);
if ($forms[$postId]->isSubmitted() && $forms[$postId]->isValid()) {
$em->persist($serviceFormEdits[$postId]);
$em->flush($serviceFormEdits[$postId]);
//(...)

Symfony 3 rendering Form wrong Request

In symfony3 i have base view and inside this one im rendering other controller (this controller render form (return view with form - form is method POST)) when i try to use this one on the page object Request-isMethod() always return GET how to fix it.
Part of base view: (main controller show welcome page and render inside this one)
<li><a>{{ render(controller('CommonUserBundle:Login:login')) }}</a></li>
Form login type
class LoginType extends AbstractType
{
public function getName()
{
return 'login_form';
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->
add('username', TextType::class, array(
'label' => 'Użytkownik'
))
->add('password', PasswordType::class, array(
'label' => 'Hasło'
))
->add('remember_me', CheckboxType::class, array(
'label' => 'Zapamiętaj mnie'
))
->add('save', SubmitType::class, array(
'label' => 'Zaloguj'
));
}
}
Controller that render the form above
/**
* #Route("/", name="login")
*
* #Template
*/
public function loginAction(Request $Request)
{
$Session = $this->get('session');
if($Request->attributes->has(Security::AUTHENTICATION_ERROR)){
$loginError = $Request->attributes->get(Security::AUTHENTICATION_ERROR);
} else {
$loginError = $Session->remove(Security::AUTHENTICATION_ERROR);
}
$loginForm = $this->createForm(LoginType::class, array(
'username' => $userName = $Session->get(Security::LAST_USERNAME)
));
return array(
'loginError' => $loginError,
'loginForm' => $loginForm->createView()
);
}
Twig of above
<button type="button" class="btn btn-success btn-sm" data-toggle="modal" data-target="#loginForm">
Zaloguj
</button>
<div class="modal fade" id="loginForm" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
<h4 class="modal-title" id="myModalLabel">Logowanie</h4>
{% if loginError is defined and loginError is not null %}
<div class="">
Blad i chuj: {{ loginError.message }}
</div>
{% endif %}
</div>
<div class="modal-body">
{% form_theme loginForm 'bootstrap_3_layout.html.twig' %}
{{ form(loginForm, {'action': path('_check_path')}) }}
</div>
<div class="modal-footer">
{{ render(controller('CommonUserBundle:Login:rememberPassword')) }}
<button type="button" class="btn btn-default" data-dismiss="modal">Zamknij</button>
</div>
</div>
</div>
</div>
So as i said, i render this form on other twig tempalte and after i click SUBMIT my Request->getMethod is GET. In html all is ok method="post", when i create other Route only to this form his own (not rendered) its working ok. How to change it to work ok?

Symfony2 2x many-to-one in form missing form data

I am using a Doctrine 2x one-to-many linking table like what is recommended in the docs
My Doctrine relationships seem to be fetching data properly, as evidenced by the fact that vars.data.myCollectionName has values,
{{ ladybug_dump( bandVacancyAssociation.bandVacancy.vars.data.genres) }}
yet when I try to access the form field children in order to use the form_row twig function my data is always an empty array
{{ ladybug_dump(edit_form.bandVacancyAssociations.children[0].children)}}
Since I do have values I can reverse engineer the symfony form structure but I would rather get it working using the built-in form widgets, any assistance would be much appreciated.
Here is the relevant code
Band
/**
* #ORM\OneToMany(targetEntity="BandVacancyAssociation", mappedBy="band", cascade={"persist"})
*/
private $bandVacancyAssociations;
BandType
$builder
->add('name')
->add('description')
->add('bandVacancyAssociations','collection',
array(
'by_reference' => true,
'type' => new BandVacancyAssociationType()
)
)
BandVacancy
/**
* #ORM\OneToMany(targetEntity="BandVacancyAssociation", mappedBy="bandVacancy",fetch="EAGER")
*/
private $bandVacancyAssociations;
/**
* #ORM\ManyToMany(targetEntity="Genre", inversedBy="bandVacancies",fetch="EAGER")
* #ORM\JoinTable(name="bandvacancy_genre",
* joinColumns={#ORM\JoinColumn(name="bandvacancy_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="genre_id", referencedColumnName="id")}
* )
*/
protected $genres;
/**
* #ORM\ManyToMany(targetEntity="Instrument", inversedBy="bandVacancies",fetch="EAGER")
* #ORM\JoinTable(name="bandvacancy_instrument",
* joinColumns={#ORM\JoinColumn(name="bandvacancy_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="instrument_id", referencedColumnName="id")}
* )
*/
protected $instruments;
BandVacancyType
$builder->add('genres', 'entity', array(
'multiple' => true,
'expanded' => true,
'class' => 'ZE\BABundle\Entity\Genre',
'property' => 'name',
))
->add('instruments', 'entity', array(
'multiple' => true,
'expanded' => true,
'class' => 'ZE\BABundle\Entity\Instrument',
'property' => 'name',
));
Band Vacancy Association
/**
* #ORM\ManyToOne(targetEntity="BandVacancy", inversedBy="bandVacancyAssociations",fetch="EAGER")
*/
private $bandVacancy;
BandVacancyAssociationType
$builder
->add('bandVacancy', 'entity', array( 'class' => 'ZE\BABundle\Entity\BandVacancy'))
;
It turns out that the one thing I was missing was this:
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('bandVacancy', new BandVacancyType())
;
}
I can now loop through the values in the form.
<div class="panel">Band Vacancies
{% for bandVacancyAssociation in edit_form.bandVacancyAssociations %}
{#{{ dump(bandVacancyAssociation.vars.data.id) }}#}
<div class="panel-group" id="accordion">
<div class="panel panel-default" id="panel{{ bandVacancyAssociation.vars.data.id }}">
<div class="panel-heading">
<h4 class="panel-title">
<a data-toggle="collapse" data-target="#collapse{{ bandVacancyAssociation.vars.data.id }}"
href="#collapse{{ bandVacancyAssociation.vars.data.id }}">
{{ bandVacancyAssociation.vars.data.bandVacancy.name}}
</a>
</h4>
</div>
<div id="collapse{{ bandVacancyAssociation.vars.data.id }}" class="panel-collapse collapse in">
<div class="panel-body">
{{ form_label(bandVacancyAssociation.bandVacancy.name) }}
{{ form_widget(bandVacancyAssociation.bandVacancy.name) }}
{{ form_label(bandVacancyAssociation.bandVacancy.comment) }}
{{ form_widget(bandVacancyAssociation.bandVacancy.comment) }}
{% for genre in bandVacancyAssociation.bandVacancy.genres %}
{{ form_label(genre) }}{{ form_widget(genre) }}
{% endfor %}
{% for instrument in bandVacancyAssociation.bandVacancy.instruments %}
{{ form_label(instrument) }}{{ form_widget(instrument) }}
{% endfor %}
</div>
</div>
</div>
</div>
{% endfor %}
</div>
Thanks to whoever downvoted this question.

Error on form submission: The CSRF token is invalid. Please try to resubmit the form [duplicate]

This question already has answers here:
The CSRF token is invalid. Please try to resubmit the form
(15 answers)
Closed 7 years ago.
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">×</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;
}
});
});
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.
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.