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 !
Related
I'm working on a symfony project and i have the login form in the header of the base.html.twig template, and all my templates extend base.html.twig.
The problem is when I try to render any template it should always render data (_csrf_token,last_username,..).
So I want to be able to login at every page and it's not recommended to render data in all my templates.
Is there any solution?
base.html.twig
<form action="{{ path("fos_user_security_check") }}" method="post">
<div class="form-group">
<input type="hidden" name="_csrf_token" value="{{ csrf_token }}" />
<input type="text" id="email-modal" name="_username" value="{{ last_username }}" class="form-control" required="required" placeholder="Nom d'utilisateur | Email">
</div>
<div class="form-group">
<input type="password" required="required" name="_password" class="form-control" id="password-modal" placeholder="Mot de passe">
</div>
<div class="form-group">
<label for="remember"> <p class="text-center text-muted"> Se souvient de moi </p> </label>
<input type="checkbox" id="remember_me" name="_remember_me" value="on" />
</div>
<p class="text-center">
<button type="submit" id="_submit" name="_submit" value="{{ 'security.login.submit'|trans({}, 'FOSUserBundle') }}" class="btn btn-primary" ><i class="fa fa-sign-in"></i>Se connecter </button>
</p>
</form>
<p class="text-center text-muted">Vous n'avez pas encore un compte?</p>
<p class="text-center text-muted"><strong>S'inscrire</strong></p>
Controller.php
}
/**
* #Route("/")
*/
public function indexAction()
{
// return $this->render('default/index.html.twig');
$request = $this->container->get('request');
/* #var $request \Symfony\Component\HttpFoundation\Request */
$session = $request->getSession();
/* #var $session \Symfony\Component\HttpFoundation\Session\Session */
// get the error if any (works with forward and redirect -- see below)
if ($request->attributes->has(SecurityContext::AUTHENTICATION_ERROR)) {
$error = $request->attributes->get(SecurityContext::AUTHENTICATION_ERROR);
} elseif (null !== $session && $session->has(SecurityContext::AUTHENTICATION_ERROR)) {
$error = $session->get(SecurityContext::AUTHENTICATION_ERROR);
$session->remove(SecurityContext::AUTHENTICATION_ERROR);
} else {
$error = '';
}
if ($error) {
// TODO: this is a potential security risk (see http://trac.symfony-project.org/ticket/9523)
$error = $error->getMessage();
}
// last username entered by the user
$lastUsername = (null === $session) ? '' : $session->get(SecurityContext::LAST_USERNAME);
$csrfToken = $this->container->has('form.csrf_provider') ? $this->container->get('form.csrf_provider')->generateCsrfToken('authenticate') : null;
return $this->renderLogin(array(
'last_username' => $lastUsername,
'error' => $error,
'csrf_token' => $csrfToken,
'operations'=>$operations,
));
}
/**
* Renders the login template with the given parameters. Overwrite this function in
* an extended controller to provide additional data for the login template.
*
* #param array $data
*
* #return \Symfony\Component\HttpFoundation\Response
*/
protected function renderLogin(array $data)
{
$template = sprintf('default/index.html.twig', $this->container->getParameter('fos_user.template.engine'));
return $this->container->get('templating')->renderResponse($template, $data);
}
In my opinion the best way to do it is to use symfony capability to render a view with method render with a controller in parameter.
In your case what you should do is to override the login.html.twig from FOSUserBundle with your form who display the login form only.
You can find how to do this here if you don't know how.
Then in your base.html.twig you can call the controller of FOSUserBundle in charge of the login action with something like this :
{{ render(controller(
'FOSUserBundle:Security:login'
)) }}
Or if you have override FOSUserBundle Controller
{{ render(controller(
'YourAppBundle:Security:login'
)) }}
You can learn more about this here.
With this method you don't need to have all the login logic of you code in your indexAction. You can render data in your template when these data are here for security. csfr_token is a must have for secure login form and lastUsername is here for the user experience. lastUsername can be remove if you really want but it's just one more data and i think it's an user experience improvement.
Possibly 2 solutions here :
First one (not sure of it), you create a controller that display the form login with all the variables and you render this controller in your base template.
Second one (better) you create your login form as service so you can use it anywhere
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'
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) }}
Given the simple form build:
$form = add('a')
->add('b')
->add('c')
->add('d')
->add('e')
->add('f');
I'd like to wrap a div tag around ABC and DEF, like so:
<div class="section1">
<input type="text" name="a" />
<input type="text" name="b" />
<input type="text" name="c" />
</div>
<div class="section2">
<input type="text" name="d" />
<input type="text" name="e" />
<input type="text" name="f" />
</div>
Problem is, I am just able to use the Symfony Form Component for this project. Is there a way with twig to render the form fields in groups like above? I will need to specify logic that says something simliar to "start section 1 with 'a' and section 2 with 'd'", so this way if any fields change inbetween (say we remove the field with the name 'b') the form will still work.
My twig file looks like this, which is obviously not correct:
<form action="#" method="POST" {{ form_enctype(form) }}>
{% for child in form.children %}
<div class="form_row">
{{ form_label(form) }}
{{ form_errors(form) }}
{{ form_widget(form) }}
</div>
{% endfor %}
</form>
The best way to do it I think is building the form outside the controller (as a class, extending AbstracType)
So, you will need these files:
Your controller (as DefaultController.php)
Your twig template (as index.html.twig)
Your main form (MyFormType.php in this example)
One form for each section (in your example Section1Type.php and Section2Type.php)
So, the idea is building one single form (MyFormType) made by many individual section forms (Section1Type and Section2Type). Then calling this main form in your controller and rendering it in your template (with a "for" loop for each section).
So here you have the code:
Your controller:
<?php
# /src/Acme/DefaultBundle/Controller/DefaultController.php
namespace Acme\DefaultBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Acme\DefaultBundle\Form\MyFormType;
class DefaultController extends Controller
{
public function indexAction()
{
$form = $this->createForm(new MyFormType());
return $this->render('AcmeDefaultBundle:Default:index.html.twig', array(
'form' => $form->createView()
));
}
}
Your template:
{# /src/Acme/DefaultBundle/Resources/views/Default/index.html.twig #}
{{ form_start(form) }}
<div class="section1">
{% for input in form.section1 %}
{{ form_label(input) }}
{{ form_widget(input) }}
<br>
{% endfor %}
</div>
<div class="section2">
{% for input in form.section2 %}
{{ form_label(input) }}
{{ form_widget(input) }}
<br>
{% endfor %}
</div>
{{ form_end(form) }}
The main form:
<?php
# /src/Acme/DefaultBundle/Form/MyFormType.php
namespace Acme\DefaultBundle\Form;
use Acme\DefaultBundle\Form\Section1Type;
use Acme\DefaultBundle\Form\Section2Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
class MyFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
// This calls your section forms as many as you need
$builder->add('section1', new Section1Type());
$builder->add('section2', new Section2Type());
$builder->add('Send', 'submit');
}
public function getName()
{
return 'myform';
}
}
The Section1 form:
<?php
# /src/Acme/DefaultBundle/Form/Section1Type.php
namespace Acme\DefaultBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
class Section1Type extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('a', 'text');
$builder->add('b', 'text');
$builder->add('c', 'text');
}
public function getName()
{
return 'section1';
}
}
And Section2 form:
<?php
# /src/Acme/DefaultBundle/Form/Section2Type.php
namespace Acme\DefaultBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
class Section2Type extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('d', 'text');
$builder->add('e', 'text');
$builder->add('f', 'text');
}
public function getName()
{
return 'section2';
}
}
And that's it, you can edit each individual section (adding and removing inputs) and you will get all of them in your template without modifying it.
Using Symfony2.3.4.
I'm trying to create a form without using a type, it is actually a very small form, only two selects loading their options from the database, so far it works, what I can not do is to get the form data(in the controller) when it is submitted. I tried to follow the instruction here but I can't get it right.
Here is my code so far:
Controller:
function to pass the data to the form:
public function selectAction($id, $id_actpost){
$em = $this->getDoctrine()->getManager();
$entity = $em->getRepository('ActPostBundle:Edition')->find($id);
$students = $em->getRepository('PersonBundle:Students')->findAll();
$students2 = $em->getRepository('PersonBundle:ForeignStudents')->findAll();
if (!$entity) {
throw $this->createNotFoundException('Unable to find Edicion entity.');
}
return $this->render('ActPostBundle:Edition:select.html.twig', array(
'entity' => $entity,
'id_actpost' => $id_actpost,
'students' => $students,
'foreignstudents' => $students2,
));
}
html fragment regarding the form itself:
<form class="form-horizontal sf_admin_form_area"
action="{{ path('edition_update_selected',
{ 'id': entity.id, 'id_actpost': id_actpost }) }}"
method="post" enctype="multipart/form-data">
<div style="margin-left: 80px" class="row-fluid">
<div class="span12">
<select name="students" multiple="multiple">
{% for s in students %}
<option {%if s in entity.students%}selected="selected"{%endif%}>
{{s}}</option>
{%endfor%}
</select>
</div>
</div>
<div class="row-fluid">
<select name="students2" multiple="multiple">
{% for s in students2 %}
<option {%if s in entity.foreignstudents%}selected="selected"
{%endif%}>{{s}}</option>
{%endfor%}
</select>
</div>
<div class="form-actions">
<button type="submit" class="btn btn-primary">
<i class="glyphicon-refresh"></i> {{'Update' | trans}}</button>
<a class="btn" href="{{ path('edition', {'id_actpost' : id_actpost }) }}">
<i class="glyphicon-ban-circle"></i> {{'Cancel' | trans }}
</a>
</div>
</form>
and here is what I read from the link posted at the beginning:
function to get the submitted data and update the database, although the database part can stay ignored until I manage to get the data from the form:
public function update_selectedAction(Request $request, $id, $id_actpost) {
$em = $this->getDoctrine()->getManager();
$entity = $em->getRepository('ActPostBundle:Edition')->find($id);
if (!$entity) {
throw $this->createNotFoundException('Unable to find Edicion entity.');
}
$defaultData = array('message' => 'Type here');
$editForm = $this->createFormBuilder($defaultData)
->add('students','choice')
->add('students2', 'choice')
->getForm();
$editForm->handleRequest($request);
I would like to know if what I read is actually what I need, because although I think it is I might be wrong, so any insights regarding this matter or even any other way to do it will be really appreciated.
You should use symfony's form builder to build the form in your update_selectedAction() like
public function update_selectedAction(Request $request, $id, $id_actpost)
{
$em = $this->getDoctrine()->getManager();
$entity = $em->getRepository('ActPostBundle:Edition')->find($id);
if (!$entity) {
throw $this->createNotFoundException('Unable to find Edicion entity.');
}
$defaultData = array('message' => 'Type here');
$form = $this->createFormBuilder($defaultData)
->add('students', 'entity',array('class' => 'PersonBundle:Students',
'property' => 'students','expanded'=>false,'multiple'=>false))
->add('students2', 'entity',array('class' => 'PersonBundle:ForeignStudents',
'property' => 'foreignstudents','expanded'=>false,'multiple'=>false))
->add('submit','submit')
->getForm();
if ($request->getMethod() == "POST") {
$form->submit($request);
if ($form->isValid()) {
$postData = current($request->request->all());
var_dump($postData); /* All post data is here */
/* echo $postData['students']; */
/* echo $postData['students2']; */
/*
* do you update stuff here
* */
}
}
return $this->render('ActPostBundle:Edition:select.html.twig', array('form'=>$form->createView()));
}
In your twig i.e ActPostBundle:Edition:select.html.twig render your form
{{ form(form) }}
Edit from comments
In your twig file render your form like
{{ form_errors(form) }}
{{ form_row(form.students) }}
{{ form_row(form.students2) }}
{{ form_row (form._token) }}
<input type="submit"> /* your submit button */
Edit from comments 2
IF you want to put the text in value attribute of selectbox you can use choice field type
$students = $em->getRepository('PersonBundle:Students')->findAll();
$students2 = $em->getRepository('PersonBundle:ForeignStudents')->findAll();
$studentsArray=array();
$students2Array=array();
foreach($students as $s){
$studentsArray[$s->getStudents()]=$s->getStudents();
}
foreach($students2 as $s){
$students2Array[$s->getForeignstudents()]=$s->getForeignstudents();
/* here array key part will be the value of selectbox like $students2Array['your val to get in post data'] */
}
$form = $this->createFormBuilder($defaultData)
->add('students', 'choice',array('choices' => $studentsArray,'expanded'=>false,'multiple'=>false))
->add('students2', 'choice',array('choices' => $students2Array,'expanded'=>false,'multiple'=>false))
->add('submit','submit')
->getForm();