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
Related
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.
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
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
How can I get multiple Cehckboxes instead a choice field in Symfony? Actually, I'm using:
->add('usergroups', 'entity', array('class' => 'PrUserBundle:Group','property' => 'name','required' => true))
This will output a select-Field..
In this case, it would be better to output checkboxes as it is easier to handle for the user.
http://symfony.com/doc/current/reference/forms/types/checkbox.html at, there is nothing helpful about multiple select....
Do I have to build an array and place it by myself?
Use the expanded option for choice and entity fields.
->add('usergroups', 'entity', array('class' => 'PrUserBundle:Group','property' => 'name','required' => true, 'expanded' => true,))
I have a scenario like this:
tracks ManyToMany instruments
I create the form in this way:
$builder
->add('instruments', 'entity_hidden', array(
'attr' => array('class' => 'one_req'),
'label' => 'form.instrument',
'translation_domain' => 'label',
'required' => false,
'class' => 'Acme\DemoBundle\Entity\Instrument'
))
"hidden_entity" is given by a custom transformer that you can find here: gist
although I believe that is not the problem.
The problem is that the user may not even choose any instrument (such asamong other specified with "required" => false).
When I go to save my returns this error:
Neither the property 'instruments' nor one of the methods 'setInstruments()',
'__set()' or '__call()' exist and have public access in
class 'Acme\DemoBundle\Entity\SoundtrackVersion'.
I do not understand why ..
Where am I doing wrong?
It sounds very obvious, but error can not lie. Check that Acme\DemoBundle\Entity\SoundtrackVersion entity has property instruments and methods setInstruments() and getInstruments().