clubmate.fi

A good[ish] website

Web development blog, loads of UI and JavaScript topics

Change post order by meta key, per post basis, in WordPress

Filed under: WordPress/PHP— Tagged with: custom fields, iteration, meta box

Let users decide which meta key is used to sort each post. I’ll be using WPAlchemy metabox class to create and save the custom fields.

Imagine a situation: a set of posts, each representing a person. Rightfully, you want to alphabetize the people according to their last name, but there might be exceptions (in some countries the first name may be the prominent name, or for whatever reason), so you should allow users to decide on which name to alphabetize the person.

Sorting post by custom field value

Normally posts are sorted in chronological order (date), but now, we're sorting based on custom field. Here's all the values the orderby parameter takes (taken form WP Codex):

  • 'none' - No order (available with Version 2.8).
  • 'ID' - Order by post id. Note the capitalization.
  • 'author' - Order by author.
  • 'title' - Order by title.
  • 'name' - Order by post name (post slug).
  • 'date' - Order by date.
  • 'modified' - Order by last modified date.
  • 'parent' - Order by post/page parent id.
  • 'rand' - Random order.
  • 'comment_count' - Order by number of comments (available with Version 2.9).
  • 'menu_order' - Order by Page Order. Used most often for Pages (Order field in the Edit Page Attributes box) and for Attachments (the integer fields in the Insert / Upload Media Gallery dialog), but could be used for any post type with distinct 'menu_order' values (they all default to 0).
  • 'meta_value' - Note that a 'meta_key=keyname' must also be present in the query. Note also that the sorting will be alphabetical which is fine for strings (i.e. words), but can be unexpected for numbers (e.g. 1, 3, 34, 4, 56, 6, etc, rather than 1, 3, 4, 6, 34, 56 as you might naturally expect). Use 'meta_value_num' instead for numeric values.
  • 'meta_value_num' - Order by numeric meta value (available with Version 2.8). Also note that a 'meta_key=keyname' must also be present in the query. This value allows for numerical sorting as noted above in 'meta_value'.
  • 'post__in' - Preserve post ID order given in the post__in array (available with Version 3.5).

You want the first name and the last name to be in separate custom fields, we can simply order the loop by meta_value and set the meta_key to last name. Here's a simple example of argument array you would later on pass to query_posts right before loop (see the loop below for more):

$args = array(
  'orderby'        => 'meta_value',       // Or post by custom field
  'meta_key'       => 'artist_last_name', // By which custom field
  'order'          => 'ASC',              // Ascending or Descending
  'post_type'      => 'artists',          // Just the post type
  'posts_per_page' => -1,                 // Show all available post
);

No miracles there. It gets tricky when we need to change the order to first name.

Giving user the power to change order

  • We're not gonna order the post based of artist_last_name
  • or artist_first_name
  • but artist_sort_name
  • which value we're gonna change accordingly

HTML for the metabox

We'll use the WPAlchemy class. Put this e.g. in /assets/meta-artists.php.

<table>
  <tbody>
    <tr>
      <th>
        <label>First Name:</label>
      </th>
      <td>
        <!-- Field for the first name -->
        <?php $mb->the_field('artist_first_name'); ?>
        <input
          name="<?php $mb->the_name(); ?>"
          class="artist-name"
          type="text"
          value="<?php $mb->the_value(); ?>"
        />
        <!-- Checkbox for for choosing which field to sort -->
        <span>
          <?php $mb->the_field('alphabetize'); ?>
          <input
            type="checkbox"
            name="<?php $mb->the_name(); ?>"
            value="alphabetize"
            <?php
            $mb-
          />the_checkbox_state('alphabetize'); ?>> Alphabetize First Name
        </span>
      </td>
    </tr>
    <tr>
      <th>
        <label>Lastname:</label>
      </th>
      <td>
        <!-- Field for the last name -->
        <?php $mb->the_field('artist_last_name'); ?>
        <input
          name="<?php $mb->the_name(); ?>"
          class="artist-name"
          type="text"
          value="<?php $mb->the_value(); ?>"
        />
      </td>
    </tr>
  </tbody>
</table>

<!-- The field the post are sorted to. Note that it's hidden -->
<?php $mb->the_field('artist_sort_name'); ?>
<input
  name="<?php $mb->the_name(); ?>"
  class="artist-name sort-name"
  type="hidden"
  value="<?php $mb->the_value(); ?>"
/>

Hidden fields are, well, not visible to users, and always a text field. They store data that the user never needs to see, like how to order this post.

The table is no means mandatory there, it’s just how label/field pares are usually displayed in WP backend.

This only creates the HTML for it, we need to initialize the WPAlchemy metabox class still.

PHP for the metabox

This goes into theme functions.php file.

// Metabox for artists
$custom_metabox_artist = new WPAlchemy_MetaBox(array(
  'id'          => 'artist_custom_meta', // #id
  'title'       => 'Artist Info',
  'template'    => TEMPLATEPATH . '/assets/meta-artists.php', // Location of HTML template
  'types'       => array('artists'), // Which post type
  'mode'        => WPALCHEMY_MODE_EXTRACT, // This needs to be set to EXTRACT for the sorting to work
  'save_filter' => 'cm_meta_sort' // The gist, this gets executed when saving the post
));

Pay attention to the save filter, the cm_meta_sort is a call to function, let's write that next:

// Save filter function, check the value of alphabetize custom field
// if it's set then use first name as sort name
function nd_meta_sort($meta, $post_id) {
  if ($meta['alphabetize'] == "alphabetize") {
    $meta['artist_sort_name'] = $meta['artist_first_name'];
  } else {
    $meta['artist_sort_name'] = $meta['artist_last_name'];
  }

  return $meta;
}

You pass two parameters for it:

  • $meta data array
  • and $post_id

This is how you access a specific value in an array $meta['alphabetize']. Use them like you’d use variables.

In the function we’re just checking if the alphabetize checkbox value is alphabetize, if it is, it means it’s checked and we'll set the artist_sort_name to be artist_first_name.

Final product looks something like this:

Screen shot of a WordPress metabox

Next, the loop.

The loop, calling the posts in right order

We’ll be using pre_get_posts filter here, rather than query_posts. More reading of it:

This lives in your functions.php.

function artist_list($query) {
  if ( is_admin() || ! $query->is_main_query() ) {
    return;
  }

  // Target the artists page
  if (is_tax('artist-category')) {
    $query->set('posts_per_page', '-1');
    $query->set('orderby', 'meta_value');
    $query->set('meta_key', "artist_sort_name");
    $query->set('order', 'ASC');
  }
}

add_action('pre_get_posts', 'artist_list');

Conclusion

It’s pretty easy to do.

Comments would go here, but the commenting system isn’t ready yet, sorry.

  • © 2022 Antti Hiljá
  • About
  • All rights reserved yadda yadda.
  • I can put just about anything here, no one reads the footer anyways.
  • I love u!