3

The Scenario

In hook_block_configure(), I'm displaying a configuration form to users that is represented by a fairly complicated render array. The purpose of the form is to allow users to configure an arbitrary number of buttons as part of a button set.

  • Each button is individually configurable.

  • As part of each button's configuration, the user can select an icon from a list of icons. This list is mostly static, although users can upload additional icons on a per-site basis.

  • This module is for use in a Drupal multi-site configuration, where each site can field one or more of these button sets.

  • Each button configuration also features a preview rendering of the button.

  • There is a single rendering of the entire button set on the form as well.

The Question

How best to use caching within this render array? Is it possible to separately cache portions of the render array? For instance, could I separately cache each button configuration, as well as the icons and the rendered buttons?

While solutions to this exact situation would be appreciated, I'm actually more interested in insight into the finer points of adding caching to a complex render array. I believe that I've read most of the introductory documentation on caching that is readily available on the Internet, but haven't found much regarding more involved render arrays.

Sources

I found these sources to be very useful in attaining a basic understanding of caching in Drupal:

Thanks to all for your insights!

4
  • Is any of the data on the form "expensive" in terms of processing time to collate?
    – mona lisa
    Apr 19, 2016 at 20:59
  • I guess I'm not completely sure what is considered expensive, but the icon selection interface has approximately 400+ icons rendered via theme_image_style(). I've also edited my post just now to include that there are previews of the rendered buttons on the form as well, and I would like to cache those rendered buttons as well. Apr 19, 2016 at 22:24
  • Calling theme_image_style (theme('image_style') is the correct one) itself isn't that expensive. The preprocess hooks list is cached, and if you pass file objects, it will not need to get the image sizes either. The actual culprit would be when all the 400 image resize requests hit the server and make a mini DoS when the server tries to resize the images, which is a lot expensive even in a moderately powered server.
    – AKS
    Apr 19, 2016 at 22:29
  • @AyeshK, thanks for the input. I am actually using a custom implementation of theme_image_style() and calling it via the #theme property in my render arrays, but described it in the question as theme_image_style for brevity's sake. Sorry for the confusion. :-) Each rendering of the icon image is passed a #path and a #style_name and is rendering the icon to specific dimensions based on the #style_name's effects (usually Scale). Apr 19, 2016 at 23:12

1 Answer 1

1

http://omnifik.com/blog/render-arrays-and-cache-drupal-7 is an excellent resource, I use it as a guide when caching render arrays. The nice thing about render arrays is they are recursive. So for the hook_menu page callback you can have that function return mutiple items. As an example I have the page callback function looks like this

function my_page_callback() {
  // Form dumps values in the URL, act on them.
  $input_values_from_form = drupal_get_query_parameters();

  // Get form with URL values set in the form as defaults.
  $input_form = drupal_get_form('my_form', $input_value_from_form);

  // Build table render array.
  $table = array(
    '#theme' => 'table',
    '#pre_render' => array('my_table_pre_render'),
    '#header' => $header,
    '#rows' => $rows,
    '#links' => $links,
    '#url_queries' => $url_queries,
    '#filters' => $filters,
    '#entity_types' => $entity_types,
    '#missing_info' => $missing_info,
    '#attributes' => array('class' => array('table', 'table-striped')),
    '#attached' => array('css' => array(drupal_get_path('module', 'my_module') . '/css/table.css' => array('type' => 'file'))),
    '#cache' => array(
      'keys' => $input_values_from_form,
      // Cache this for 1 hour.
      'expire' => REQUEST_TIME + 3600,
    ),
  );

  // Put them together.
  $page_array = array($input_form, $table);

  return $page_array;
}

So this will render the input form first and the table second. Using this you can in theory split up your complex page into many smaller parts caching each array separately.

In the above example theme_table does most of the work but some other parts get used inside my_table_pre_render so the '#theme' => 'table', here is a complex example but it shows how to do this.

Pick the keys for each render array wisely as in make it relevant only to that part of the page; see https://api.drupal.org/api/drupal/includes!common.inc/function/drupal_render_cid_create/7.x

2
  • thanks for your response, and for taking the time to post an example. My form's render array is also built in pieces, and so I think you may be addressing the heart of my question. Is it correct, then, to say that individual branches of a single render array can be separately cached? Apr 21, 2016 at 14:36
  • If you break it up then yes you can cache each render array giving each section an appropriate key so only that section needs to be rebuilt in the case of a cache miss
    – mikeytown2
    Apr 21, 2016 at 17:39

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.