Show hierarchical relationship between to custom post types on admin list

I have two custom post types: “request” and “quote”. Both have the “hierarchical” flag set to true.

A ‘request’ cpt can be the parent of one or more ‘quotes’ cpt.

Read More

Since these are two different post types, WordPress admin has seperate overview lists (e.g. edit.php?post_type=request) for both.

What I am looking for is a way to format the request overview the same way a hierarchical page list is formatted, for example:

  • Request 1
    • Quote 1
    • Quote 2
  • Request 2
    • Quote 3
    • Quote 4

I only need to do this on the admin side (as I’m not using the front-end).

Related posts

1 comment

  1. The way WordPress works for hierarchical post types is that post parent should always be the same post type of children.

    For that reason, in admin screens, the post type in edit.php is pretty hardcoded.

    However, what WordPress do is to run a WP_Query where ‘post_type’ argument is the current one.

    So, what you can do?

    First of all, if you want to merge the 2 cpt admin screens, you can hide the admin screen for ‘quote’. It is very simple: when you register the post type, simply set ‘public’ argument to false:

    $args = array(
       // all other args here
      'hierarchical' => TRUE
      'public' => FALSE
    );
    register_post_type( 'quote', $args );
    

    Doing so, the quote admin screen is not shown, so you have only the ‘request’ one.

    What you have to do is intercept the query and set 'post_type' argument to array containg both ‘request’ and ‘quote’.

    The problem doing so is that the global ‘post_type’ variable will be set to that array, but WordPress expects ‘post_type’ a string ending up in some errors. To avoid that errors, we should find a hook to force the global 'post_type' to be the string ‘request’.

    After a quick look at the file responsible to output the admin posts table: 'wp-admin/includes/class-wp-posts-list-table.php' I found that a good hook for the scope can be the filter hook 'edit_posts_per_page'.

    So the code:

    add_action( 'pre_get_posts', function( $query ) {
      if ( is_admin() ) { // do nothing if not is admin
        $scr = get_current_screen();
        // if in the right admin screen
        if ( $scr->base === 'edit' && $scr->post_type === 'request' ) {
          // force query to get both 'request' and 'quote' cpt
          $query->set( 'post_type', array( 'request', 'quote' ) );
          // force global $post_type to be = 'request' if is an array 
          add_filter( 'edit_posts_per_page', function( $perpage ) {
            global $post_type;
            if ( is_array( $post_type ) && in_array( 'request', $post_type ) ) {
              $post_type = 'request';
            }
            return $perpage; // we don't want to affect $perpage value 
          } );
        }
      }
      return $query;
    } );
    

Comments are closed.