Symfony 4 - performance issue on form with a selectbox with many options

Symfony 4 - performance issue on form with a selectbox with many options



I built a form with a selectbox (EntityType) with a big amount of choices (about 50 000) :


->add(
'destination', EntityType::class, array(
'label' => 'à',
'multiple' => false,
'required' => false,
'class' => Stop::class,
'attr' => array(
'class' => 'form-control',
)
)
);



I am facing a big performance issue : dozens of seconds before the list is displayed when I click on it.



I guess the solution would be to initially only load a few elements (e.g. a hundred), and then use Ajax to request the DB when the user starts typing (I am using a select2 box with search field).
Problem is that I cannot figure out the most efficient way to do it through Symfony.



I have seen that the choice_loader functionality could do it, but there is no detailed documentation available : https://symfony.com/blog/new-in-symfony-3-2-lazy-loading-of-form-choices



Would be great if somebody can help on this,



Thanks for your support,






Autocompletion is the answer. One option is listed as an answer by Marcos below. I've also had success with PUGX autocompleter.

– ehymel
Sep 15 '18 at 23:13






Hi, Thanks for your feedback. I've tried this way, it is almost working but I am still facing an issue : - using select2 library it does not work, I get following js error : "if (typeof(text) == 'undefined')return;" - using jquery ui, it works but as it is stored in an input, I can only display the primary key of my entity in the field if I want Symfony form processor to retrieve it, which is not so intuitive for the use... Any idea how I can solve this ?

– Logboo
Sep 17 '18 at 16:07





1 Answer
1



When I face this kind of trouble, I use another approach.
If the select option will have more than 20 entries, so I change it to a Input Text with autocomplete.



Install a good autocomplete Javascript lib like jquery-typeahead



I like to use Wepack Encore in Symfony. With Webpack, Npm and Yarn the installation is easy like


yarn add jquery-typeahead --dev



You would need to run yarn run encore dev after installation.


yarn run encore dev



Create a new FieldType for your form to replace the EntityType



Lets suppose that we need to create a register form with city field. The default behaviour will use EntityType and show a Select Option with all cities.
To change it to autocomplete lets create another FieldType.


<?php
// src/Form/Type/AutocompleteCityType.php
namespace AppFormType;
use DoctrineORMEntityManagerInterface;
use SymfonyComponentFormAbstractType;
use SymfonyComponentFormExtensionCoreTypeSearchType;
use SymfonyComponentOptionsResolverOptionsResolver;
class AutocompleteCityType extends AbstractType


public function configureOptions(OptionsResolver $resolver)

$resolver->setDefaults(array(
'attr' => ['autocomplete' => 'off']
));


public function getParent()

return SearchType::class;




NOTE: On the code above, I am extending SearchType::class that is a Input Type Search (HTML 5).



Our new field type can be used on our forms but it is just another string field. Won't work correctly to replace EntityType. We need to convert this string to an Entity. So we need a DataTransformer



Create a City to String DataTransformer


<?php
// src/Form/DataTransformer/CityToStringTransformer.php
namespace AppFormDataTransformer;
use AppEntityCity; // Pay attention to use your Entities correctly
use SymfonyComponentFormDataTransformerInterface;
use SymfonyComponentFormExceptionTransformationFailedException;

class CityToStringTransformer implements DataTransformerInterface
null $city
* @return string
*/
public function transform($city)

if (null === $city)
return '';


return $city->getSomethingUnique()


/**
* Transforms a string to an object (city).
*
* @param string $somethingUnique
* @return City



Note: The string must be some kind of unique key to work correctly and need to be good to show on Autocomplete and fill the field (Like [CityCode] CityName). The DataTransformation cannot return more than one result on findByThatSomethingUnique() method.


findByThatSomethingUnique()



Almost done. We can use both classes on our FormType to replace EntityType.



Using on the FormType


// src/Form/MyFormType.php
class MyFormType extends AbstractType
{
private $cityTransformer;

public function __construct(CityToStringTransformer $cityTransformer)

$this->cityTransformer = $cityTransformer;


public function buildForm(FormBuilderInterface $builder, array $options)

/* @var $myEntity MyEntity */
$myEntity = $builder->getData();
$builder
->add('city', AutocompleteCityType::class, [
'label' => 'Custom City Label',
])
// (...) Other fields (...)
;

$builder->get('city')
->addModelTransformer($this->cityTransformer);




With the code until here, the form will be shown correctly, but the typeahead must be configured properly.



You can create a new twig block type for this new field type. The code below must reside in the your custom form_theme



The Twig block


% block autocomplete_city_widget %
% spaceless %
<div class="typeahead__container">
<div class="typeahead__field">
<div class="typeahead__query">
form_widget(form)
</div>
</div>
</div>
% endspaceless %
% endblock %



NOTE: The Twig code above is related to jquery-typeahead and only works with a field called AutocompleteCityType. If you install another lib or change the name of FieldType class, change it properly. Also pay attention to form names that change the block name to be rendered.



The last thing is to write the javascript for typeahead get the entries.



The Typeahead Javascript


jQuery.typeahead(
input: "#myForm_city", // Related to FormName and Autocomplete Field Name
minLength: 1,
maxItem: 20,
order: "asc",
dynamic: true,
delay: 500,
backdrop: "background-color": "#eeeeee" ,
template: "<small style='color:#999;'> '[citycode] cityname' </small>", // remember that this is a Twig template...
emptyTemplate: "No results for typed string",
source:
city:
display: ["citycode", "cityname"],
ajax: function (query)
return
type: "POST",
url: ' path('controller_with_city_list_json_response') ',
path: "city",
data:
"q": " 'query' ",
"length" : "40",
,
callback:
done: function (res)
var d = ;
d.city = ;
jQuery(res.data).each(function(index, value)
d.city.push(value.city);
);
return d;





,
callback:
onClickAfter: function (node, a, item, event)
event.preventDefault();
jQuery(node).val("[" + item.citycode + "] " + item.cityname);

,
debug: false
);



NOTE: The Typeahead code above want a json response in format


"data":
[
"city":
"cityname":"City Name X", "citycode": "NXNX"
,
"city":
"cityname":"City Name Y", "citycode": "NYNY"

]



Thanks for contributing an answer to Stack Overflow!



But avoid



To learn more, see our tips on writing great answers.



Required, but never shown



Required, but never shown




By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Popular posts from this blog

𛂒𛀶,𛀽𛀑𛂀𛃧𛂓𛀙𛃆𛃑𛃷𛂟𛁡𛀢𛀟𛁤𛂽𛁕𛁪𛂟𛂯,𛁞𛂧𛀴𛁄𛁠𛁼𛂿𛀤 𛂘,𛁺𛂾𛃭𛃭𛃵𛀺,𛂣𛃍𛂖𛃶 𛀸𛃀𛂖𛁶𛁏𛁚 𛂢𛂞 𛁰𛂆𛀔,𛁸𛀽𛁓𛃋𛂇𛃧𛀧𛃣𛂐𛃇,𛂂𛃻𛃲𛁬𛃞𛀧𛃃𛀅 𛂭𛁠𛁡𛃇𛀷𛃓𛁥,𛁙𛁘𛁞𛃸𛁸𛃣𛁜,𛂛,𛃿,𛁯𛂘𛂌𛃛𛁱𛃌𛂈𛂇 𛁊𛃲,𛀕𛃴𛀜 𛀶𛂆𛀶𛃟𛂉𛀣,𛂐𛁞𛁾 𛁷𛂑𛁳𛂯𛀬𛃅,𛃶𛁼

How do I collapse sections of code in Visual Studio Code for Windows?

ャフサォクコ ケウ,コ,ワ メ,ロスョノ゙,クネ,フムカヤヲニ,エコ゚ツ ウイオン゙ケワサネォキモュキォウイノンコチ゚メヌナイゥフュ,カヒウネェ ネ,ホノケ,ムュキ ッボーミュハ,チ ツス ィ メウイマヤ,゙ウチ ヅ ロ,ォジヌェ ャヌット ェ,マャ,チナエヒネソキツテ トホヲヲミーァ