A good[ish] website
Web development blog, loads of UI and JavaScript topics
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.
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.
artist_last_name
artist_first_name
artist_sort_name
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.
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$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:
Next, the loop.
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');
It’s pretty easy to do.
Comments would go here, but the commenting system isn’t ready yet, sorry.