Symfony 4 - Difficults with ChoiceType function - symfony

I've a Symfony 4 project.
My users can submit holidays.
There are several types of leave (illness, overtime, etc.), and for each of these types, they have a balance.
When they submit a leave, they must choose from the list of leave types the type they wish. But I want to display in this list only those whose balance is greater than 0.
So, in my form I've:
->add('typeConge', EntityType::class, [
'class' => TypeConge::class,
'label' => false,
'placeholder' => "Type d'absence",
'choice_label' => 'nom',
])
And in my User entity, I've the function to take only typeConges with positive balance:
public function getSoldesActif()
{
$soldesTypesConge = $this->soldeConges;
foreach ($soldesTypesConge as $solde) {
if ($solde->getSolde() == 0) {
$soldesTypesConge->removeElement($solde);
}
}
return $soldesTypesConge;
}
But even with the documentation, I don't understand how can I affect this list ?
Can someone help me please ?

You can use the query_builder-option on the entity type to specify which entities are displayed.
It should probably look roughly, something like this:
->add('typeConge', EntityType::class, [
'class' => TypeConge::class,
'label' => false,
'placeholder' => "Type d'absence",
'choice_label' => 'nom',
'query_builder' => function(EntityRepository $repo) {
$builder = $repo->createQueryBuilder('typeConge');
return $builder->where('typeConge.solde > 0');
},
])
The query builder is responsible for which entities are fetched and then displayed in the list. By default it will fetch all entities and with the query_builder-option you can restrict this to your liking using the ORM-query builder from Doctrine.
There is also an example in the docs, you can use as reference: https://symfony.com/doc/current/reference/forms/types/entity.html#ref-form-entity-query-builder

Related

Form of CollectionType implementing EntityType in ManyToMany relation: Duplicate record is added to database table when 'multiple' => false

I have 2 classes with many to many relationship: Foo (owning side) and Bar (inverse side). My form is CollectionType (of Foo) implementing EntityType (of Bar) (add Bar into Foo). This is some code for my form
FooType:
->add('Bars', CollectionType::class, array('entry_type' => BarType::class, 'allow_add' => true, 'allow_delete' => true,
'by_reference' => false))
BarType
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('text', EntityType::class,
array(
'class' => Bar::class,
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('bar')
->orderBy('bar.text', 'ASC');
},
'choice_label' => 'text',
'by_reference' => false,
)
)
;
}
if i set 'multiple' => true, there is no problem
if i set 'multiple' => false (dont ask me why, this is a requirement from work: add a plus and minus button for the form. When plus button is click, the options from Bar.text are shown, and user can select select only one from those options. When minus button is clicked, undo the plus action), relationships are established but a duplicate record of selected option is added to Bar table.
I would like to ask:
Why does it happen? Is that the way symfony supposed to works?
Is there any fix/workaround for it?
According to this github disccussion of Symfony, you are supposed to use 'multiple' => true to make it work
I am using Symfony 4.4, doctrine bundle 2.6.3
Edit:
I would like to clarify this: my goal is to select multible options loaded from Bar->$text. But instead of selecting multiple options in one run (by setting multiple true), i would like to archieve it by adding many times (with a plus button), and each time only one option can be selected.
The reason I am doing this is that i am migrating from many to one to many to many, and I do not want to change the current UI (select one at a time) to multiple select.

Make Symfony forms use simple GET names

I am currenly creating a simple search form, using Symfony 4.1's form builder. Rendering works great, but I lack a bit of control on the way the request is formed.
The generated request looks like:
http://localhost:8000/fr/search?advanced_search%5Bquery%5D=test&advanced_search%5Bcategory%5D=1&advanced_search%5BverifiedOnly%5D=&advanced_search%5Bsave%5D=
And I want it to look like:
http://localhost:8000/fr/search?query=test&category=1&verifiedOnly=
The code I'm using:
$this->ff->create(
AdvancedSearchType::class,
null,
['csrf_protection' => false]
)->createView()
And
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->setMethod('GET')
->add('query', SearchType::class,
[
'attr'=> [
'placeholder' => 'search.query'
]
]
)
->add('category', EntityType::class,
[
'class' => Category::class,
'label' => 'search.category',
'group_by' => function(Category $category, $key, $value) {
// Code...
return $category->getName();
}
]
)
->add('verifiedOnly', CheckboxType::class,
[
'label' => 'search.verifiedOnly',
'required' => false
]
)
->add('save', SubmitType::class,
[
'label' => 'search.submitButton'
]
);
}
The reason behind this is because it generates widgets like this
<input .. name="advanced_search[query]" ..>
When I want them like this
<input .. name="query" ..>
Is there any way to change this? Thank you!
When you are using the shortcut method provided by Symfony's ControllerTrait, the name of the generated form will be automatically derived from the form type's block prefix.
You can change the implicit name for all forms based on this type by overriding the getBlockPrefix() method in your form type:
public function getBlockPrefix()
{
return '';
}
Or you decide to change the name for just one particular form by giving its name explicitly making use of the form factory that is used under the hood by the controller trait's shortcut methods like this:
$form = $this->get('form.factory')
->createNamedBuilder('', AdvancedSearchType::class, null, [
'csrf_protection' => false,
])
->getForm();
But you need to be careful now. If there is more than one form without a name handled during the same request, the component is not able to decide which of these forms has been submitted and will act as if both had been so.

Symfony: how to add a sub set of items from big collection to entity type?

A car machine oil is applicable to several car models, but in my case, there are hundreds of car models in our system, I don't want to load them all to a page, then , let a user to choose specific car models, it would be better to use ajax call to get car model by car brand , car series. the user choose a sub collection of items , post these selected to server.
In the form type ,I add a form field like this below, as we know, if I don't set its option choices as empty array, the entity field will get all car models , which will result in huge performance penalty.
->add('applicableModels', 'entity', array(
'class' => 'VMSP\CarBundle\Entity\CarModel',
'choices'=>array(),
'multiple'=>true,
'property_path' => "modelName",
'label' => 'vmsp_product.product.form.applicable_model',
)
)
So, how do you add a sub set of items from a big collection , and assign these selected items to entity type?
The simple answer is that you can define a custom query builder for each element, you add to the form, i.e.
$data = $builder->getData();
\\...
->add('applicableModels', 'entity', array(
'class' => 'VMSP\CarBundle\Entity\CarModel',
'multiple' => true,
'property_path' => "modelName",
'label' => 'vmsp_product.product.form.applicable_model',
'query_builder' => function (EntityRepository $er) use ($data) {
$qb = $er->createQueryBuilder('e')
->where('e.manufacturer IN (:manufacturer)')
->setParameter('manufacturer', $data['manufacturer']);
return $qb->orderBy('e.name');
},
))
So you may have a sort of a "wizard" where user select a manufacturer, the page reloads, and the cars shown are only a subset.
In a very similar situation I ended up doing things a little differently, though. In the form, the choices are set to an empty array. On frontend, the options are filled via Ajax calling a separate API. Then on the form "pre-submit" event, I fill the field with the selected option.
$builder->addEventListener(
FormEvents::PRE_SUBMIT, function (FormEvent $event) {
$form = $event->getForm();
$data = $event->getData();
if (!empty($data['applicable_model'])) {
$form->add('applicable_model', 'entity', array(
'class' => 'VMSP\CarBundle\Entity\CarModel',
'query_builder' => function (EntityRepository $er) use ($data) {
$qb = $er->createQueryBuilder('e')
->where('e.id IN (:applicable_model)')
->setParameter('applicable_model', $data['applicable_model']);
return $qb;
},
));
}
Update: I learned that the addEventListener part can probably be replaced by a DataTransforme instead, see https://stackoverflow.com/a/31938905/410761

Add roles in FOSUserBundle

I feel the same as a forum user who posted this:
I implemented FOSUserBundle, and I want to add to RegisrationFormType roles that are taken from a table. When I had it like this:
->add('roles', 'choice', array('label' => 'Rol', 'required' => true,
'choices' => array( 'ROLE_ADMIN' => 'ADMINISTRADOR','ROLE_SUPERADMIN' => 'SUPERADMINISTRADOR',
'ROLE_USER' => 'USUARIO'), 'multiple' => true))
And it works! But they must leave the BD, I can not put the Entity field because roles should be an array, not an object. How I can generate the array with the roles taken from a table? In FosUSerbundle as you would add roles?
Thanks ....
I write because that user had no answer. I followed [the steps of official documentation] (https://github.com/FriendsOfSymfony/FOSUserBundle/blob/master/Resources/doc/index.md) and adding the above lines in the register of FOSUserBundle works, but I want to work this from the database.
And then I used to create groups this. Two additional tables were created and even now joined a group or role in the list, but not how to show the login to register a new user.
Has anyone solved it?
So you have the roles in a table? You could inject the EntityManager in the form type and use it to fetch the choices.
->add('roles', 'choice', array(
'label' => 'Rol',
'choices' => $this->getRoles(),
'multiple' => true,
'required' => true,
))
Then create a method which gives you the data the way you need.
Something like:
public function getRoles()
{
$roles = array();
$er = $this->em->getRepository('AppBundle:Role');
$results = $er->createQueryBuilder('r')
// conditions here
->getQuery()
->getResult();
// process the array?
foreach ($results as $role) {
$roles[$role->getId()] = $role->getLabel();
}
return $roles;
}

How to validate two instances of the same entity?

Use case
I am learning Symfony2 and am creating a Table Tennis tracking app to learn the framework. I have configured my entities as follows.
Player 1..n Result n..1 Match
On my form I'd like to validate that the scores for a match are correct.
Implementation
Match has an ArrayCollection() of results.
My MatchType and ResultType forms contain the following.
// Form\MatchType
$builder->add('matchType', 'entity', array(
'class' => 'PingPongMatchesBundle:MatchType',
'property' => 'name',
)
)
->add('results', 'collection', array(
'type' => new ResultType(),
'allow_add' => true,
'by_reference' => false,
)
)
->add('notes');
// Form\ResultType
$builder->add('player', 'entity', array(
'class' => 'PingPongPlayerBundle:Player',
'query_builder' => function(EntityRepository $er) {
return $er->createQueryBuilder('p')
->orderBy('p.firstName', 'ASC');
},
))
->add('score');
Issue
I need to be able to validate the scores. However I'm not sure how to approach this type of validation as I need to compare two instances of my Result#score in order to know if they are valid or not.
Is anyone able to suggest a method or approach that I could use in order to be able to compare Result#score between the two different instances? Can I validate the ArrayCollection in the Match entity for example?
You could creat a custom validator constraint on Match entity.
http://symfony.com/doc/2.0/cookbook/validation/custom_constraint.html
Take a look in Callback constraint:
http://symfony.com/doc/2.1/reference/constraints/Callback.html

Resources