How to use Symfony2 form elements individually? - symfony

For example I would like to use ChoiceType in Twig by passing choices, selected value and options.
Something like..
{{ render_select(choices, selected) }}
or
{{ render_choices(choices, selected, {'expanded': true, 'multiple': true}) }}

You shouldn't do it in Twig. The best practice is to creare a Form Class (Type) and define there this type of behaviour.
Example:
class TaskType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('task')
->add('dueDate', null, array('widget' => 'single_text'))
->add('active', 'choiche', array(
'choices' => array('0' => 'Deactive', '1' => 'Active'),
'required' => true,
)
)
->add('save', 'submit');
}
public function getName()
{
return 'task';
}
}
More info in the Symfony2 Docs
If you want to do it anyway, you should do it like this:
{{ form(form.formField, {'expanded': true, 'multiple': true, 'choices': {'0': 'Zero', '1': 'One'}}) }}
As you can see, it's really not easy...

Related

How to set a default value for an EntityType / <select> element in a symfony form?

How to set value in select on twig?
When I submit my form one of the value is null and when dump in the controller show me null.
$builder
->add('date', null, [
'widget' => 'single_text'
])
->add('hairstyle',EntityType::class,[
'class' => Hairstyle::class,
'mapped' => false,
])
;
I add in twig this:
{{ form_start(form) }}
{{ form_widget(form.hairstyle, {value: Setcountry ~ "" } ) }}
{{ form_errors(form) }}
{{ form_end(form) }}
This is my var_dump in controller:
private 'normData' => object(BeautySalonBundle\Entity\Reservation)[824]
private 'id' => null
private 'date' => object(DateTime)[1034]
public 'date' => string '2020-02-21 21:21:00.000000' (length=26)
public 'timezone_type' => int 3
private 'hairstyle' => null
private 'user' => null
private 'viewData' =>
You can leverage the empty_data option to provide a default entity for your EntityType select-field of type Hairstyle.
Inject the repository into your form type
Use the repository in a closure for the empty_data option
Fetch and return the default entity from the repository inside the closure
class YourFormType extends AbstractType
{
private const DEFAULT_HAIRSTYLE_ID = 1;
/** #var HairstyleRepository */
private $hairstyleRepository;
public function __construct(HairstyleRepository $hairstyleRepository)
{
$this->hairstyleRepository = $hairstyleRepository;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
// create an intermediate variable for use in the closure
$hairstyleRepository = $this->hairstyleRepository;
$builder
// ..
->add('hairstyle', EntityType::class, [
'class' => Hairstyle::class,
// return a default Hairstyle entity
'empty_data' => function(FormInterface $form) use ($hairstyleRepository) {
return $hairstyleRepository->getOneById(self::DEFAULT_HAIRSTYLE_ID);
},
])
}

Symfony2 form filter for ManyToMany entity relation

I have 2 entities:
class Exercise
{
//entity
}
and
class Question
{
//entity
}
Their relationship is ManyToMany.
The question is: How I can filter (on the exercise form) the Question entities?
I have this form for Exercise:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('questions', null, array(
'property' => 'name',
'multiple' => true,
'expanded' => false,
'query_builder' => function(EntityRepository $er) use ($options) {
return $er->createQueryBuilder('u')
->where('u.level = :level')
->setParameter('level',$options['level'])
->orderBy('u.dateR', 'ASC');},
))
//other fields
->add('Save','submit')
;
}
I know that I can filter with the query_builder but, how I can pass the var $search?
Should I create another form?
Any help would be appreciated.
Best regards
EDIT: I have found the solution that I want. I put 2 forms on the Controller Action, one form for the entity and one form for filter.
//ExerciseController.php
public function FormAction($level=null)
{
$request = $this->getRequest();
$exercise = new Exercise();
//Exercise form
$form = $this->createForm(new ExerciseType(), $exercise,array('level' => $level));
//create filter form
$filterForm = $this->createFormBuilder(null)
->add('level', 'choice', array('choices' => array('1' => '1','2' => '2','3' => '3')))
->add('filter','submit')
->getForm();
//Manage forms
if($request->getMethod() == 'POST'){
$form->bind($request);
$filterForm->bind($request);
//If exercise form is received, save it.
if ($form->isValid() && $form->get('Save')->isClicked()) {
$em = $this->getDoctrine()->getManager();
$em->persist($exercise);
$em->flush();
return $this->redirect($this->generateUrl('mybundle_exercise_id', array('id' => $exercise->getId())));
}
//If filter form is received, filter and call again the main form.
if ($filterForm->isValid()) {
$filterData = $formFiltro->getData();
$form = $this->createForm(new ExerciseType(), $exercise,array('level' => $filterData['level']));
return $this->render('MyBundle:Exercise:crear.html.twig', array(
'ejercicio' => $ejercicio,
'form' => $form->createView(),
'formFiltro' => $formFiltro->createView(),
));
}
}
return $this->render('juanluisromanCslmBundle:Ejercicio:form.html.twig', array(
'exercise' => $exercise,
'form' => $form->createView(),
'formFiltro' => $filterForm->createView(),
));
}
Templating the forms
On the form.html.twig
{{ form_start(filterForm) }}
{{ form_errors(filterForm) }}
{{ form_end(filterForm) }}
{{ form_start(form) }}
{{ form_errors(form) }}
{{ form_end(form) }}
It works for me and it is that i was looking for.
What you probably want to do here, is to make use of the form builder:
$search = [...]
$form = $this->createForm(new AbstractType(), $bindedEntityOrNull, array(
'search' => $search,
));
here you can provide any list of arguments to the builder method of your AbstractType.
public function buildForm(FormBuilderInterface $builder, array $options)
as you may have guessed at this point you may access the options array throu the $option variable.
As a side note remember to provide a fallback inside the default option array. In your AbstractType you can do something like this:
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'search' => 'some_valid_default_value_if_search_is_not_provided'
));
}
hope it helps, regards.

Form rendering with choice fields terrible performance

I have a variety of choice fields in a form I'm creating, but all of them are loaded from PHP arrays that are defined in code. createForm() is given an empty model. Each field, when rendered with form_row() in twig tacks on about 2 seconds to render, each, making the request take about 8 or 9 seconds, which is ridiculous. I've tried searching, reading, etc. and from what I can tell I am following best practices. There are no database queries being run. Please help me nail down this huge performance problem.
Let's start with the form type:
class SubscriptionType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('latitude', 'hidden')
->add('longitude', 'hidden')
->add('crop', 'choice', [
'choices' => Crop::getFormChoices(),
'required' => true,
])
->add('infliction', 'choice', [
'choices' => Infliction::getFormChoices(),
'required' => true,
])
->add('emergenceDate', 'date')
->add('threshold', 'choice', [
'choices' => Threshold::getFormChoices(),
'label' => 'Severity threshold',
'required' => true,
])
->add('save', 'submit', ['label' => 'Subscribe']);
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults([
'data_class' => 'PlantPath\Bundle\VDIFNBundle\Entity\Subscription',
]);
}
public function getName()
{
return 'subscription';
}
}
Then, in my controller:
public function formAction(Request $request)
{
$subscription = new Subscription();
$form = $this->createForm(new SubscriptionType(), $subscription);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
// ...
}
return $this->render('PlantPathVDIFNBundle:Subscription:form.html.twig', [
'form' => $form->createView(),
]);
}
Subscription/form.html.twig:
{{ form_start(form, {'attr': {'id': 'subscription-form'}}) }}
{{ form_row(form.crop) }}
{{ form_row(form.infliction) }}
{{ form_row(form.emergenceDate) }}
{{ form_row(form.threshold) }}
{{ form_end(form) }}
As I mentioned, each call to form_row() takes a good two seconds. If I take out all the form_row()s and the form_end(), the template is rendered (without a form) in milliseconds. I cannot fathom why Symfony needs over 8 seconds to render several lines of HTML for a blank form.
The blocks for rendering the choice fields in bootstrap_3_layout.html.twig contains '|trans' filter which tries to find translation for each option.
Override choice_widget_options block in your twig file, if you do not wish to translate. This should eliminate the warning messages caused due to translation.

Disabling some checkboxes of choice widget in buildForm()

I have a form widget of type "choice" which is being displayed as a list of many checkboxes. Everything works great. So to stress it out: there is ONE widget, with MANY checkboxes (and NOT several checkbox widgets).
Now, I want to disable some of those checkboxes. The data for that is avaliable in the $options-Array.
Here is the buildForm()-function of my FooType.php
...
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('foo', 'choice', array('choices' => $options['choiceArray']['id'],
'multiple' => true,
'expanded' => true,
'disabled' => $options['choiceArray']['disabled'] // does not work (needs a boolean)
'data' => $options['choiceArray']['checked'], // works
'attr' => array('class' => 'checkbox')))
;
}
...
My Twig-template looks like this:
{% for foo in fooForm %}
<dd>{{ form_widget(foo) }}</dd>
{% endfor %}
I can only disable ALL the checkboxes (by setting 'disabled' => true in buildForm). And passing an array there does not work (as commented in the snippet).
How can I disable some selected checkboxed (stored in $options['choiceArray']['disabled']) of my choice widget?
I have solved the problem using JQuery.
in my FooType.php I stringify the Array of fields that should be disabled.
I pass that string in the buildForm()-Function via a hidden field to the template
there I use JQuery to split the string again into IDs and process disable the checkboxes and grey out the label
Here is the PHP-Code (FooType.php):
...
public function buildForm(FormBuilderInterface $builder, array $options)
{
$disabledCount = sizeof($options['choiceArray']['disabled']);
$disabledString = '';
for ($i = 0; $i < $disabledCount; $i++)
{
$disabledString .= $options['choiceArray']['disabled'][$i];
if ($i < $disabledCount-1)
{
$disabledString .= '|';
}
}
$builder
->add('foo', 'choice', array('choices' => $options['choiceArray']['id'],
'multiple' => true,
'expanded' => true,
'data' => $options['choiceArray']['checked'],
'attr' => array('class' => 'checkbox')))
->add('foo_disabled', 'hidden', array('data' => $disabledString))
;
}
...
Here is the JavaScript part (Twig-template):
function disableModule()
{
var disabledString = $('#foo_disabled').val();
var disabledArray = disabledString.split('|');
$.each( disabledArray, function( disKey, disVal )
{
// deactivate checkboxes
$('input[id="'+idCurrent+'"]').attr("disabled", true);
// grey out label for checkboxes
$('label[for="'+idCurrent+'"]').attr("style", "color: gray;");
});
}
In my Entity/Foo.php I had to add the property "foo_disabled" of type string with setter and getter methods.
This page is first on Goole search results for 'twig checkbox checked and disabled'
To set some checkbox inputs checked or/and disabled in twig, one could use choice_attr
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('email', EmailType::class, ['label'=>'Login: '])
->add('roles', ChoiceType::class, [
'label'=>'Role: ',
'multiple'=>true,
'expanded'=>true,
'choices'=>['User'=>'ROLE_USER','Admin'=>'ROLE_ADMIN'],
'choice_attr'=> [
'User' => ['disabled'=>'disabled', 'checked'=>'checked'],
]
])
->add('password', PasswordType::class, ['label'=>'Password: '])
->add('register', SubmitType::class, ['label' => 'Register'])
;
}
In this example I set checked and disabled checkbox for ROLE_USER as this is default role.

Build a form having a checkbox for each entity in a doctrine collection

I'm displaying an html table for a filtered collection of entities and I want to display a checkbox in each row as part of a form which will add the selected entities to a session var.
I'm thinking that each checkbox should have the entity id as its value and I'll get an array of ids from the form field data (ok, so the value ought to be an indirect ref to the entity, but for the sake of simplicity).
I've tried creating a form Type with a single entity type field, mapped to the id property of the entity and embedded into another form Type which has a collection type field.
class FooEntitySelectByIdentityType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('foo_id', 'entity', array(
'required' => false,
'class' => 'MeMyBundle:FooEntity',
'property' => 'id',
'multiple' => true,
'expanded' => true
));
}
# ...
and
class FooEntitySelectionType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('identity', 'collection', array(
'type' => new FooEntitySelectByIdentityType,
'options' => array(
'required' => false,
'multiple' => true,
'expanded' => true,
'attr' => array('class' => 'foo')
),
));
}
# ...
and in a controller the form is created with a collection of entities as the initial data
$form = $this
->createForm(
new \Me\MyBundle\Form\Type\FooEntitySelectionType,
$collection_of_foo
)
->createView()
;
When the form is rendered there is a single label for the identity field, but no widgets.
Is it even possible to use entity and collection type fields in this particular way?
If so, what might I be doing wrong?
I think this will answer your question.
Forget the FooEntitySelectionType. Add a property_path field option to FooEntitySelectByIdentityType and set the data_class option to null:
class FooEntitySelectByIdentityType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('foo_id', 'entity', array(
'required' => false,
'class' => 'MeMyBundle:FooEntity',
'property' => 'id',
'property_path' => '[id]', # in square brackets!
'multiple' => true,
'expanded' => true
));
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => null,
'csrf_protection' => false
));
}
# ...
and in your controller, build the FooEntitySelectByIdentityType:
$form = $this
->createForm(
new \Me\MyBundle\Form\Type\FooEntitySelectByIdentityType,
$collection_of_foo
)
->createView()
;
and then in the controller action which receives the POSTed data:
$form = $this
->createForm(new \Me\MyBundle\Form\Type\FooEntitySelectByIdentityType)
;
$form->bind($request);
if ($form->isValid()) {
$data = $form->getData();
$ids = array();
foreach ($data['foo_id'] as $entity) {
$ids[] = $entity->getId();
}
$request->getSession()->set('admin/foo_list/batch', $ids);
}
and finally, in your twig template:
{# ... #}
{% for entity in foo_entity_collection %}
{# ... #}
{{ form_widget(form.foo_id[entity.id]) }}
{# ... #}
If someone is looking for solution for Symfony >=2.3
You have to change this:
$data = $form->getData();
$ids = array();
foreach ($data['foo_id'] as $entity) {
$ids[] = $entity->getId();
}
to this:
$data = $form['foo_id']->getData();
$ids = array();
foreach ($data as $entity) {
$ids[] = $entity->getId();
}
I create a symfony bundle for render a collection of entities as a checkbox table in a configurable way. Although it has been a while since this question was asked, I hope this can help others with this same kind of problem.

Resources