How to use the ChoiceListInterface in Symfony 2? - forms

I'd like to display a dynamic list of checkboxes in a form.
So far, I built a form embedding a static list of checkboxes, and I created a Tag entity for different values in different languages and populated the database. I'd like to replace the static checkboxes by a dynamic list based on the Tag entity.
The documentation says I should use the ChoiceListInterface. But it is really poorly documented. Would you have an example or a global logic explanation to help me ?

You can extend LazyChoiceList abstract class and implement loadChoiceList() method, create a service of it, inject it to the form and set it as choice_list option.

Finally, I used an entity field type :
->add('tags', 'entity', array(
'class' => 'bndMyBundle:Tag',
'query_builder' => function(EntityRepository $er){
return $er->createQueryBuilder('t')
->orderBy('t.en', 'ASC');
},
'expanded' => true,
'multiple' => true,
'property' => 'en',
))
Then, I just need to replace the 'en' value by the user's current locale to choose the right language.

Related

How to have the placeholder translated in an EntityType?

I'm building a form which has an EntityType element, the problem I have is that I'm unable to have the placeholder translated.
Here is my code:
$builder
->add('Products', EntityType::class, [
'mapped' => false,
'expanded' => true,
'multiple' => false,
'required' => false,
'class' => Product::class,
'choices' => $options['step']->getProducts(),
'placeholder' => 'form.mat.alreadyOwned',
'label' => 'form.mat.alreadyOwned',
'translation_domain' => 'messages'
])
When I use the form.mat.alreadyOwnedtranslation as the label it works fine, but I would like to use it in the placeholder instead. What am I missing?
Looking forward to any tips or tricks that you have to offer!
[UPDATE]
As pointed out by #gp_sflover I'm not trying to replace the general placeholder, but the one for the empty value. This one only appears if you set required to false.
After some research and thought, the places where the placeholder is actually used are quite limited in number (as it should be). However, specific placeholder translation is not a special case (sadly).
For every choice in a ChoiceType a ChoiceView is added. Also for the placeholder a ChoiceView is added, that inherits the options of the form (which is a somewhat sensible choice for the ChoiceType), including the translation_domain parameter, which indicates that the choices shall be translated (all of them). There's also a reference in some twig template that specifically manages the translation in the twig bridge for non-expanded choice types. However, those don't open up a specific best practices answer on how to specifically handle translations for the placeholder in the ChoiceType.
For the EntityType, this doesn't change.
So there are a few approaches, some of which might be absolutely utility-free ...
translate the placeholder right there when building the form
Although this is conceptually not the most beautiful option, IMHO it's still the most practical one. Essentially, in Symfony 4 forms can receive dependencies in their constructor via auto-wiring, so injecting a TranslatorInterface will open up the ability to translate the placeholder with the requests locale (which is automatically set for the translator via event listener):
public function __construct(TranslatorInterface $translator) {
$this->translator = $translator;
}
and in your buildForm you can then use it to translate the placeholder
$builder
->add('Products', EntityType::class, [
'mapped' => false,
'expanded' => true,
'multiple' => false,
'required' => false,
'class' => Product::class,
'choices' => $options['step']->getProducts(),
'placeholder' => $this->translator->trans('form.mat.alreadyOwned'), // <-- change
'label' => 'form.mat.alreadyOwned',
'translation_domain' => 'messages'
])
not to withhold the other options, which in my opinion are almost all overkill...
set a choice_translation_domain for all entries, including placeholder
obviously, this can and probably will lead to problems, if there ever is an entity that whose choice label matches the key in the translator ... and it is not intended for translation. but it will translate the placeholder with the very same translation_domain ...
adapt the form rendering and check for the placeholder
this is problematic, since you have to assign the form theme/form rendering to all relevant forms. the placeholder does have a unique name ('placeholder', who would have thought) so it could very well be used to achieve that goal. However, setting a different translation domain could very much be challenging. (if you attempt this, it's a bit of a nuisance)
write your own entity type (optionally adding own form rendering)
theoretically, you could create your own EntityType and handle the placeholder properly there. like ... adding a translation domain to the choice view and sub form and what not. For inspiration/reference consult the ChoiceType, EntityType and DoctrineType (parent type).

Symfony: how to place a specific item in first position with query_builder option?

In a form, I use an EntityType field, which allows selection of several items from entity Member. I am sending the id of a specific member to my form through the form's option (a variable named $selfId) and would like to use the query_builder function to return a list of members where this specific member would appear in first position. How could I achieve this? I'm using Symfony 3.
I'm thinking of something like this:
->add('members', EntityType::class, array(
'required' => true,
'label' => 'Members',
'class' => 'AppBundle:Member',
'multiple' => true,
'query_builder' => function (MemberRepository $er) use ($selfId) {
$qb = $er->createQueryBuilder('m');
return $qb
->orderBy('m.id = :selfId') // invented code!!!!!!!
->setParameter('selfId', $selfId)
;
}
))
;
There is an item you can add to the code above item called preferred_choices that should do what you are asking
http://symfony.com/doc/current/reference/forms/types/entity.html#preferred-choices

Symfony - Combine two properties in a single entity form field

I have an entity for publications with many different fields as booktitle, conference etc. I want to build a search form and one of the feature requests is to combine two search parameters in a single choice field. So far I have something like this in the form builder:
$builder->add('booktitle', 'entity', array(
'required' => false,
'label' => 'Conference/Booktitle',
'property' => 'booktitle',
'class' => 'indPubBundle:Publication',
'query_builder' => function(EntityRepository $er) {
return $er->createQueryBuilder('p')
->groupBy('p.booktitle')
->orderBy('p.booktitle', 'ASC');
}
));
Basically I am displaying all booktitles as a choice field. What I want now is to have the conferences as well in the same choice field. Is there a way to achieve that?
The Entity field type is a child of the Choice field type. So you can provide Data via the "choices" parameter. Combine that with a (e.g. repository) method that returns an array with the data you need might be a solution that works for you.
$builder->add('booktitle', 'entity', array(
'required' => false,
'label' => 'Conference/Booktitle',
'class' => 'indPubBundle:Publication',
'choices' => $this->getDoctrine()->getRepository('indPubBundle:Publication')->getData(),
));

Disable some checkboxes in form

I was wondering if there is an easy way of how to disable one checkbox from modifying it by user (Symfony 2.1). I was trying something like this:
$builder->add('adminRoles', 'entity', array(
'property' => 'roleName',
'class' => 'MyBundle:Role',
'query_builder' => function(EntityRepository $er) {
return $er->createQueryBuilder('r')
->orderBy('r.roleName', 'ASC');
},
'disabled' => $this->disabledRoles,
'expanded' => true,
'multiple' => true
));
By $this->disabledRoles I meant an array of IDs of Role entities or entities themselves, but it seems that it just accepts boolean value which is applied for all entities (checkboxes). Thanks for your advice :-)
You will need to add a form listener in order to customize individual elements.
http://symfony.com/doc/current/cookbook/form/dynamic_form_modification.html
It might seem like a lot of work but it's easy enough once you work through the examples. You will end up passing disabledRoles to the listener and then setting the disabled flag accordingly.

Translate select options in Symfony2 class forms

I'm using a class form in Symfony2 Beta3 as follows:
namespace Partners\FrontendBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilder;
class ConfigForm extends AbstractType
{
public function buildForm(FormBuilder $builder, array $options)
{
$builder->add('no_containers', 'choice', array('choices' => array(1 => 'yes', 0 => 'no')));
...
I want to translate the 'yes' and 'no' options, but I don't know how to use the translator here.
You can use the translation resources as usual. This worked for me:
$builder->add('sex', 'choice', array(
'choices' => array(
1 => 'profile.show.sex.male',
2 => 'profile.show.sex.female',
),
'required' => false,
'label' => 'profile.show.sex.label',
'translation_domain' => 'AcmeUserBundle'
));
And then add your translations to the Resources->translations directory of your Bundle.
Update from #CptSadface:
In symfony 2.7, using the choice_label argument, you can specify the translation domain like this:
'choice_label' => 'typeName',
'choice_translation_domain' => 'messages',
Without specifying the domain, options are not translated.
I searched a while to find an answer, but finally I found out how Symfony translates form content. The easiest way in your case seems to be to just add a translation for "yes" and "no" by adding a YAML or XLIFF translation file to your application (e.g. app/Resources/translations/messages.de.yml) or your bundle. This is described here:
http://symfony.com/doc/current/book/translation.html
The problem - in my opinion - is that you don't seem to be able to use custom translation keys. The guys from FOSUserBundle solve this (or a similar) problem with "Form Themes" (http://symfony.com/doc/2.0/cookbook/form/form_customization.html). Here are two significant lines of code to achieve the usage of the form element id as translation key:
https://github.com/FriendsOfSymfony/FOSUserBundle/blob/master/Resources/views/Registration/register_content.html.twig#L1 / https://github.com/FriendsOfSymfony/FOSUserBundle/blob/50ab4d8fdfd324c1e722cb982e685abdc111be0b/Resources/views/form.html.twig#L4
By adding a Form Theme you're able to modify pretty much everything of the forms in the templates - this seems to be the right way of doing this.
(Sorry, I had to split two of the links b/c I don't have enough reputation to post more than two links. Sad.)
In symfony 2.7, using the choice_label argument, you can specify the translation domain like this:
'choice_label' => 'typeName',
'choice_translation_domain' => 'messages',
Without specifying the domain, options are not translated.
CptSadface's answer was what helped me with translating my entity choices.
$builder
->add(
'authorizationRoles',
null,
[
'label' => 'app.user.fields.authorization_roles',
'multiple' => true,
'choice_label' => 'name', // entity field storing your translation key
'choice_translation_domain' => 'messages',
]
);