And we’re still on LezWatchTV where there are two CPTs: Characters and Shows.
Every Character has a meta key of the Show (or Shows) it relates to. Since this could be 1, 2, or 7 (you get the idea), it’s an array of Post IDs.
On every page for a Show, I do a count for how many Characters link back to it. Since they’re post IDs, I have to watch out for the LINK/IN results (which I mention in my post on querying the characters for their deaths … look it’s a weird site). And in so far as showing this on a front-end post, it’s not hard to run a query of characters, grab the shows (in their meta key) and check if that specific show is in the array. If it is, update the count by one.
But there are problems with this. It’s slow, and as it’s something I run fairly often this makes the site slow. Also it isn’t sortable. I ended up with a lot of ugly, repetitive, code. After screwing around with SQL and the like, I determined (around the same time my buddy Norcross opined the same) that the smart thing would be have the shows all have a meta field for character number and did this:
- Every time a show is saved, check how many characters link to it and save the count.
- Every time a character is saved, check what shows it links to and run the update for shows.
In that way, I can just grab the meta keys and call it a day. It would make everything much easier.
Would have been nice if I’d thought of that 1000 posts ago, huh? Well you live and learn and take down the elephant one bite at a time.
Make a Function to Count Characters
Since I know I’m going to want to check the number of characters from multiple places, it seemed wise to make this a function on it’s own.
/* * Count Queers * * This will update the metakey 'lezshows_char_count' on save * * @param int $post_id The post ID. */ function lez_count_queers( $post_id ) { // If this isn't a show post, return nothing. if ( get_post_type( $post_id ) !== 'post_type_shows' ) return; // Loop to get the list of characters $charactersloop = new WP_Query( array( 'post_type' => 'post_type_characters', 'orderby' => 'title', 'order' => 'ASC', 'posts_per_page' => '-1', 'meta_query' => array( array( 'key' => 'lezchars_show', 'value' => $post_id, 'compare' => 'LIKE', ), ), ) ); $queercount = 0; // Store as array to defeat some stupid with counting and prevent querying the database too many times if ($charactersloop->have_posts() ) { while ( $charactersloop->have_posts() ) { $charactersloop->the_post(); $char_id = get_the_ID(); if ( !is_array (get_post_meta( $char_id, 'lezchars_show', true)) ) { $shows_array = array( get_post_meta( $char_id, 'lezchars_show', true) ); } else { $shows_array = get_post_meta( $char_id, 'lezchars_show', true); } if ( in_array( $post_id, $shows_array ) ) { $queercount++; } } wp_reset_query(); } // Return Queers! return $queercount; }
When I want to use it, I just call lez_count_queers( POST_ID )
and I get a number of queers for that show.
Update Shows When Saving Shows
I could have done this in one massive function, but I decided to break it apart so I didn’t have to loop through things unnecessarily. That will make more sense when you look at the next function. But for this, I’m taking advantage of the action save_post_{post_type}
which lets me trigger my update only when that post type is updated.
/* * Save post meta for shows on SHOW update * * This will update the metakey 'lezshows_char_count' on save * * @param int $post_id The post ID. * @param post $post The post object. * @param bool $update Whether this is an existing post being updated or not. */ add_action( 'save_post_post_type_shows', 'lez_shows_update_char_count', 10, 3 ); function lez_shows_update_char_count( $post_id ) { // unhook this function so it doesn't loop infinitely remove_action( 'save_post_post_type_shows', 'lez_shows_update_char_count' ); $meta_value = lez_count_queers($post_id); update_post_meta( $post_id, 'lezshows_char_count', $meta_value ); // re-hook this function add_action( 'save_post_post_type_shows', 'lez_shows_update_char_count' ); }
The infinite loop check is very important, otherwise every time I ran the update, it would trigger the update and trigger the update and trigger the update. You get the idea.
Update Shows When Saving Characters
Having decided to break the saves apart, I had a very similar command for the characters, only this time I am forcing it into an array. This allows me to compensate for the characters who were added before I updated to the new, array, system. There are only a few left, but better safe than sorry.
/* * Save post meta for shows on CHARACTER update * * This will update the metakey 'lezshows_char_count' on save * * @param int $post_id The post ID. */ add_action( 'lez_shows_do_update_char_count', 'lez_shows_update_char_count', 10, 2 ); add_action( 'save_post_post_type_characters', 'lez_characters_update_char_count', 10, 3 ); function lez_characters_update_char_count( $post_id ) { if ( !is_array (get_post_meta( $post_id, 'lezchars_show', true)) ) { $shows_array = array( get_post_meta( $post_id, 'lezchars_show', true) ); } else { $shows_array = get_post_meta( $post_id, 'lezchars_show', true); } foreach ( $shows_array as $show_id ) { do_action( 'lez_shows_do_update_char_count' , $show_id ); } }
This time you’ll note the lack of infinite loop check. It’s not needed here, since I’m not updating the character content. I’m also doing two actions here, one for ‘do_action’ and one that is the action. This allows me to handle some odd workarounds like… the catch.
If a character doesn’t have any shows associated with it yet, like a new character, then it won’t update the character until the second save. And just as bad, if a show is changed, it won’t update. And no, I don’t have a complete fix for this yet. Working on it. The best idea I have so far is to schedule a single cron command to run the show update in 5 or 10 seconds. However this won’t solve the issue with changing shows.
Batch Update All The Things
Once I got the code in (and I checked that it worked), I had to update all the posts. Thanks to wp-cli, there’s a handy post update command. And yes, there are a lot of ways around this one but I thought this would be ‘simpler.’ If you want the super lazy way around it, you can view all the pages on the edit screen, select all, pick ‘quick edit,’ and then save them with no changes.
Since I decided to use WP-CLI, though, I wanted to get a list of all
$ wp post list --field=ID --post_type=post_type_shows --post_status=publish
This gave me a list of every show that was published.
$ wp post update 1 2 3 n --post_status=publish --defer-term-counting
And this re-saved every single show that was published with the post status of … published.
Make it Sort
Once I had done all that leg work, I could use the super basic sort by meta key value. The only difference to what we did before is that now we’re using meta_value_num
.. since characters are numbers.