Yesterday I talked about ordering posts by post meta in a weirdly unique situation. Today I’m going to tell you about an even weirder one.

As I mentioned, what I’m working with is a list of all the dead characters recorded on a site. One of the aspects of this is to allow for us to show a widget that tells visitors how many days it’s been since the last death, and who it was. There are a lot of ways to do this, and one of them is manually updating, but no one likes this. If an event can be automated, everyone wins.

Since I’d already done the lion’s share of the work, forking it over into a widget and grabbing the last (i.e. most recent) death was easier.

The Code

I broke this up into two classes because it’s habit. I have no other reason.

class Dead {
	public function __construct() {
		add_action( 'widgets_init', array( $this, 'register_widget' ) );
	}

	public function register_widget() {
		$this->widget = new Dead_Widget();
		register_widget( $this->widget );
	}

	public static function dead_queers() {
		// Get all our dead queers
		$dead_chars_loop = new WP_Query( array(
			'post_type'       => 'post_type_characters',
			'posts_per_page'  => -1,
			'meta_query'      => array(
				array(
					'key'     => 'chars_death_year',
					'value'   => $thisyear,
					'compare' => 'REGEXP',
				),
			),
		) );
		$dead_chars_query = wp_list_pluck( $dead_chars_loop->posts, 'ID' );

		// List all queers and the year they died
		if ( $dead_chars_loop->have_posts() ) {
			$death_list_array = array();

			// Loop through characters to build our list
			foreach( $dead_chars_query as $dead_char ) {

				// Date(s) character died
				$died_date = get_post_meta( $dead_char, 'lezchars_death_year', true);
				$died_date_array = array();

				// For each death date, create an item in an array with the unix timestamp
				foreach ( $died_date as $date ) {
					$date_parse = date_parse_from_format( 'm/d/Y' , $date);
					$died_date_array[] = mktime( $date_parse['hour'], $date_parse['minute'], $date_parse['second'], $date_parse['month'], $date_parse['day'], $date_parse['year'] );
				}

				// Grab the highest date (aka most recent)
				$died = max( $died_date_array );

				// Get the post slug
				$post_slug = get_post_field( 'post_name', get_post( $dead_char ) );

				// Add this character to the array
				$death_list_array[$post_slug] = array(
					'name' => get_the_title( $dead_char ),
					'url' => get_the_permalink( $dead_char ),
					'died' => $died,
				);
			}

			// Reorder all the dead to sort by DoD
			uasort($death_list_array, function($a, $b) {
				return $a['died'] <=> $b['died'];
			});
		}

		// Extract the last death
		$last_death = array_slice($death_list_array, -1, 1, true);
		$last_death = array_shift($last_death);

		$diff = abs( time() - $last_death['died'] );
		$years = floor($diff / (365*60*60*24));
		$months = floor(($diff - $years * 365*60*60*24) / (30*60*60*24));
		$days = floor(($diff - $years * 365*60*60*24 - $months*30*60*60*24)/ (60*60*24));

		$days_since = '';
		if ( $years != 0 ) $days_since  .= $years .' '. _n( 'year, ', 'years, ', $years );
		if ( $months != 0 ) $days_since .= $months .' '. _n( 'month', 'months', $months );
		if ( $years != 0 ) $days_since  .= ',';
		if ( $months != 0 ) $days_since .= ' and ';
		if ( $days != 0 ) $days_since   .= $days .' '. _n( 'day', 'days', $days );

		$return = 'It has been <strong>'. $days_since .'</strong> since the last death: <a href="'.$last_death['url'].'">'.$last_death['name'].'</a> - '.date('F j, Y', $last_death['died'] );

		echo $return;
	}
}
new Dead();

class Dead_Widget extends WP_Widget {

	protected $defaults;

	function __construct() {
		$this->defaults = array(
			'title'		=> 'Bury Your Dead',
		);

		$widget_ops = array(
			'classname'   => 'dead-queer deadwidget',
			'description' => 'Displays days since the last death',
		);

		$control_ops = array(
			'id_base' => 'dead-person',
		);

		parent::__construct( 'dead-person', 'Days Since Last Dead', $widget_ops, $control_ops );
	}

	function widget( $args, $instance ) {
		extract( $args );
		$instance = wp_parse_args( (array) $instance, $this->defaults );

		echo $before_widget;

		if ( ! empty( $instance['title'] ) ) {
			echo $before_title . apply_filters( 'widget_title', $instance['title'], $instance, $this->id_base ) . $after_title;
		}

		echo Dead::dead_chars();

		echo $after_widget;
	}

	function update( $new_instance, $old_instance ) {
		$new_instance['title']      = strip_tags( $new_instance['title'] );
		return $new_instance;
	}

	function form( $instance ) {
		$instance = wp_parse_args( (array) $instance, $this->defaults );
		?>
		<p>
			<label for="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>">Title:</label>
			<input type="text" id="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'title' ) ); ?>" value="<?php echo esc_attr( $instance['title'] ); ?>" class="widefat" />
		</p>
		<?php
	}
}

Which looks like this:

Bury Your Queers

An Explanation

Some things to note.

While I only allow for customizing the title, I’ve left my arguments as an array in order to let me extend it later. What if, for example, I want to display the featured image as well?

I’ve also left in some ‘default’ calls to Genesis’ StudioPress theme with regards to the widget layout. That’s because I wrote this for a StudioPress theme.

The whole reason the death year is an array is that, as I mentioned the other day, people may die multiple times. Insert Buffy singing “Hey I died twice” here. I have to get all the dates, pick the largest number (which is the most recent date, remember Unix timestamps are forever increasing), and save that one. Once I find the ultimate largest number, I know that’s my character to save. Problem is I have to go through them all to figure out who died last.

I’m inordinately happy about getting to use array_slice() to grab the final item in an array, and ditto array_shift() to move the multidimensional array to a single dimension.

Using array_shift() changed this:

Array ( 
    [gina] => Array (
        [name] => Gina 
        [url] => https://example.dev/character/gina/ 
        [died] => 1481068800 
    )
)

to this:

Array ( 
    [name] => Gina 
    [url] => https://example.dev/character/gina/ 
    [died] => 1481068800 
) 

And getting to finally use _n() in a non-translation sense was fun.

The reason the dead characters are in a function in the first class is that I plan to extend this code later on, maybe making a shortcode or a JSON API call. Who knows!

Finally, I mentioned that there were two ways to list the dead characters. One was by a taxonomy of character clichรฉs and the other was by the death date post meta. In this case, I opted to sort by the taxonomy because if someone has a death date but is not marked as dead, then they were dead and came back to life.

Reader Interactions

Comments

  1. I saw that you used wp_pluck_list() here and in a recent post.
    I have used 'fields' => 'ids' in my WP_Query calls to get a list of post IDs with minimal memory overhead (I discovered this while debugging a regular Loop (posts_per_page => -1) was using too much memory on a shared hosting package. Retrieving only IDs was a nice solution for me.

    The code would look like:
    $dead_chars_loop = new WP_Query(...., 'fields' => 'ids', ...);
    $dead_chars_query = $dead_chars_loop->posts;

    I don’t know if it is quicker or whether it uses less memory than wp_list_pluck().

%d bloggers like this: