phpsymfony

Symfony 3.3 - Alternatives to ChoiceType with Too Much Data


I have a form I'm creating and I want the user to be able to select from a list of values (using ChoiceType) although this list is over 100k values (with name and id). As expected, this is way too much data and causes the browser to glitch with this much data in the DOM. Any ideas of how to solve this?

One idea I had was doing a type ahead search feature and storing the id into a hidden value, although I wanted to see if any Symfony experts on here had better ideas.


Solution

  • You have the right idea, in your formType class, just add a placeholder choice, for your js to grab as a template (clone, manipulate, append). Don't trigger the AJAX event until after at least 3 characters have been typed to limit the response time and size. Store the results in a global cache object variable, and check the cache first before sending the request. That way you don't send duplicate requests. Below is an example of using jQuery in the twig template and assuming you have a contoller method annotated like so

    /**
     * @Route("api/search/{term}", defaults={"term"=null},  name="ajax_search_route_name")
     */
    public function getInputChoices($term){
      // do stuff
      return new Symfony\Component\HttpFoundation\JsonResponse($choices);
    }  
    

    with an optional parameter for the search term, so twig path() function will work for the base path at load time and the search term is added by js on the fly.

    {% block javascripts %}
      <script>
        ...
        function triggerMeAfterTypingThreeOrMore(){
          var input_val = $('#form_input_id).val();
          var choices = (myCacheObject[input_val] != undefined) ? 
            myCacheObject[input_val] : false;
    
          if(choices !== false){
    
            // add cached choices to the dom
    
            return;
          }
          // we only make it this far if there is no cache entry
          getAutocompleteTerms(input_val);
        }
    
        function getAutocompleteTerms(input_val){
    
            $.get({
              url: "{{ path('ajax_search_route_name') }}" + "/" + input_val,
              dataType: 'json',
    
              success: function (data) {
                //store the response locally, indexed by the search term
                window.myCacheObject[input_val] = data;
              },
    
              complete: function (d) {
                // retrigger to insert into dom
                triggerMeAfterTypingThreeOrMore();            
              }
    
        }
      </script>
    {% endblock javascripts %}
    

    This way using $.get every request is sent to a different url like "api/search/abc", "api/search/abcd" and Symfony will handle caching responses on the server.

    I used this technique in a Symfony2 project with very big data and multiple search fields and it was very efficient, even on a 1Mbps connection. I'm currently up to my neck in Symfony4, and I only built one small project with Symfony3, so I apologize if my conventions are a little off (version-wise).