Symfony form not valid and not submittable - forms

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.

Related

symfony search form keep seach parameters

I made a searchform, but i don't know how to keep the selected search parameters in the form.
I would like to do this through the controller and not in twig file self.
I thought by setting the parameter it will preselect the option but that doesn't work.
Below my form
$builder
->add('type', EntityType::class, [
'class' => Producttype::class,
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('p')
->orderBy('p.id', 'ASC');
},
'choice_label' => 'Title',
'placeholder' => 'All types',
'required' => false,
])
->add('Search', SubmitType::class)
;
}
In the controller I have this
$query = $request->query->get('searchfilter');
$querybuilder = $em->getRepository(Product::class)
->createQueryBuilder('p');
if($query['type'] != NULL) {
$querybuilder->innerJoin('p.producttype', 'b')
->addSelect('b')
->andWhere('b.id like :type')
->setParameter('type', $query['type']);
;
}
and the form in twig
<div class="container">
{{ form_start(form, {'action': path('product_search'), 'method': 'GET'}) }}
{{ form_widget(form) }}
{{ form_end(form) }}
</div>
Thanks in advance!

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',
));

Collection of form embedded is showing the whole form insetead of a single field

I'm following the official documentation example but I can't make it to work properly.
Summarizing, I have material and items_budget table. Both of them are related as oneToMany and manyToOne, by the id and material fields:
Material.orm.yml:
oneToMany:
itemsBudget:
targetEntity: ItemsBudget
mappedBy: material
ItemsBudget.orm.yml:
material:
targetEntity: Material
inversedBy: itemsBudget
joinColumn:
name: material
referencedColumnName: id
Here, the ItemsBudgetType where I set the material field as a collection:
$builder->add('budget', 'entity', array(
'class' => 'PanelBundle:Budget',
'attr' => array(
'class' => 'form-control',
),
))
->add('material', 'collection', array(
'type' => new MaterialType(),
'allow_add' => true
))
->add('quantity', 'number', array(
'attr' => array(
'class' => 'form-control',
),
))
->add('price', 'money', array(
'attr' => array(
'class' => 'form-control',
),
));
Just for information, the MaterialType:
$builder->add('name', 'text', array(
'attr' => array(
'class' => 'form-control',
),
))
->add('description', 'text', array(
'attr' => array(
'class' => 'form-control',
),
))
->add('current_quantity', 'text', array(
'attr' => array(
'class' => 'form-control',
'required' => false
),
))
->add('price', 'money', array(
'attr' => array(
'class' => 'form-control',
),
));
Here the index.html.twig of my ItemsBudget view:
<strong>Materiais</strong>
<div class="form-group">
<ul class="materials" data-prototype="{{ form_widget(form.material.vars.prototype)|e }}">
{% for material in form.material %}
<li>{{ form_widget(form.material.vars.prototype.name) }}</li>
{% endfor %}
</ul>
</div>
In the view, I have tried also as it is in the example: {{ form_row(material.name) }}, but it still shows the whole Material form.
And where I'm calling them, in the ItemsBudgetController:
public function addAction(Request $request)
{
$form = $this->createForm(new ItemsBudgetType(), new ItemsBudget());
$manager = $this->getDoctrine()->getManager();
if ($request->getMethod() == 'POST') {
$form->handleRequest($request);
if ($form->isValid()) {
$ItemsBudgetEntity = $form->getData();
$manager->persist($ItemsBudgetEntity);
$manager->flush();
$BudgetEntity = $form->get('budget')->getData();
$BudgetEntity->addItemsBudget($ItemsBudgetEntity);
$manager->persist($BudgetEntity);
$manager->flush();
$this->addFlash('success', 'Materiais para o orçamento adicionados');
return $this->redirect($this->generateUrl('panel_budgets'));
}
}
return $this->render('PanelBundle:ItemsBudget:index.html.twig', array(
'form' => $form->createView(),
'budgets' => $manager->getRepository('PanelBundle:Budget')->findAll()
));
}
The JavaScript is the same from the example too:
function addMaterial($collectionHolder, $newLinkLi) {
var prototype = $collectionHolder.data('prototype');
var index = $collectionHolder.data('index');
var newForm = prototype.replace(/__name__/g, index);
$collectionHolder.data('index', index + 1);
var $newFormLi = $('<li></li>').append(newForm);
$newLinkLi.before($newFormLi);
}
var $collectionHolder;
var addMaterialLink = $('Mais');
var $newLinkLi = $('<li></li>').append(addMaterialLink);
jQuery(document).ready(function () {
$collectionHolder = $('ul.materials');
$collectionHolder.append($newLinkLi);
$collectionHolder.data('index', $collectionHolder.find(':input').length);
addMaterialLink.on('click', function (e) {
e.preventDefault();
addMaterial($collectionHolder, $newLinkLi);
});
});
This is the issue: instead of showing only the name field from Material form, it is showing the whole form every time I click the "Mais". Am I missing something?
The javascript takes the content of the data-prototype attribute and appends it to the html of the form. The attribute in your template contains {{ form_widget(form.material.vars.prototype)|e }} which is the html prototype of the form.
Try:
<ul class="materials" data-prototype="{{ form_widget(form.material.vars.prototype.name) }}">

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

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))
)
);