Here’s the slide deck from my WordCamp Maine 2016 presentation, “Faster Templates With The WordPress Transients API”:
Direct link to Google Drive document: https://docs.google.com/presentation/d/1qRhAJ22feA7ojl7bG-u4DP3g-d3q53WSrtnQeKXP3cs/edit?usp=sharing
Related posts
I have previously written two posts on the WordPress Transients API: How To: Use Transients To Speed Up WordPress Templates and Using WordPress Transients To Speed Up Your PHP-Heavy Templates: Introduction.
Sample code: Transients for complex / recycled WP Queries
This code sample is a generalization of the code used to create the movies landing page on MaineToday.com. It allows us to cache the displays of movies, which require somewhat expensive custom field queries; and it allows us to recycle the “jump” menu at the top of the page, for use on the movies and theaters single page templates.
<?php function create_pulldown_transient() { //'jump' menu $output = ''; $output = '<!-- transient created ' . date( 'r' ) . ' -->'; $output .= '<select id="mt-movies-select" class="mt-movies-jump">'; $output .= '<option value="">Pick a movie ...</option>'; $movies = get_posts( array( 'posts_per_page' => -1, 'post_type' => 'mt_movie', 'meta_key' => 'movie_nowplaying', 'meta_value' => '1', 'orderby' => 'title', 'order' => 'ASC' ) ); foreach( $movies as $movie ) { $output .= '<option value="' . get_post_permalink( $movie->ID ) . '">' . $movie->post_title . '</option>' . "\n"; } $output .= '</select>'; $output .= '<select id="mt-theaters-select" class="mt-movies-jump">'; $output .= '<option value="">or Pick a theater ...</option>'; $theaters = get_posts( array( 'posts_per_page' => -1, 'post_type' => 'mt_theater', 'orderby' => 'title', 'order' => 'ASC' ) ); foreach( $theaters as $theater ) { $output .= '<option value="' . get_post_permalink( $theater->ID ) . '">' . $theater->post_title . '</option>' . "\n"; } $output .= '</select>'; return $output; } function create_movies_transient() { $output = ''; //get timestamps for start and end of this week date_default_timezone_set( 'GMT' ); $start = new DateTime( 'Sunday last week midnight' ); $end = new DateTime( 'Saturday this week 23:59:59' ); //get all nowplaying movies new this week $query = new WP_Query( array( 'post_type' => 'mt_movie', 'meta_query' => array( array( 'key' => 'movie_releasedate', 'value' => array( $start->format( 'U' ), $end->format( 'U' ) ), 'compare' => 'BETWEEN', 'type' => 'NUMERIC' ) ), 'posts_per_page' => -1, 'orderby' => 'title', 'order' => 'ASC' ) ); $output = '<!-- transient created ' . date( 'r' ) . ' -->'; $output .= '<h2>NEW THIS WEEK</h2>'; $has_new_movies = false; if( $query->have_posts() ) { $new_movies_segment = '<ul>'; while( $query->have_posts() ) { $query->next_post(); $msdate = get_post_meta( $query->post->ID, 'movie_releasedate', true ); $today = mktime( 23, 59, 59, date( 'n' ), date( 'j' ), date( 'Y' ) ); //make sure "new this week" movie isn't a one-day engagement prior to today if( ( '1' != get_post_meta( $query->post->ID, 'movie_nowplaying', true ) && $msdate > $today ) || '1' == get_post_meta( $query->post->ID, 'movie_nowplaying', true ) ) { $has_new_movies = true; $new_movies_segment .= '<li>'; $new_movies_segment .= '<a href="' . get_post_permalink($query->post->ID) . '">'; if( ! empty( get_the_post_thumbnail($query->post->ID, 'full') ) ) { $new_movies_segment .= get_the_post_thumbnail( $query->post->ID, 'full' ); } else { $new_movies_segment .= '<img src="/wp-content/uploads/2014/04/nomovie.png" />'; } $new_movies_segment .= '</a>'; if( $msdate > $today && '1' != get_post_meta( $query->post->ID, 'movie_nowplaying', true ) ) { $new_movies_segment .= '<span class="mt-movie-opendate">Opens ' . date( 'l', $msdate ) . '</span>'; } $new_movies_segment .= '<h3><a href="' . get_post_permalink($query->post->ID) . '">' . $query->post->post_title . '</a></h3>'; $new_movies_segment .= '<p>' . $query->post->post_excerpt . '</p>'; $new_movies_segment .= '</li>'; } } $new_movies_segment .= '</ul>'; } if( !$has_new_movies ) { $new_movies_segment = '<p><em>No new movies this week.</em></p>'; } $output .= $new_movies_segment; $output .= '<h2>NOW PLAYING:</h2>'; //all nowplaying movies not new this week $query = new WP_Query( array( 'post_type' => 'mt_movie', 'meta_query' => array( array( 'key' => 'movie_releasedate', 'value' => array( $start->format( 'U' ), $end->format( 'U' ) ), 'compare' => 'NOT BETWEEN', 'type' => 'NUMERIC' ), array( 'key' => 'movie_nowplaying', 'value' => '1' ) ), 'posts_per_page' => -1, 'orderby' => 'title', 'order' => 'ASC' ) ); if( $query->have_posts() ) { $output .= '<ul>'; while( $query->have_posts() ) { $query->next_post(); $output .= '<li>'; $output .= '<a href="' . get_post_permalink($query->post->ID) . '">'; if( ! empty( get_the_post_thumbnail( $query->post->ID, 'full' ) ) ) { $output .= get_the_post_thumbnail( $query->post->ID, 'full' ); } else { $output .= '<img src="/wp-content/uploads/2014/04/nomovie.png" />'; } $output .= '</a>'; $output .= '<h3><a href="' . get_post_permalink( $query->post->ID ) . '">' . $query->post->post_title . '</a></h3>'; $output .= '<p>' . $query->post->post_excerpt . '</p>'; $output .= '</li>'; } $output .= '</ul>'; } else { $output .= '<p><em>No movies now playing.</em></p>'; } return output; } //render page get_header(); get_sidebar(); ?> <div class="mt-main-col"> <div class="mt-movies-picker"> <h2>Movies & Showtimes</h2> <?php //nav pulldown menus if( false === ( $pulldown = get_transient( 'movies_picker' ) ) { $pulldown = create_pulldown_transient(); set_transient( 'movies_picker', $pulldown, 14 * HOUR_IN_SECONDS ); } echo $pulldown; ?> </div><!-- //mt-movies-picker --> <div class="mt-movies-landing"> <?php //movies listing if( false === ( $movies = get_transient( 'movies_listing' ) ) { $movies = create_movies_transient(); set_transient( 'movies_listing', $movies, 14 * HOUR_IN_SECONDS ); } echo $movies; ?> </div><!-- //mt-movies-landing --> </div><!-- //mt-main-col --> <?php get_footer(); ?>
Github gist: https://gist.github.com/dougvdotcom/26535a4dea6b491b1a9eb180e3ce4bed
Sample code: Transients to selectively cache parts of a page
This sample demonstrates how we can use transients to cache multiple segments of a single webpage for different durations. This removes the need to set our primary cache to expire the page as often as the most frequently changed code; instead, all content can be “pseudo-cached” according to its actual expiration.
It is lifted from the post, How To: Use Transients To Speed Up WordPress Templates.
In this case, we’re building a local, live music blog landing page, with three sections:
- A listing of upcoming gigs, which refreshes every 5 minutes;
- a list of music reviews, which updates every 4 hours; and
- a list of venues, which updates once a day.
//get all asides that are in the category music-gigs if( false === ( $output = get_transient( 'my-gig-asides-list' ) ) ) { //rebuild transient $gigs = new WP_Query( array( 'post_status' => 'publish', 'posts_per_page' => 10, 'orderby' => 'date', 'order' => 'DESC', 'tax_query' => array( array( 'taxonomy' => 'post_format', 'field' => 'slug', 'terms' => array( 'post-format-aside' ), 'operator' => 'IN' ), array( 'taxonomy' => 'category', 'field' => 'slug', 'terms' => array( 'music-gigs' ), 'operator' => 'IN' ) ) ) ); //create transient's value $output = '<div id="music-gigs"><h2>Upcoming gigs</h2><ul>'; if( $gigs->have_posts() ) { while( $gigs->have_posts() ) { $gigs->the_post(); $output .= '<li><a href="' . the_permalink() . '"><h3>' . the_title() . '</h3></a>'; $output .= the_excerpt() . '</li>'; } } else { $output .= '<li>No upcoming gigs available.</li>'; } $output .= "</ul></div>"; //store transient in WP database; expires in 5 minutes set_transient( 'my-gig-asides-list', $output, 5 * MINUTE_IN_SECONDS ); } //one way or another, we have a valid value in $output; echo it echo $output; //get the music review posts if( false === ( $output = get_transient( 'my-music-reviews' ) ) ) { $reviews = new WP_Query( array( 'post_type' => 'music_reviews', 'post_status' => 'publish', 'posts_per_page' => -1, 'orderby' => 'title', 'order' => 'ASC' ) ); $output = '<div id="music-reviews"><h2>Music reviews</h2>'; if( $reviews->have_posts() ) { while( $reviews->have_posts() ) { $output .= '<div class="music-review">'; $reviews->the_post(); $output .= '<a href="' . the_permalink() . '"><h2>' . the_title() . '</h2></a>'; $output .= the_excerpt(); $output .= "</div>"; } } else { $output .= "<p>No venues available.</p>"; } $output .= '</div>'; //this transient lasts 4 hours set_transient( 'my-music-reviews', $output, 4 * HOUR_IN_SECONDS ); } echo $output; //get the music review posts if( false === ( $output = get_transient('my-venue-reviews') ) ) { $reviews = new WP_Query( array( 'post_type' => 'music_venues', 'post_status' => 'publish', 'posts_per_page' => -1, 'orderby' => 'title', 'order' => 'ASC' ) ); $output = '<div id="music-venues"><h2>Where to listen</h2>'; if( $reviews->have_posts() ) { while( $reviews->have_posts() ) { $output .= '<div class="music-venues">'; $reviews->the_post(); $output .= '<a href="' . the_permalink() . '"><h2>' . the_title() . '</h2></a>'; $output .= the_excerpt(); $output .= "</div>"; } } else { $output .= "<p>No venues available.</p>"; } $output .= '</div>'; //this transient lasts one day set_transient( 'my-venue-reviews', $output, DAY_IN_SECONDS ); } echo $output;
Github gist: https://gist.github.com/dougvdotcom/804e1661d4c9fa4acc3fd5d3d96db74d
Sample code: Showing content to authenticated users
This sample demonstrates how we can use transients to show content to logged-in users. The same techniques can be used to allow us to leverage PHP superglobals, such as cookies, GET and POST objects, server / environment variables, etc., all of which are generally unavailable, or at least very unpredictable, if we are caching a web page.
This code is lifted from How To: Use Transients To Speed Up WordPress Templates
if( current_user_can( 'read' ) ) { if( false === ( $output = get_transient( 'my-insider-news' ) ) ) { $blurbs = new WP_Query( array( 'post_status' => 'publish', 'posts_per_page' => 5, 'orderby' => 'date', 'order' => 'DESC', 'tax_query' => array( array( 'taxonomy' => 'post_format', 'field' => 'slug', 'terms' => array( 'post-format-aside' ), 'operator' => 'IN' ), array( 'taxonomy' => 'category', 'field' => 'slug', 'terms' => array( 'insiders' ), 'operator' => 'IN' ) ) ) ); $output = '<ul>'; if( $blurbs->have_posts() ) { while( $blurbs->have_posts() ) { $blurbs->the_post(); $output .= '<li><a href="' . the_permalink() . '"><h3>' . the_title() . '</h3></a>'; $output .= the_excerpt() . '</li>'; } } else { $output .= '<li>No insider information today.</li>'; } $output .= '</ul>'; //transient will last a half-hour set_transient('my-insider-news', $output, 30 * MINUTE_IN_SECONDS); } } else { $output = "<p>Join the Insiders to get news before everyone else!</p>"; } echo $output;
Github gist: https://gist.github.com/dougvdotcom/b1d761cb590ddf85269fffa27e333ae6