Symfony2 form collection with checkboxes - forms

I'm building a form with Symfony2 and need to group some checkboxes. I simply cannot figure out how to pass choices/label along to the checkboxes in BonusGroup.
Form:
$builder->add('groups', 'collection', array(
'type' => new BonusGroup(),
'allow_add' => false,
'allow_delete' => false,
'by_reference' => false
));
BonusGroup():
$builder->add('bonus', 'choice', array(
'choices' => $options['bonus'],
'multiple' => true,
'expanded' => true
));
View.twig:
{% for group in form.groups %}
{{ form_label(group) }}
{% for final in group.bonus %}
{{ form_widget(final) }}
{% endfor %}
{% endfor %}
Passing data to form:
$data = array(
'groups' =>
array ('Group 1 label' => array())
);
$form = $app['form.factory']->createBuilder(new Form(), $data))->getForm();
Any tips?
Thanks!

First, change form_widget to form_row, but it won't work yet because collection type need a piece of JavaScript to work.
See examples here:
http://symfony.com/doc/2.1/reference/forms/types/collection.html

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!

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.

Display repeated field type in Symfony2

I have a password repeated field type in Symfony2 form that goes like this:
->add('password', 'repeated', array(
'type' => 'password',
'invalid_message' => 'Les mots de passe doivent correspondre',
'options' => array('required' => true),
'first_options' => array('label' => 'Mot de passe'),
'second_options' => array('label' => 'Mot de passe (validation)'),
'required' => $bRequired,
'trim' => true,
'constraints' => array(
new Assert\Regex(array(
'pattern' => "/^(?=.*[0-9])(?=.*[a-z])[a-zA-Z0-9!?+]{8,15}$/",
'match' => true,
'message' => "msg"
)),
new Constraints\NotBlank(),
)
))
In twig I'm used to show this field like this:
{% for passwordField in form.password %}
{{ form_row(passwordField,{'attr': { 'class': 'form-control'} }) }}
{% endfor %}
I'm wondering if there is a way to display the password field and the validation field separately so I can display them in different places in my form.
As it is explained in doc:
// in your template.html.twig
/.../
{{ form_row(form.password.first,{'attr': { 'class': 'form-control'} }) }}
{{ form_row(form.password.second,{'attr': { 'class': 'form-control'} }) }}
/.../
where:
The names first and second are the default names for the two sub-fields. However, these names can be controlled via the first_name and second_name options. If you've set these options, then use those values instead of first and second when rendering.

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) }}">

symfony 2, form_rest(form) shows collection fields

My form has 3 collection fields:
$builder->add('affiliates', 'collection', array(
'type' => new AffiliateForm(),
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false,
'options' => array(
'affiliate_types' => $options['affiliate_types'],
'business_types' => $options['business_types'],
),
));
$builder->add('other_businesses', 'collection', array(
'type' => new OtherBusinessForm(),
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false,
));
$builder->add('welfare_activities', 'collection', array(
'type' => new WelfareActivityForm(),
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false,
'options' => array(
'welfare_activity_types' => $options['welfare_activity_types'],
),
));
In the template I show each subform field separately one by one, like below:
<td class="t1c5" >{{ form_widget(affiliate.location) }}
{{ form_errors(affiliate.location) }}</td>
At the end of the form I did:
{{ form_rest(form) }}
But it causes to display the following words at the end of form, when given collection is empty: "Affiliates", "Other businesses", "Welfare activities".
So the question is:
Why those words are displayed on the form?
I can do the following to avoid the above issue:
<div style="display:none;">{{ form_rest(form) }}</div>
Is it correct way of dealing with the problem (maybe i can make a field hidden or whatever)?
Thank you.
those words are displayed on the form because you forget :
{{ form_label(affiliate.location) }}
…
…
According to the doc :
form_rest(view, variables)
This renders all fields that have not yet been rendered for the given form. It's a good idea to always have this somewhere inside your form as it'll render hidden fields for you and make any fields you forgot to render more obvious (since it'll render the field for you).
{{ form_end(form, {'render_rest': false}) }}