Reputation: 5066
Is there a way to get an EntityType
form element in Symfony, that doesn't preload all the entries, but still accepts them?
Background: It's for an image selector, where possibly 1000+ images will be stored. I don't want to put load on the DB, every time the field is rendered. The form has a custom frontend, using hidden fields an Javascript (to load a paged list of images).
The current implementation works, as it should, but as it extends the EntityType
it does all the preloading, while when using a ChoiceType
the form doesn't accept the values (unless, you give them as choices, which would mean to load all entries from the DB)
----------------------- EDIT -----------------------
I'm working with the suggested bundle. If you feel the need to have your own view (widget) as I do, you can achive that quite easy:
If you use composer, you can simple install all needed files running
composer require alsatian/form-bundle dev-master
With the bundle installed you can follow this steps:
First, you have to create your own form field (mine looks like this):
<?php
namespace AppBundle\Form\Fields;
use Symfony\Component\Form\AbstractType;
use Alsatian\FormBundle\Form\ExtensibleEntityType;
class UploadFileType extends AbstractType
{
/**
* {@inheritdoc}
*/
public function getParent() {
return ExtensibleEntityType::class;
}
/**
* {@inheritdoc}
*/
public function getName() {
return 'app_upload_file';
}
/**
* {@inheritdoc}
*/
public function getBlockPrefix()
{
return 'app_upload_file';
}
}
Note: you can put anything you like in getBlockPrefix
, just remember what you put there.
Next you need to modify your form theme (doc) and add you own widget:
{%- block app_upload_file_widget -%}
{{ dump(form) }}{# output some debug info #}
{% if not expanded %}
{# example for some fake entries #}
{% if multiple %}
<input type="hidden" name="{{ full_name }}" value="1" />
<input type="hidden" name="{{ full_name }}" value="2" />
<input type="hidden" name="{{ full_name }}" value="3" />
{% else %}
<input type="hidden" name="{{ full_name }}" value="1" />
{% endif %}
{% else %}
{{- block('choice_widget') -}}
{% endif %}
{%- endblock app_upload_file_widget -%}
Note: Here we need our getBlockPrefix
and just add a _widget
to it!
Finally, we can add it to one of our forms:
<?php
namespace AppBundle\Form\Admin;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class CompanyType extends AbstractType
{
/**
* @param FormBuilderInterface $builder
* @param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('logo', \AppBundle\Form\Fields\UploadFileType::class, array(
'class' => \AppBundle\Entity\File::class,
'choice_label' => 'originalName',
))
;
}
/**
* @param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Company'
));
}
}
Of course we need to have a join in our entity, for me, it looks something like this:
/**
* @ORM\ManyToOne(targetEntity="File")
* @ORM\JoinColumn(name="logo_id", referencedColumnName="id")
*/
private $logo;
I hope, this helps someone, that is in the same situation as I am (:
Upvotes: 2
Views: 2223
Reputation: 3135
This question comes every 3 days :)
I paste my answer from here:
I wrote a bundle (Alsatian/FormBundle), which does what you want on the server side.
How to avoid loading each entities by each form rendering :
abstract class AbstractExtensibleChoicesType extends AbstractRoutableType
{
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefault('choices',array());
}
}
Get the sumitted choices in a Form::PRE_SUBMIT event (also PRE_SET_DATA if you use your form to edit), and reinject these choices to the field.
Upvotes: 4