Categories
Past Tutorials WordPress

Custom Sort Backend Posts In WordPress

There were probably instances where you wished you could custom sort posts in WordPress. There are a few very good plugins out there that accomplish this task. This brief tutorial will show you how to apply the sorting function to the All Post Screen itself, bypassing the need to create a submenu or settings page.

Queueing the scripts

Let’s queue the scripts we’ll be using on the backend. We’ll be using the sortable widget from jQuery UI, and our own admin.js. Many are not aware that WordPress includes many popular scripts and libraries, including jQuery UI. In our functions.php file, we’ll add the following:

function admin_enqueue() 
{
	wp_enqueue_script('jquery-ui-sortable');
	wp_enqueue_script('adminjs',get_template_directory_uri().'/js/admin.js',array( 'jquery-ui-sortable' ),'1', true);
}

add_action( 'admin_enqueue_scripts', 'admin_enqueue' );

Make sure to add jquery-ui-sortable in your admin.js dependency parameter, we want to make sure jquery-ui-sortable is loaded BEFORE our admin.js script is loaded.

Applying the sortable plugin

Let’s open our admin.js and add the following:

( function( $ ) {

	"use strict";
	
	//Attach sortable to the tbody, NOT tr
	var tbody = $("body.post-type-post tbody#the-list");
	
	tbody.sortable({
		cursor: "move",
	    update: function (event, ui) {
	        
	    }
	});

})( jQuery );

It took a while to figure out that in order to sort a table row, you have to attach the function the the table body. So let’s check out our progress so far…

Cool! Table row dragging and dropping is now working. But how do we save the order?

Saving the order

There are a few ways to skin this cat, but I’m going to use the ajax approach. Let’s make some modifications to our admin.js:

( function( $ ) {

	"use strict";
	
	//Attach sortable to the tbody, NOT tr
	var tbody = $("body.post-type-post tbody#the-list");
	var data = {
			'action': 'sort-posts', //Set an action for our ajax function
		};
	
	tbody.sortable({
		cursor: "move",
	    update: function (event, ui) {
	    	//grabs all of the ids of the post rows and pushes them into an array
	        data.sort = $(this).sortable('toArray');
	        
	        $.post(ajaxurl, data)
	        .done(function(response) {
				//console.log(response);
				alert( "Sorting Successful." );
			}).fail(function() {
				alert( "Uh Oh! You tried to divide by zero." );
			});
	    }
	});

})( jQuery );

Did you know: The ajaxurl is available as a global in external javascript files queued on the administration side.

Ok, we’re half way there. We prepped our admin.js to pass the ids array of the sorted table rows to an ajax function that we will create now. In the functions.php, add:

add_action('wp_ajax_sort-posts', '_custom_sort_posts');

function _custom_sort_posts()
{	
	if( empty($_POST['action'])){return;}

	$data = array_map('sanitize_text_field',$_POST['sort']);
	$messages = array();

	foreach($data as $k => $v)
	{
		$id = ltrim($v, 'post-'); //Trim the "post-" prefix from the id
		$index = ($k + 1); //Make sure our sorting index starts at #1
	
		update_post_meta( $id, '_custom_sort_post_order', $index );
	}
	
	exit();
}

What’s happening here is that our function is looping through the “sort” array passed from the admin.js script. We trimmed the post- prefix from the id so we can get the correct post id. Finally, we updated the post with the _custom_sort_post_order meta key. Of course you can replace that handle with your own. So now we have our post order saved for the All Posts Screen. But you probably have noticed that the order is still not reflected upon refresh. So let’s fix that.

Displaying the sort order

We’re going to use the pre_get_posts action to display the custom sort order. Go back to the functions.php and add the following function and hook:

add_action( 'pre_get_posts', '_custom_sort_orderby' );

function _custom_sort_orderby( $query ) 
{
	global $pagenow;

	/*
	*
	* Make sure pre_get_posts only fires on the All Posts Screen
	* with the series of checks below. If not, bad things will happen site wide.
	*
	*/
	
    if( !is_admin() ){ return; } //If we're not in the backend, quit
    
    if( isset( $_GET['post_type'] ) ) //Make sure post type is set
    {	    
    	//Make sure we're on the All Post Screen
		if( 'edit.php' === $pagenow && 'post' === $_GET['post_type'] )  
		{
			$query->set('meta_key','_custom_sort_post_order');
	        $query->set('orderby','meta_value');
	        $query->set('order', 'ASC');
		}	    
    }
}

It’s imperative that you only invoke our custom sort  hook on the All Posts Screen. If not, pre_get_posts will apply our custom sort query site wide and we don’t want that.

Now you’ll see the All Post Screen display the proper sort order upon refresh.

Applying the custom sort order to your templates

Use the WP_Query class to call your custom sort order on your archive templates

$args = array(
	'meta_key'   => '_custom_sort_post_order',
	'orderby'  => array( 'meta_value_num' => 'ASC' ),
);

$query = new WP_Query( $args );

And there you have it. Of course you can expand this brief tutorial to include custom post types, taxonomy sorting, etc. Tell me what to think in the comments section below.