Following on the previous post in this series (using transients to improve the performance of custom queries), we’re going to look at another type of query that can be improved by using transients. We’re going to cache your navigation menus.
Caching WordPress menus
Every time you load a page on your site, chances are that you are loading at least one menu. To retrieve that menu, WordPress has to query the database to find all of the menu items associated with that menu and all of the data associated with them (menu item titles etc.). Using the same principles as improving the custom queries, we can cache this database query as well.
Take for example my original code for retrieving a WordPress menu:
Ever since the introduction of the menu functions in WordPress 3.0, nearly every theme now uses this method to display menus on your site, so you should have some similar looking code in your theme files.
Now, this is what my revised code looks like and I’ll explain it line by line, below:
In line 2, we’re setting our variable ($header_menu_query) equal to the value of the transient of the same name using get_transient(). On line 3, we check for whether that transient data exists. If it doesn’t (either because it hasn’t been created yet, or because it has expired and been deleted), we go ahead and run the query to retrieve the WordPress menu. In line 5, we set our variable equal to the result of the wp_nav_menu query.
Whereas in the example of the custom queries we didn’t change the query itself, we do need to make a slight tweak to the wp_nav_menu function. By default, wp_nav_menu echoes the result of the query (which is normally the desired behaviour, so that it appears on screen), but we want to return the data to PHP to be stored as a transient, so we need to add the argument ‘echo’ => 0 to the function, as shown in line 12. Aside from that, the remainder of the query stays the same.
In line 15, we take the result of the query and set our transient with that data using set_transient(), so that it can be reused on the next page load. Again, you’ll note that I’ve set a maximum expiration time (of 24 hours), but more on that later.
On line 17, we have to make a slight addition: since previously wp_nav_menu was automatically echoing the menu to screen and we modified the query to return it to PHP, we need to echo the result in order to actually see it, so we add in this line to achieve that.
Updating the transient
As with the custom queries example, we have set an absolute maximum expiration time for the data, after which we want to forcibly replace it. WordPress menus don’t change often, so this could be quite a long time, but 1 day allows you to do it often enough to check daily without impacting performance (one database query per day wouldn’t even be noticed).
However, we want to proactively delete the data when we know the data has changed. To do this, I wrote another function which I dropped into my functionality plugin, though this could just as easily go into functions.php. This is what it looks like:
I hooked into the wp_update_nav_menu action, which fires every time a WordPress menu is updated. Since this is so rare, I didn’t bother checking which menu had been edited, to decide which menu transients to delete, though you could certainly do that.
The function simply uses the delete_transient() function to delete all of my menu transients every time a menu is updated (I had two additional menus [footer_menu1 and footer_menu2] where I applied the same technique).
Now, the transients are refreshed at least every 24 hours, and immediately after a nav menu has been edited or saved. You’re now saving yourself several database queries on every page load, which can amount to a nice little savings, depending on busy your site is and how many menus you have.