Forms With Checkboxes For Each Entity in Symfony
(Redirected from Multiple Checkboxes Without an Entity Class in Symfony)
Goal[edit]
To display a list of records, select the records with checkboxes, then apply a batch update to the records when the form is submitted.[1]
Form class[edit]
// src/AppBundle/Form/Type/PendingTutorialOptionType.php
// ...
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class PendingTutorialOptionType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('tutorialId', 'entity', array(
'required' => false,
'class' => 'AppBundle:TutorialTemp',
'property' => 'id',
'property_path' => '[id]',
'multiple' => true,
'expanded' => true
));
$builder->add('update', 'submit', array('label' => 'save selected'));
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => null
));
}
public function getName()
{
return 'pending_tutorials';
}
}
- In the
buildForm()routine,- Use
entityas the field type. - Assign the entity type with the
classoption. - The
propertyandproperty_pathoptions assign the id field of the entity to the checkbox elements. multipleandexpandedoptions set totruecause the form to render with a series of checkboxes as opposed to text fields (or dropdowns if the field type waschoice).
- Use
- Important to set
data_classtonullinsetDefaultOptions(). - Value returned by
getName()is used to identify the form in the Twig template and to collect the form data after it is submitted.
Controller[edit]
// src\AppBundle\Controller\DefaultController.php
//...
public function pendingAction(Request $request)
{
// get all the available options
$pending_options = $this->get('app.pending_tutorial_options')
->getTutorialOptionsList();
// get a form object, passing it the available options
$form = $this->createForm(new PendingTutorialOptionType(), $pending_options);
// process form submission
$form->handleRequest($request);
// check if form data was submitted
if ($form->isValid()) {
// collect selected entities from form data
$data = $form['tutorialId']->getData();
$selected_ids = array();
foreach($data as $tutorial) {
// $data is a collection of PendingTutorial objects with all their data
array_push($selected_ids, $tutorial->getId();
}
// do something with the matching records...
// ...
$form->getData() returns a collection of all the available options in the form. $form['tutorialId']->getData() returns the selected options as entity objects.
N.B. I was having difficulty with isValid() because I was manually inserting the submit buttons in the form, with a name value of form[update]. The form class returns pending_options as its name; the correct value for the submit button's name should have been pending_options[update].
Template[edit]
{{ form_start(form) }}
{{ form_errors(form) }}
<button type="submit" class="btn btn-default" name="form[update]">save selected</button>
{% for tutorial in tutorials %}
{{ form_widget(form.tutorialId[loop.index0] ) }}
{% endfor %}
<button type="submit" class="btn btn-default" name="form[update]">save selected</button>
{% do form.update.setRendered %}{# compensates for manually inserting this multiple times in the form #}
{{ form_end(form) }}
- The checkboxes are rendered with
form_widget().tutorialIdcorresponds to the index of the checkbox collection added to the form builder object.- The individual checkboxes in the collection are accessed through a numeric index. Twig provides the
loop.index0helper.
- The html for the submit buttons are is manually inserted into the template. The Symfony helper functions will only render a form element once to avoid duplicate
idvalues. form_end()will render any fields that have not explicitly rendered in the form already.form.update.setRenderedprevents this.
Processing the form data[edit]
After the call to $form['tutorialId']->getData(), the submitted form data will be accessible through a Doctrine ArrayCollection of entity objects:
object(Doctrine\Common\Collections\ArrayCollection)[1583]
private '_elements' =>
array (size=6)
0 =>
object(AppBundle\Entity\PendingTutorial)[652]
...
1 =>
object(AppBundle\Entity\PendingTutorial)[660]
...
2 =>
object(AppBundle\Entity\PendingTutorial)[664]
...
Examples[edit]
- Pending Tutorials, Tutorial Database
Notes[edit]
- ↑ Build a Form Having a Checkbox For Each Entity in a Doctrine Collection, Stackoverflow
Take note of the following answer that points out how to correctly access the collection in the form data with the current version of Symfony.