In my application I've got a couple of form fields with many options. The problem I experienced is similar to this question: getting and parsing all options at every page load is expensive (Twig renders all options over and over again while no client side caching possible). That problem made me create a way to send the options via AJAX to the browser. Fairly simple approach:
To prevent Symfony from querying all options (not necessary: they're loading via AJAX) I added setMaxResults(0)
to the QueryBuilder
when the form is loaded (by adding an option via the controller). Yes, that's kludge. When submitting a form it will still perform a query, because it has to verify if the selected option exists (and check for Constraints).
I would like to create a custom Form Field Type that adds this functionality to the current EntityType
: don't load the options while rendering the form, but still check if the selected option exists. I found many examples related to dynamically modifying a form, but I haven't found examples related to modifying just one form field, independently of it's parent form.
How do I create a form field type like this? What's a good starting point? Extend EntityType
, ChoiceType
or an other approach?
I'm already using Symfony 3.1, so using lazy loading of form choices (New in Symfony 3.2) won't be a problem. Not sure if this new feature is related to my problem.
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());
}
}
How to populate the form field with cached content :
That's your own logic, I would suggest : create a controller which only returns (as HTML) :
<option value="1">Option 1</option>
<option value="2">Option 2</option>
In the controller set Maxage :
/*
* @Route(...)
* @Cache(maxage=64000)
*/
public function getOptionsAction(Request $request) // Home
{
$choices = $this->getDoctrine()->getManager()->getRepository //....
return $this->render(/*...*/);
}
Use javascript to load this url and to put the html result in your select field.
If you are using something like Select2 : Your Controller can also return the options as a JSONReponse(), then you can load this JSON directly from the select2 ajax option (see bundle documentation, that's how I use it).
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.
Autocomplete with an Ajax controller option looks nice to me, but heres' another( maybe quicker?) option: render your form through hinclude.
hinclude is a JS library used to "defer" load of parts of a page, thought Ajax. Symfony comes with integrated support (official documentation).
How yo use it:
formAction
use this code to render your form:
{{ render_hinclude(controller('...::form'), {'default': 'Loading...'}) }}
you will probably want to keep your form handling in your original controller action, so modify the "action" of the generated form like this:
$form = $this->createForm(new FormType(), $obj, array( 'action' => $this->generateUrl('original_form_action')));
Considering your usecase, The best way would be to use an Autocomplete. Here is a Symfony bundle I have been using and its awesome. I did some overwrite its javascript (autocompleter) to enhance few functionality as per my case.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With