Hiding Posts in WordPress with a Custom Post Status

Are you looking for a way to hide WordPress posts from both the frontend and the backend?

By default, WordPress displays all post statuses on the backend post list admin screen.  To specifically tell it to exclude a post status, you’ll want to register the post status with the following arguments:

function themeprefix_register_inactive_post_status() {
         'label' => 'Inactive',
         'public' => false,
         'internal' => true,
         'protected' => true,
         'private' => false,
         'exclude_from_search' => true,
         'publicly_queryable' => false,
         'show_in_admin_all_list' => false,

add_action( 'init', 'themeprefix_register_inactive_post_status' );

I recommend prefixing the post status to avoid potential conflicts — just incase WordPress or a plugin decides to register an inactive post status themselves.

One unintuitive thing is that “private” actually needs to be set to “false” as shown above.

So why would you do this?

In most cases, I’d recommend differentiating types of posts by using a different post type instead of a different post status.

However, in my case, I wanted a way to “soft delete” posts without moving them to the trash.  See, the trash auto-empties after 30 days, and if you move all of the posts to the trash at once, then in 30 days, all of them will be deleted at once, and this could cause some performance issues if you’re dealing with thousands of posts.

So instead I made an inactive post status, and wrote a WP CLI script to set this post status on the content I planned on deleting.  This way I could restore the content if needed, so it’s truly a “soft delete”.  Then, when I want to permanently delete it, I’ll write another custom WP CLI script that permanently deletes them.  This script will sleep at set intervals and also will take other measures to make sure that we don’t cause performance issues!


Setting up PhpStorm, Xdebug to work with WordPress PHPUnit Testing

I’m a huge fan of using Xdebug and recently I ran into some issues with writing PHPUnit tests for my WordPress theme.  It took me longer to debug than usual because I couldn’t get PhpStorm to stop at my debug breakpoint.  Actually, I couldn’t get Xdebug to work at all — if I had Xdebug enabled, PHPUnit wouldn’t even run — it would just hang.

I tracked the issue down to this line in tests/bootstrap.php

system( WP_PHP_BINARY . ' ' . escapeshellarg( dirname( __FILE__ ) . '/install.php' ) . ' ' . escapeshellarg( $config_file_path ) . ' ' . $multisite );

A Google search brought me to this closed WP Core trac ticket.

That ticket has a helpful suggestion, which I hope to immortalize here, as it is the solution to this problem.

Max. Simultaneous Connections Setting

The solution is to change the Max. simultaneous connections setting, which can be found in Default preferences -> Languages and Frameworks -> PHP -> Debug.

Increase the phpStorm max connections setting to get xdebug to work.

“Configure Php Include Paths”

The default setting is 1, change it to 3 or more!


If that doesn’t solve your problem instantly, then I shall ask, did you Configure php include paths so that the phpunit files are included?  Also, depending on your development environment, you may need to set up path mappings so that PhpStorm knows how the server files are mapped to your local ones.

Happy testing!

WordPress get_the_terms by term order [bug fix]

WordPress 4.7 introduced a change to the default term order for get_the_terms()

In earlier versions of WordPress, get_the_terms( $post_id, $taxonomy_name) returned the terms in the same order in which the terms were added to the post.  But, starting with WordPress 4.7, the terms are being returned in alphabetical order by term slug.  (Note:  This is for non-hierarchical terms.  I’m not sure if this is true for hierarchical  terms.)

As you may know, get_the_terms is a convenient function for getting the terms for a specific post ID.  It is used by many plugins, and can be used for built in taxonomies like categories and post_tag or for custom taxonomies.

get_the_terms is a cached function, meaning that WordPress caches the results, so that if it’s called repeatedly, it doesn’t have to do a database query each time.  So it’s a good function to use for performance reasons.  It’s unlikely to slow your site down.  However, it doesn’t accept an orderby argument to specify how to order the results.

So, there are 3 ways to fix this problem.

Option #1: get_terms_orderby Filter

You can add a filter on get_terms_orderby

This filter allows you to alter the $orderby part of the SQL query that WordPress uses to get the terms from the database.

Assuming that author is the name of your custom taxonomy, you can use this function:

function wpcf_author_filter_terms_order( $orderby, $query_vars, $taxonomies ) {
    if ( ! empty( $query_vars['object_ids'] )
        && ! empty( $taxonomies[0] )
        && ( 1 === count( $taxonomies ) )
        && 'author' === $taxonomies[0] )
            $orderby = 'tr.term_order';
    return $orderby;

add_filter( 'get_terms_orderby', 'wpcf_author_filter_terms_order', 10, 3 );

By the way, the above code will fix this Co-Authors Plus plugin ordering issue. The Co-Authors Plus plugin creates a taxonomy named author and uses get_the_terms. This is actually where I first saw this bug, but since it is a result of a WordPress core change, I’m sure that there are many other WordPress themes and plugins in the wild with this same issue.

Keep reading if you’re curious, as this is a solution, but not the only solution.

Option #2: use wp_get_post_terms()

Alternatively, you can simply not use get_the_terms() but instead use wp_get_post_terms() which accepts a list of overrides, which means that you can specify the orderby in the third parameter.

For example,

Instead of this:

$terms = get_the_terms( $post_id, $taxonomy );

Write this:

$terms = wp_get_post_terms( $post_id, $taxonomy, array( 'orderby' => 'term_order' ) );

The reason I prefer option #1, even though it’s more complicated, is that wp_get_post_terms is an uncached function.  On a low traffic site that’s using full page caching, it may make no difference.  But if you have a site with a lot of logged in users where caching isn’t happening, I’d recommend using a more performant method.

Option #3: use wp_get_object_terms

Similar to option #2, with the same warning about it being an uncached function.

I’m running out of steam to elaborate, and I think that the codex entry on wp_get_object_terms does a pretty good just of explaining how to use this function.

Just remember my warning about no caching.  If you’d like to see how to implement some DIY caching, here’s an example I wrote.  Adding your own caching is something you can do anywhere you want in theme and plugin development.  It’s not really hard to do once you figure out the pattern.  The tricky part is to remember to delete the transients when needed — for this case, you’ll want to delete them on save_post and delete_post, which you can see how to do in my example.  If you don’t want to worry about when to delete them, you could just create short transients — like one minute or fifteen minute ones.  Having the query run every minute is way better than having it run for every page load, which could be many times per second.   For more info, check out the WordPress Transients API.  It goes into quite some detail about how to use them for caching data.