Symfony transform ChoiceType in CheckboxType - php

I've problem with my form type. I have an entity activity and an other entity class. It's in ManyToMany. When I display the form, it's in ChoiceType, but I want it to be in CheckboxType. So I've :
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('libelle')
->add('horraire')
->add('horraireDebut')
->add('horraireFin')
->add('description')
->add('classes');
}
It display a ChoiceType but I want a CheckboxType, so I changed this to :
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('libelle')
->add('horraire')
->add('horraireDebut')
->add('horraireFin')
->add('description')
->add('classes', CheckboxType::class);
}
But this only displays one checkbox while I have several recordings (which appears well with the first code).
My form.html.twig :
<div class="form-group{% if form.classes.vars.errors|length %} has-error{% endif %}">
<label for="{{ form.classes.vars.id }}" class="col-sm-3 control-label no-padding-right required">Classes <span class="red">*</span></label>
<div class="col-sm-9">
{{ form_widget(form.classes,{'attr': {'class': 'form-control'}}) }}
{{ form_errors(form.classes) }}
</div>
How can I get a checkbox line or a checkbox dropdown ?
Thanks!

Just use multiple and expanded options together to achieve this (Ref):
$builder->add('classes', null, array(
'multiple' => true,
'expanded' => true,
));
Then checkboxes will be rendered.
Note that null value mean EntityType::class in case you use Doctrine ORM. Otherwise, use EntityType::class and 'class' => Entity::class option.

In order to achieve what you're looking for, you could also try the EntityType with a query builder:
->add('classes', EntityType::class, array(
'by_reference' => true,
'multiple' => true,
'expanded' => false,
'class' => 'AppBundle\Entity\Class',
'property' => 'name',
'query_builder' => function(\Doctrine\ORM\EntityRepository $er) {
$qb = $er->createQueryBuilder('c');
return $qb->orderBy('c.name', 'ASC');
}
))

Related

Symfony 3 - Dynamic Form - ChoiceType Ajax

I am currently working on a user creation form.
A user has a profile attribute:
/**
* Many Users have One profile
* #ORM\ManyToOne(targetEntity="ProjectBundle\Entity\User\Profile", inversedBy="users")
* #ORM\JoinColumn(name="profile_id", referencedColumnName="id")
*/
private $profile;
This profile is chosen according to another select (on change action jquery) :
{% autoescape 'html'%}
{{ '<script id="tmpl_user_profile" type="text/x-jquery-tmpl">
<option value="${id}">${libelle}</option>
</script>'|raw }}
{% endautoescape %}
<script>
$('select#user_organisationMember').on('change',function(){
var value = this.value;
if (value == '') {
$('#user_profile').empty();
}
var urlAjax = "{{ path('admin_user_get_profile', { 'entity': "value" }) }}";
$.ajax({
url: urlAjax.replace("value",value),
method: "post"
}).done(function(msg){
$('#user_profile').empty();
$('#tmpl_user_profile').tmpl(JSON.parse(msg)).appendTo('#user_profile');
}) ;
});
</script>
Until then everything worked correctly !
The different profiles in the select tag changes well according to the other select.
Upon arrival on the page, I want the list of profiles to be empty.
So I adapted my form using Symfony's FormEvent.
This is my first use of FormEvent, I may have made a mistake!
My FormType :
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('lastname')
->add('firstname')
->add('gender',null,array('required'=>true))
->add('organisationMember',null,array(
'required' => true,
'choices' => $options['organisation'],
'group_by' => 'type_organisation',
'placeholder' => 'Choisissez votre organisation'
))
->add('job')
->add('mobile')
->add('phone')
->add('alert_failure')
->add('alert_full')
->add('alert_other')
->add('plainPassword', TextType::class, array('required' => true))
->add('email')
->add('profile', null, array(
'required' => true,
));
$builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) {
$entity = $event->getData();
$form = $event->getForm();
if (!$entity || null === $entity->getId()) {
$form->remove('profile');
$form->add('profile', ChoiceType::class);
}
});
}
By default all the profiles of my database are loaded in the select to not get the error 'This value is incorrect'.
But I do not want the user to see all the profiles, so I remove it in the event and return the field empty.
But I still get the error 'This value is incorrect' because actually, since the base select is empty, the form does not found the value entered.
I would like to have a select which by default is empty, which is filled in Ajax and which does not show me the error 'This value is incorrect'.
How can I please do it?
Thanks
Add field not only on PRE_SET_DATA, but also on PRE_SUBMIT event. Check my answer on similar question.
I found a very simple solution, I do not know if it's a good idea, but it takes not a lot of lines.
I have delivered the default form
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('lastname')
->add('firstname')
->add('gender',null,array('required'=>true))
->add('organisationMember',null,array(
'required' => true,
'choices' => $options['organisation'],
'group_by' => 'type_organisation',
'placeholder' => 'Choisissez votre organisation'
))
->add('job')
->add('mobile')
->add('phone')
->add('alert_failure')
->add('alert_full')
->add('alert_other')
->add('plainPassword', TextType::class, array('required' => true))
->add('email')
->add('profile', null, array(
'required' => true,
'placeholder' => 'Choose an organisation !',
));
}
And on my view:
<div class="form-group{% if form.profile.vars.errors|length %} has-error{% endif %}">
<label for="{{ form.profile.vars.id }}" class="col-sm-3 control-label no-padding-right required">Profile(s) </label>
<div class="col-sm-9">
{% do form.profile.setRendered %}
<select name="user[profile]" id="user_profile" required class="form-control">
//We can do a loop if needed
</select>
{{ form_errors(form.profile) }}
</div>
</div>
This way I fully manage whether or not I want information in my select.
And it works for me.

Symfony 3.1.3: Form submit button becomes unresponsive (not disabled) after isValid() errors are triggered and rendered

I have created a form to invite new users. The form has no problem submitting and being handled correctly, until isValid() method returns errors after a submission. When this happens, the page is re-rendered correctly with the appropriate errors being shown. Unfortunately, the submit button then becomes un-responsive: It is still styled correctly (the style changes when it is disabled via jQuery, so that doesn't seem to be the problem), the correct submission URL still appears at the bottom-left of the navigator, but nothing happens when it is clicked.
The UserInviteType form building class:
class UserInviteType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('userBackground', ChoiceType::class, array(
'choices' => array(
'Professeur' => 'professor',
'Étudiant' => 'student',
'Employé de laboratoire' => 'labEmployee',
'Employé administratif' => 'adminEmployee',
'Autre' => 'other'
)))
->add('firstName', TextType::class)
->add('lastName', TextType::class)
->add('email', EmailType::class)
->add('misc', TextType::class, array(
'required' => false,
))
->add('level', EntityType::class, array(
'required' => false,
'class' => 'AspProfessorProfileBundle:Level',
'choice_label' => 'value',
'multiple' => false,
))
->add('canModify', CollectionType::class, array(
'entry_type' => CanModifyInitType::class,
'allow_add' => true,
'allow_delete' => true,
'prototype' => true,
'required' => true,
))
->add('save', SubmitType::class, array(
'disabled' => 'false',
))
;
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Asp\UserBundle\Entity\UserInvite',
'validation_groups' => array(
'Asp\UserBundle\Entity\UserInvite',
'determineValidationGroups'
),
));
}
}
The controller which handles page rendering and form submission:
public function usersAction(Request $request)
{
$user = new UserInvite();
$form = $this->get('form.factory')->create(UserInviteType::class, $user);
/* retreive all users - for user list rendering*/
$userManager = $this->get('fos_user.user_manager');
$users = $userManager->findUsers();
if ($request->isMethod('POST') && $form->handleRequest($request)->isValid()) {
$em = $this->getDoctrine()->getManager();
$data = $form->getData();
if ($data->getUserBackground() == 'professor' || $data->getUserBackground() == 'adminEmployee' || $data->getUserBackground() == 'other') {
$canMod = $user->getCanModify();
foreach ($canMod as $cM) {
$user->removeCanModify($cM);
}
}
$em->persist($user);
$em->flush();
$request->getSession()->getFlashBag()->add('notice', 'Invitation envoyé à '.$user->getFirstName().' '.$user->getLastName().'('.$user->getEmail().').');
return $this->redirectToRoute('asp_core_admin_users');
}
return $this->render('AspCoreBundle:Admin:users.html.twig', array(
'users' => $users,
'form' => $form->createView()
));
}
The Twig view rendering of the form:
<div class="user-init-form-container">
{{ form_start(form, {'attr': {'class': 'form-horizontal'}}) }}
{{ form_errors(form) }}
{# ....... Declare other form elements ....... #}
<div class="form-group">
<div class="col-sm-offset-3 col-sm-8 col-lg-offset-2 col-lg-9">
{{ form_widget(form.save, { 'id': 'submit_button', 'label': 'Inviter', 'attr': {'class': 'btn btn-primary'}}) }}
</div>
</div>
{{ form_end(form) }}
</div>
I do not do any jQuery interaction with the submit button directly (except when I wanted to see the disabled button styling to confirm this wasn't the problem).
I have looked around quite a bit and can't seem to find any topic anywhere discussing this. Hope one of you can spot what I am doing wrong, because I am really stumped right now!
Thank you.
Alex S.
Try to specify form action ?
{{ form_start(form, {'attr': {'id': 'myId'}, 'action': path('my_route')}) }}
Other methods if you prefer http://symfony.com/doc/current/form/action_method.html
I Figured it out.
It was due to a hidden field which was in-properly rendered after error generation. This hidden field suddenly became required and would prevent the submission of the form.
Thank you.
Alex

how to delete embedded form (collection field) label in symfony

I have this form in my symfony application:
namespace MyNamespace\EntityBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class OrganizationType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
// profession checkboxes imbrication
->add('professions', 'collection', array(
'type' => new ProfessionType(),
'allow_add' => true,// if unrecognized items are submitted to the collection, they will be added as new items
'allow_delete' => false,
'by_reference' => false, //in order that the adders are called.
'mapped' => true,
))
->add('name')
->add('siret')
->add('corporation')
;
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'MyNamespace\EntityBundle\Entity\Organization',
'csrf_protection' => true,
'csrf_field_name' => '_token_',
// a unique key to help generate the secret token
'intention' => 'organization_stuff',
));
}
/**
* #return string
*/
public function getName()
{
return 'organization';
}
}
And this how I render the form in my twig view:
<div>
{{ form_start(form, {'action': path('path_action'), 'method': 'POST'}) }}
{{ form_errors(form) }}
{{ form_row(form.professions.vars.prototype) }}
{{ form_row(form.name) }}
{{ form_row(form.siret) }}
{{ form_row(form.corporation) }}
{{ form_end(form) }}
</div>
It renders me this in my html view on my browser:
As you can see I have a required label named __name__label__ (at the top of the form) and the embedded form label Professions above the submit button.
How can I fix that, or customize this behavior ?
Note: in my twig if I only use {{ form_row(form.professions) }}, my professionType does not display the fields.
This is the code of ProfessionType.php :
$builder
->add('production', 'checkbox', array('required' => false ))
->add('transport', 'checkbox', array('required' => false ))
->add('pumping', 'checkbox', array('required' => false ))
;
I think you are having those labels because you have used the default view format predefined by symfony you need to customize it , the other reason is that you have displayed the embedded form prototype, you need to set this prototype as data type attribute :
<ul class="tags" data-prototype="{{ form_widget(form.tags.vars.prototype)|e }}">
...
See http://symfony.com/doc/current/cookbook/form/form_collections.html

How to control marked checkbox order in twig

So I'm facing this weird behavior when listing a series of checkboxes with twig
see the new page, it displays everything right, and saying that i'm saving this laboratory
img1
ok, I saved the laboratory, now went to the edit page
img2
the marked checkboxes displays at first, ruining the alphabetic order that was at the new page.
I need that the edit page become exactly like the new page, but I don't know how to organize these checkboxes on twig.
block that renders the checkboxes:
{% block _appbundle_laboratory_laboratoryExams_row %}
<div class="form-group">
{{ form_label(form) }}
<div class="col-md-10 col-md-offset-2">
<div class="app-checkbox-collection">
<p>{{ 'laboratory.field_laboratory' | trans }}</p>
{% dump(form) %}
{% for child in form %}
{% if child.exam.vars.data.type == constant('AppBundle\\Entity\\ExamLaboratory::TYPE')%}
{{ form_widget(child.permission, {
'attr' : {
'class' : 'exam-checkbox'
},
'label' : child.exam.vars.data.name
}) }}
{% endif %}
{% endfor %}
<p>{{ 'laboratory.field_image' | trans }}</p>
{% for child in form %}
{% if child.exam.vars.data.type == constant('AppBundle\\Entity\\ExamImage::TYPE')%}
{{ form_widget(child.permission, {
'attr' : {
'class' : 'exam-checkbox'
},
'label' : child.exam.vars.data.name
}) }}
{% endif %}
{% endfor %}
</div>
</div>
</div>
{% endblock %}
I don't ask at portuguese stackoverflow because no one answers symfony questions there, few users probably
EDIT:
Here is the buildForms:
Laboratory
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name', null, array(
'label' => 'laboratory.name',
'attr' => array('class' => 'focus')
))
->add('leader', null, array(
'label' => 'laboratory.leader'
))
->add('city', null, array(
'label' => 'laboratory.city',
'placeholder' => 'action.select_one'
))
->add('laboratoryExams', 'collection', array(
'label' => 'laboratory.laboratoryExams',
'type' => new LaboratoryExamType(),
'allow_delete' => true,
'by_reference' => false,
))
;
}
LaboratoryExam
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('permission', null, array(
'label' => 'laboratoryexam.permission',
))
->add('exam', null, array(
'label' => 'laboratoryexam.exam',
))
;
}
You can set the order of checkboxes while building the form:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('permission', 'entity', [
'class' => 'Acme\AppBundle\Entity\Parameter',
'multiple' => true,
'expanded' => true,
'query_builder'=> function(EntityRepository $repository) {
return $repository->createQueryBuilder('exam')->orderBy('exam.title', 'ASC');
},
]);
}
I would say it has nothing to do with twig.
That would be set on the form type object where the form with checkboxes is constructed.
It seems the data is populated programmatically on form creation with already selected choices being first.
If it isn't the case or you got some problem, please edit your question with formtype code and code where you get the data for the checkboxes.
thanks for the answers, i found a solution by resorting the ArrayCollection in the form.
$iterator = $form->get('laboratoryExams')->getData()->getIterator();
$iterator->uasort(function ($a, $b) {
return ($a->getExam()->getName() < $b->getExam()->getName()) ? -1 : 1;
});
$form->get('laboratoryExams')->setData(new ArrayCollection(iterator_to_array($iterator)));
IMO this should be done by default, I believe that no one would like to have the check-boxes disordered that way

Symfony2, How to make a form label class/attr different than its input?

I would like to build a form with label and inputs, but the class of them should be different. Code below creates the label for the input with same attr:
public function buildForm(FormBuilder $builder, array $options)
{
$builder
->add('hours', null ,
array('attr'=>
array(
'placeholder'=>'Working Hours',
'class'=>'ui-spinner-box') ) )
}
In my code above the ui-spinner-box will be outputted for both label and input. It will even put placeholder for its label.
So how to make it create attr for label separately so I can output something like below :
<label class="MYCLASSFOR_LABEL" for="input_id">Hours</label>
<input class="MYCLASSFOR_INPUTS" type="text" id="input_id" name="" value="" >
As mentioned in the documentation:
attr : A key-value array that will be rendered as HTML attributes on the field
label_attr: A key-value array that will be rendered as HTML attributes on the label
You can set those attributes in twig template or in form builder:
Twig template:
for symfony 2.1 and newer use:
{{ form_label(form.hours, null, {'label_attr': {'class': 'foo'}}) }}
in the legacy symfony 2.0 it used to be
{{ form_label(form.hours, { 'label_attr': {'class': 'MYCLASSFOR_LABEL'} }) }}
{{ form_widget(form.hours, { 'attr': {'class': 'MYCLASSFOR_INPUTS'} }) }}
Form builder
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('hours', null, array(
'label_attr' => array('class' => 'MYCLASSFOR_LABEL'),
'attr' => array('class' => 'MYCLASSFOR_INPUTS'),
));
}
This may be new, but there's an easy way to do this now:
$builder
->add('hours', null , array(
'attr'=>
array(
'placeholder'=>'Working Hours',
'class'=>'MYCLASSFOR_INPUTS')
) ,
'label_attr' => array(
'class' => 'MYCLASSFOR_LABEL'
)
);
The option you're looking for is label_attr.
This works for me in Symfony 2.3:
{{ form_row(form.hours, { 'label': 'Hours:'
,'label_attr': {'class': 'MYCLASSFOR_LABEL'}
,'attr': {'class': 'MYCLASSFOR_INPUTS'}
}
)
}}
The above is no longer correct, at least in the context I was using it. In Symfony 2.1 the solution is:
{{ form_label(form.item, label|default(null), { 'label_attr': { 'class': 'MYCLASS' } }) }}

Categories