In most of the cases, tables without foreign relations in a database is far from the reality. This means that you will have always at least one foreign relation from a table to another, ignoring the type of project. In the Many-To-Many Relationship, implemented with Doctrine in your Symfony project can be a headache, however if you have discovered how to implement correctly, you surely have an EntityType field in your form. For this article we'll create a simple example with the relationship Games to Categories, in this case every Game can be related to many Categories, which means that in our Games form, we'll give the user the possibility to choose to which categories is the game related, therefore our Entity Field will be rendered as multiple checkbox and obviously multiple selections are allowed:
<?php
namespace AppBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\Common\Collections\ArrayCollection;
use Symfony\Component\Form\FormInterface;
// Add reference to the Categories Entity and EntityType field
use AppBundle\Entity\Categories;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
class GamesType extends AbstractType
{
// ... //
public function buildForm(FormBuilderInterface $builder, array $options)
{
// ... //
// Add the field "Categories" to the GamesForm
$form->add('Categories', EntityType::class, [
// Multiple selection allowed
'multiple' => true,
// Render as checkboxes
'expanded' => true,
// This field shows all the categories
'class' => Categories::class,
'mapped' => false
]);
// ... //
}
// ... //
}
In our Twig Form of the Games Entity, we could easily render the field using form_row
or rendering the entire form once with Twig:
{{ form_start(games_form) }}
{#
Render all widgets of the Form
#}
{{ form_widget(games_form) }}
<input type="submit" value="Create Game" />
{{ form_end(games_form) }}
But this would produce the following output in our project:
According to your CSS or design rules, you don't want this because it's pretty ugly, for you and for the user. In our project, every category has an icon (image) and we want to be able to display some kind of panel with the same checkbox but that displays the name of the category and the icon as well and for that, we need to retrieve every entity of every checkbox.
Retrieving Entity Object with Twig
The first that you need to know is that when you work with an EntityType field that is expanded and allows multiple selections, is that the default widget renders a group of checkbox, so you can iterate over them with a for loop. As in our GamesForm
the Category
field is named as "Categories", we could render every single widget as:
{% for CategoryField in GamesForm.Categories %}
<div>
{{ form_row(CategoryField) }} <br>
</div>
{% endfor %}
However it stills pretty ugly (although we know how to customize it now):
As previously mentioned, every Category Entity has a property namely icon that stores the name of the icon of every category, so we could generate a fancier widget to select the category in the form retrieving its entity to know which is the Icon of every Entity. This can be achieved as follows:
{% for CategoryField in GamesForm.Categories %}
{# store index of the category in a variable #}
{% set index = CategoryField.vars.value %}
{# get entity object from its index in the Categories Field #}
{% set entity = GamesForm.Categories.vars.choices[index].data %}
{#
The entity variable contains a CategoryEntity with all its properties.
So we can now render
#}
<div class="col-md-4">
<div class="nk-feature-1">
<div class="nk-feature-icon">
<!-- Render the Icon of the Category -->
<img src="{{ asset('uploads/icons/' ~ entity.icon ) }}" alt="">
</div>
<div class="nk-feature-cont">
<h3 class="nk-feature-title">
{{ form_label(category_type) }}
</h3>
<h3 class="nk-feature-title text-main-1">
{{ form_widget(category_type) }}
</h3>
</div>
</div>
</div>
{% endfor %}
Which in our project would generate the following widgets:
Looks better right? As you can see, the entity of every checkbox of the EntityType field can be retrieved from the form.FieldName.vars.choices
object that contains the choices of the field. To obtain the correct entity you just need to specify the index of the choices array and retrieve the entity from the data
property.
Happy coding !