Here’s a problem I encountered a while ago. The list page for a custom post type in WordPress’s admin area started taking longer and longer to load, until one day it just started timing out completely. I googled all over the place and the solutions I kept encountering were the same, albeit in different flavours: set hierarchical to false in the custom post type’s definition.

Back in the real world…

There are a few reasons why this “solution” is inadequate:

  1. It doesn’t actually offer any explanation of why the admin page grinds to halt when trying to list hierarchical post types. Instead, it quietly sweeps it under the carpet.
  2. In the case of conditionally setting hierarchical to false depending on whether or not is_admin() == true, it’s inconvenient and impractical. If a CPT has been made hierarchical, it’s likely because there are (or will be) relationships within collections of posts that need to be maintained. This is impossible to do if you disable the CPT’s hierarchical structure “only” in admin. It makes no sense.
  3. If you used a plugin (like CustomPress) to create the CPT, there’s no way to disable hierarchical conditionally, so option #2 is no option at all (unless you want to hack the plugin, which would be ill-advised).

Oh my gawd! You’re awesome! It’s fixed!

The types of responses I was seeing when the above was offered as a solution… Yawn. Meanwhile, I was thinking, WTF? This is telling me nothing. I doubt my employer is going to accept disabling hierarchy for a CPT that has 20,000 posts, and many existing hierarchical relationships to boot.

So, I thought, fuck what these people are saying. I need to find the real cause, and then maybe I can come up with and implement an acceptable solution. As is often the case, I had to go diving into the WordPress source code — one of my least favourite activities.

I narrowed down the problem to /wp-admin/includes/class-wp-posts-list-table.php. Specifically, this block, beginning around line 1100:

if ( $bulk )
    $dropdown_args['show_option_no_change'] =  __( '— No Change —' );

/**
 * Filter the arguments used to generate the Quick Edit page-parent drop-down.
 *
 * @since 2.7.0
 *
 * @see wp_dropdown_pages()
 *
 * @param array $dropdown_args An array of arguments.
 */
$dropdown_args = apply_filters( 'quick_edit_dropdown_pages_args', $dropdown_args );

wp_dropdown_pages( $dropdown_args );

wp_dropdown_pages is used to render a drop-down list that’s used in the quick-edit menu. The page was hanging at this point, presumably because it was running a bunch of queries to retrieve all the pages. Here’s the thing: our custom post didn’t even use this drop-down list. I soon discovered that wp_dropdown_pages defaults to retrieving all pages and child pages, and lists the complete hierarchy. And, of course, it’s doing this for each post on the CPT’s list page. I guess it wasn’t considered what might happen when there are thousands of hierarchical pages!

Fortunately, there's an accompanying filter (above) so that you can alter wp_dropdown_pages's behaviour. This is done by limiting the depth of the retrieval to only 1 level (learn more about it here).

function alter_wp_dropdown_pages_args($args) {

    // Because I only care about it happening on the list page for my special post type. 
    if (is_admin() && preg_match('@^/wp-admin/edit.php@', $_SERVER['REQUEST_URI']) && $_GET['post_type'] == 'my-special-post-type') {
        $args['depth'] = 1;
    }
}
add_filter('quick_edit_dropdown_pages_args', 'alter_wp_dropdown_pages_args', 10, 1);

As soon as I did this, the CPT’s list page started loading as quickly as any other page in admin. But this only became an issue in the first place because a menu was being loaded that isn’t actually used. Ideally, what I really want to do is prevent the quick-edit menu from loading at all. I discovered this (once I knew what I was looking for):

function remove_quick_edit($actions) {
    global $post;

    if ($post->post_type == 'my-special-post-type') {
        unset($actions['inline hide-if-no-js']);
    }
}
add_filter('post_row_actions','remove_quick_edit', 10, 2);

Either of the above solutions will eliminate the slow-loading post list in admin. I found that the issue was limited to hierarchical CPT’s that had a large number of posts. Those are ways to fix it.