Search code examples
drop-down-menucoldfusionhandlercoldbox

Coldfusion Coldbox - create and cache drop down options from queries


I am looking for opinions and alternate ideas, as what I am doing is working, but wanted to ask if it was optimal

I have a site that when the index handler is called, it populates the request collection with specific queries from database tables so that I can build drop downs for the user to select.

i am querying two models and putting their results in their respective variables, then loop thru them in the view to create the drop down

index Handler

function index(event, rc, prc){
    event.paramValue("debug",0);
    rc.stages  = getmodel("tms_proposal_stage").select();
    rc.plannedGiftTypes  = getmodel("tms_funding_type").select();
    event.setLayout('layout.bootstrap');
}

index view

  <div class="form-group">
    <label for="proposal_stage" class="control-label">Proposal Stage Code</label>
    <select id="proposal_stage" name="proposal_stage" class="form-control">
    <cfloop query="rc.stages">
        <option value="#stage_code#">#short_desc#</option>
    </cfloop>

    </select>
  </div>

I understand that two queries isnt that costly, but if I needed to run 100 of these that would have scalability issues. These query result sets do not change much, so I was thinking, shouldnt these be cached or stored and accessed a different way?

I thought about html5 local storage, which I have used but not in this regard. I also considered making a new handler function that makes all of these database calls and is cached, then referenced by other functions

anyways, all thoughts are appreciated


Solution

  • You have several options available to you. Since you're using ColdBox, you have CacheBox readily available to you. https://github.com/ColdBox/cbox-refcards/raw/master/CacheBox/CacheBox-Refcard.pdf

    A very simple way to do inline caching of the data would be to inject a CacheBox provider at the top of your component:

    component {
      property name="cache" inject="cachebox:default";
    }
    

    Then use the cache API in your event to store and retrieve data. My favorite method is getOrSet() because it wraps it up in a single call.

    rc.stages = cache.getOrSet(
      objectKey="stages",
      produce=function(){ 
        return getmodel("tms_proposal_stage").select();
      }
    );
    

    The closure is only executed if the key is not already in the cache. http://wiki.coldbox.org/wiki/WhatsNew:CacheBox-1.6.cfm#Get_Or_Set_Baby

    Another approach is to cache the full HTML. To do this, create a viewlet that only outputs the HTML for your form control. Create an event that returns the output of just that view like so:

    function stagesFormInput(event, rc, prc) cache=true {
      var stagesData = getmodel("tms_proposal_stage").select();
      return renderView(view="viewlets/stages", args={ stagesData : stagesData } );
    }
    

    Note, I'm passing stageData directly into the view so it doesn't pollute the rc or prc. This data will be available in your viewlet as "args.stagesData".

    Also note the "cache=true" in the method declaration. That's the magic that will tell ColdBox to cache this event (inside CacheBox's "template" provider"). You can specify a timeout, but this will use the default. Now, enabled eventCaching in your /config/ColdBox.cfc file.

    coldbox={
        eventCaching = true
    }
    

    http://wiki.coldbox.org/wiki/ConfigurationCFC.cfm#Application_Aspects

    And finally, in your main view or layout, just run your new viewlet everywhere you want the cached HTML to be output.

    #runEvent("viewlets.stagesFormInput")#
    

    This is a little bit more setup, but is more powerful since it caches the full HTML snippet which is really ideal. I also have an entire sample app that demos this inside a working app. You can check it out here: https://github.com/bdw429s/ColdBox-Viewlet-Sample