Symfony2 form class renders all rows even if I don't want it to - forms

First of all, I am sorry if the question title is a bit odd, but I do not know what else to call it...
I have this form class, which I cannot change:
class ItemDetailType extends AbstractType {
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder
->add('name', 'text', array(
'label' => 'Název'))
->add('room', 'entity', array(
'class' => 'CvutFitIctCtuIdentityBundle:Room',
'label' => 'Místnost'))
->add('person', 'entity', array(
'class' => 'CvutFitIctCtuIdentityBundle:Person',
'label' => 'Osoba'))
->add('organizationalUnit', 'entity', array(
'class' => 'CvutFitIctCtuIdentityBundle:OrganizationalUnit',
'label' => 'Organizační jednotka'))
;
$builder->setAttribute('attr', array());
if (isset($options['render_submit']) && $options['render_submit'])
$builder
->add('submit', 'submit', array(
'label' => 'Uložit',
'attr' => array('class' => 'btn btn-success')))
->add('reset', 'reset', array(
'label' => 'Zrušit',
'attr' => array('class' => 'btn btn-danger')))
;
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver) {
$resolver->setDefaults(array(
'data_class' => 'Cvut\Fit\BiWt2\InventoryBundle\Entity\Item',
'render_submit' => true,
'attr' => array(
'role' => 'form',
'class' => 'form-horizontal'
)
));
}
/**
* #return string
*/
public function getName() {
return 'cvut_fit_biwt2_inventory_form_item';
}
}
But in a template, I have to render only some of the rows (room, person, organizationalUnit and submit), and not render name and reset. This is in the conditions I am obliged to meet, so editing the class is not a valid option.
In controller I create the form like this:
$form = $this->createForm(
new ItemDetailType, $item, array(
'action' => $this->generateUrl('items_detail_form', array('id' => $id)),
'render_submit' => true
)
);
I tried to render only the desired rows this way, but it only makes the go on top of the form and the remaining two are still rendered under them...
{{ form_start(form) }}
{{ form_errors(form) }}
{{ form_row(form.room) }}
{{ form_row(form.person) }}
{{ form_row(form.organizationalUnit) }}
{{ form_row(form.submit) }}
{{ form_end(form) }}
So I am a bit confused now. Is this the correct behavior? If yes, than how do I achieve what I need? The documentation is somewhat brief about this...
Thank you very much!

In symfony2 the default behaviour of:
{{ form_end(form) }}
is render all (even not mentioned before) fields like
{{ form_rest(form) }}
If you want to prevent this behaviour the one option is use:
</form>
or the better way like in this document http://symfony.com/doc/current/reference/forms/twig_reference.html#form-end-view-variables
{{ form_end(form, {'render_rest': false}) }}
Remember to render CSRF token manualy if you do this in that way:
{{ form_widget(form._token) }}

what about using
{% do form.name.setRendered %}
{% do form.reset.setRendered %}
This tell twig that the fields are shown even if they aren't

I am confused as to what exactly do you want to achieve here. But here are some thoughts:
You can create a new form type that extends this one if you want.
class ShorterItemDetailType extends ItemDetailType
{
public function buildForm(FormBuilderInterface $builder, array $options) {
// only add the fields you want
}
public function getParent() {
return 'cvut_fit_biwt2_inventory_form_item'
}
/**
* #return string
*/
public function getName() {
return 'cvut_fit_biwt2_inventory_form_item_shorter';
}
}
And in your controller use this one.
$form = $this->createForm(
new ShorterItemDetailType(), $item, array(
'action' => $this->generateUrl('items_detail_form', array('id' => $id))
)
);

Related

Symfony return empty form after validation with errors

$user = new User();
$form = $this->createForm(RegistrationFormType::class, $user);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
/** do stuff */
}
return $this->render('security/register.twig', [
'registrationForm' => $form->createView()
]);
class RegistrationFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('nickname', TextType::class, [
'label' => 'asdasdasd',
])
->add('email', TextType::class, [
'label' => 'Email',
])->add->add end more add }
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => User::class,
'csrf_protection' => true,
// the name of the hidden HTML field that stores the token
'csrf_field_name' => '_token',
// an arbitrary string used to generate the value of the token
// using a different string for each form improves its security
'csrf_token_id' => 'registrationForm',
]);
}
}
after wrong validation render an empty form
formType
this is a registrationtype class
what i can add to formType or to controller for returning filled input fields
After viewing the comments under the question, it seems that the Twig part of the form is missing.
To render your form inside your Twig template, you should use Twig form functions.
In your case you can simply write:
{{ form_start(form) }}
{{ form_errors(form) }}
{{ form_row(form.nickname) }}
{{ form_row(form.email) }}
{{ ... }}
{{ form_row(form.submit, { 'label': 'Submit me' }) }}
{{ form_end(form) }}
You also can use all form types that Symfony provides. For example, you can use for email field: Email field type.
->add('email', EmailType::class, [
'label' => 'Email',
])
You can use many form field types, Twig can handle them: form types

Can't set submit button

I created a form with a submit button type in it. But it doesn't work.
Here is the form :
class DisputedType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
//->add('points')
->add('position')
->add('joueur', EntityType::class, [
// looks for choices from this entity
'class' => Joueur::class,
// uses the User.username property as the visible option string
'choice_label' => 'prenom',
'expanded' => 'true',
'multiple' => 'true'
])
->add('save', SubmitType::class, ['label' => 'Suivant']);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Disputed::class,
]);
}
}
Then the twig view :
<h1>Sélectionnez les joueurs</h1>
{{ form(form) }}
{{ form_start(form) }}
{{ form_row(form.save)}}
{{ form_end(form) }}
But it gives me this error :
Neither the property "save" nor one of the methods "save()", "getsave()"/"issave()"/"hassave()" or "__call()" exist and have public access in class "Symfony\Component\Form\FormView".
I don't know why I get this error message. Do you have any idea ?
{{ form(form) }}
Above code itself rendered all fields.
So you have use either this line or below code
{{ form_start(form) }}
{{ form_row(form.save)}}
{{ form_end(form)}}

Symfony form not valid and not submittable

When trying to submit my form, it is not working. The button is not doing anything at all. So I tried to dump something withing my:
if ($form->isValid() && $form->isSubmitted())
And realized that it's not even entering the if statement, so I assume that there is something wrong with my form. But I can't yet figure out what it would be, so I would be glad if anyone could help me!
/**
* #Route("/document/bulkdeactivate", name="documentBundle_document_bulkDeactivate")
* #Template()
*/
public function bulkDeactivateAction(Request $request) {
$em = $this->getDoctrine()->getManager();
$selected_documents = $request->request->all();
$form = $this->createForm(DocumentDeactivationType::class);
$form->handleRequest($request);
if ($form->isValid() && $form->isSubmitted()) {
foreach($selected_documents as $document) {
$documentR = json_decode(json_encode($document), true);
dump($documentR);
for($i=0; $i<count($documentR); $i++){
$doc = $em->getRepository('DocumentBundle:Document')->findOneById($documentR[$i]);
dump($doc);
$doc->setActive(false);
$em->persist($doc);
$em->flush();
}
}
$this->addFlash(
'success',
'The document has been deactivated!'
);
return $this->redirectToRoute('documentBundle_document_list');
}
return $this->render('DocumentBundle:Panels:ActivationPanel.html.twig', array(
'form' => $form->createView(),
));
}
my Form Type:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('documentlist', EntityType::class, array(
'class' => 'DocumentBundle:Document',
'choice_label' => 'name',
'required' => false,
'multiple' => true,
'expanded' => false,
'placeholder' => "Select Documents",
'label' => 'label.document_list',
))
->add('submit', SubmitType::class, array(
'label' => 'Submit',
'attr' => array(
'class' => 'btn btn btn-default',
),
));
}
And the form part of my twig template:
{% block content %}
{{ form_start(form) }}
{{ form(form.documentlist) }}
{{ form(form.submit) }}
{{ form_end(form) }}
{% endblock content %}
There must be some issues with the form, do you have any advice for my?
Actually there are several issues:
you cannot add a submit button that is not bound to a Symfony form in your case because Symfony will not identify it properly (for lack of a name that has to match SF's automatic input names generation)
In your formBuilder put:
$builder->add('submit', SubmitType::class);
your form has an incorrect name ("form") that conflicts with Twig's form() function, you have to change it,
you might have an issue (not tested, just a guess) regarding the many calls made to Twig's form() because this function dumps the whole form content, to manually dump each part use
Your {% block content %} should have this:
{{ form_widget(form.your_widget_name, attrs) }}
I eventually solved my issue:
1. as pointed out by #NaeiKinDus, I needed a Submit button that actually belonged to my form, plus I changed my form name to a custom deactivationForm
{% block content %}
{{ form(deactivationForm) }}
{% endblock content %}
the buildForm method:
$builder
->add('documentlist', EntityType::class, array(
'class' => 'DocumentBundle:Document',
'choice_label' => 'name',
'required' => false,
'multiple' => true,
'expanded' => false,
'placeholder' => "Select Documents",
'label' => 'label.document_list',
'translation_domain' => 'Documents',
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('d')
->where('d.active = FALSE');
},
))
->add('submit', SubmitType::class, array(
'label' => 'Submit',
'attr' => array(
'class' => 'btn btn btn-default',
),
));
then in my controller:
/**
* #Route("/document/bulkdeactivate", name="documentBundle_document_bulkDeactivate")
* #Template()
*/
public function bulkDeactivateAction(Request $request) {
/*
* GET DOCUMENT FROM DB
*/
$em = $this->getDoctrine()->getManager();
$selected_documents = $request->request->all();
$deactivationForm = $this->createForm(DocumentDeactivationType::class);
$deactivationForm->handleRequest($request);
if ($deactivationForm->isValid() && $deactivationForm->isSubmitted()) {
foreach($selected_documents as $document) {
$documentR = json_decode(json_encode($document), true);
dump(count($documentR['documentlist']));
for($i=0; $i<count($documentR['documentlist']); $i++){
$doc = $em->getRepository('DocumentBundle:Document')->findOneById($documentR['documentlist'][$i]);
dump($documentR['documentlist'][$i]);
$doc->setActive(true);
$em->persist($doc);
$em->flush();
}
}
$this->addFlash(
'success',
'The selected document(s) have been deactivated!'
);
return $this->redirectToRoute('documentBundle_document_list');
}
return $this->render('DocumentBundle:Panels:ActivationPanel.html.twig', array(
'deactivationForm' => $deactivationForm->createView(),
));
}
I tried to access the wrong array positions (I didn't realize that the decoding of the ajax data sent me 3 array positions but I only wanted to access the first 'documentlist' and then get the document IDs from that one.

how to show specific field of a form for 'create' and 'edit' action - Symfony3

I have a simple FormType attached to an entity called media wich I rendered in my view.
I have a newAction that lets me create my object, and a editAction that lets me edit it with my same form in my controller.
However I don't want some field appears in my editview` as I already entered them when I created it.
But even though I use form_row to specifically render my form line by line, when I add the form_end at the end, it renders all my fields, even the ones I didn't call.
My FormType
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name', FileType::class, array(
'data_class' => null,
'label' => "Add an image"
))
->add('context', ChoiceType::class, array(
'label' => 'image section',
'choices' => array(
'header' => 'header',
'middle' => 'middle',
)
))
->add('save', SubmitType::class, array(
'label' => "Add"
));
}
My view
{{ form_start(editForm) }}
{{ form_row(editForm.name) }}
{{ form_row(editForm.save) }}
{{ form_end(editForm) }}
But even if I use the form rows, it actually shows my context field in the view, which I didn't call.
So I tried some hack to get around it.
this one worked but when I click on submit form, it shows me an error that context field cannot be null, so this doesn't do the trick
{% do editForm.context.setRendered %}
And I found a way to do it with jQuery to hide the form like this
<script>
$(document).ready(function () {
$("#media_context").parent().hide();
});
</script>
the jQuery works and hide my row in my form. But I was wondering if I could do it without using jQuery and be able to render only specific field of my form in my view?
In Symfony 2, you could remove some fields from the builder when editing your entity. Your edit form must extends your create form in Symfony 2.
I think you can do the same in Symfony 3, try something like :
class EditType extends CreateType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
parent::buildForm($builder, $options);
$builder
->remove('context') //remove the fields that no longer needed
}
public function configureOptions(OptionsResolver $resolver)
{
/...
}
}
You don't need to change CreateType
class Createtype extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name', FileType::class, array(
'data_class' => null,
'label' => "Add an image"
))
->add('context', ChoiceType::class, array(
'label' => 'image section',
'choices' => array(
'header' => 'header',
'middle' => 'middle',
)
))
->add('save', SubmitType::class, array(
'label' => "Add"
));
}
}
From the symfony docs:
This helper (form_end) also outputs form_rest() unless you set render_rest to false.
form_rest(view, variables)
This renders all fields that have not yet been rendered for the given form.
{# don't render unrendered fields #}
{{ form_end(form, {'render_rest': false}) }}
Try this
{{ form_start(editForm) }}
{{ form_row(editForm.name) }}
{{ form_row(editForm.save) }}
{{ form_end(editForm, {'render_rest': false}) }}

Symfony2 : Change {{ form_enctype }} to {{ form_start }} with a custom path

I'm working on updating my code to Symfony3 standards but I come to some problems with some of my forms. I tried several things but nothing is working.
In my twig :
<form action="{{ path('paces_colle_gestioncsv_imprimer',
{ 'id' : document.id }) }}" method="post" {{ form_enctype(form) }}>
</form>
{{ form_enctype }} is deprecated since Symfony 2.8. I have to get the path to the controller to change it to {{ form_start }}
In my controller :
public function ajouterErrataAction($id)
{
$em = $this->getDoctrine()->getManager();
$document = $em->getRepository('PACESColleBundle:DocumentCSV')->find($id);
if(!$document){
throw $this->createNotFoundException('Document inexistant');
}
$colle = $document->getColle();
$form = $this->createForm(ajouterErrataType::class);
return $this->render('PACESColleBundle:Classement:ajouterErrata.html.twig', array(
"colle" => $colle,
"document" => $document,
"form" => $form->createView()
));
}
My formType :
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('errata', TextareaType::class, array('required' => false,
'attr' => array('class'=>'ckeditor')
))
->add('precision', TextareaType::class, array('required' => false,
'attr' => array('class'=>'ckeditor')))
->add('submit',SubmitType::class, array('label' => 'Imprimer'));
;
}
You can override action in controller or template:
http://symfony.com/doc/current/book/forms.html#changing-the-action-and-method-of-a-form
Have you tried?
You can set the action path like this:
$form = $this->createForm(ajouterErrataType::class, $data, [
'action' => $this->generateUrl(''paces_colle_gestioncsv_imprimer', ['id' => $document->getId()]),
'method' => 'POST',
]);
The Symfony 3 way of doing it is like this:
$form = $this->createForm(TaskType::class, $task, array(
'action' => $this->generateUrl('target_route'),
'method' => 'GET',
));