How to target a specific custom post type post and its all children and grandchildren?

I will really appreciate your help to create a function that will check by a given $my_custom_post_type_name and $post_name and return true if the current page / post is a $post_name or $post_name_child or $post_name_grandchild and so on based on the provided parent slug and custom post type.

Let's say this is the structure:

my_custom_post_type_name

-first_page (example.com/my_custom_post_type_name/first_page)
--first_page_child
---first_page_grandchild
--first_page_child_2

-second_page
--second_page_child
---second_page_grandchild

I want to be able to target all pages that are first_page or its children / grandchildren and so on.

Something like:

if( my_custom_function('my_custom_post_type_name', 'first_page') ){

//do stuff if the current page is 'first_page' OR 'first_page_child' OR 'first_page_grandchild' OR 'first_page_child_2'

}

After some research I was able to come up with the following (still need to change first_page ID to be a slug, and to get rid of in_array() check so the custom function will do it all at once):

function get_posts_children($CPT, $parent_id){

        $children = array();
        $children[] = $parent_id;

        // grab the posts children
        $posts = get_posts( 
            array( 
                'numberposts' = -1, 
                'post_status' = 'publish', 
                'post_type' = $CPT, 
                'post_parent' = $parent_id
            )
        );

        // now grab the grand children
        foreach( $posts as $child ){

            // call the same function again for grandchildren   
            $gchildren = get_posts_children($CPT, $child-ID);
            // merge the grand children into the children array
            if( ! empty($gchildren) ) {
                $children = array_merge($children, $gchildren);
            }
        }

        // merge in the direct descendants we found earlier
        $children = array_merge($children, $posts);

        return $children;
    }

//and then (where 3060 is the first_page ID):

global $post;

if( in_array( $post-ID, get_posts_children( 'my_custom_post_type_name', 3060 ) ) ) {
  //do stuff. As it is now, it is working fine. But would really like to simplify it and to change the first_page ID to be a slug for obvious reasons.
}

Topic children functions slug custom-post-types Wordpress

Category Web


Ok I found a way to do that. I hope it will help someone who is facing the same issue. If you want to target only specific posts based on the parent slug or ID (both will work) and a given post type (custom_post_type or a page or a post).

In this case, only affecting:

my_custom_post_type_name

-first_page (example.com/my_custom_post_type_name/first_page)
--first_page_child
---first_page_grandchild
--first_page_child_2

But without affecting:

my_custom_post_type_name

-second_page
--second_page_child
---second_page_grandchild

Here is the function you need:

function get_posts_children($CPT, $post_slug){

    //check if the $post_slug is a string.
    if(is_string($post_slug)){
        $parent_page_obj = get_page_by_path( $post_slug, '', $CPT );
        if ( ! empty( $parent_page_obj ) ) {
            $parent_id = $parent_page_obj->ID; //assign $parent_id to be an integer.
        }
    }

    //check if the $post_slug is an integer(ID) for when the function is calling itself (for grandchildren check).
    if( is_int ( $post_slug ) ){
        $parent_id = $post_slug; //if $post_slug is an integer assign it to $parent_id
    }

    $group = array();
    $group[] = $parent_id; //add $post_slug (in this case coverted to $parent_id already) to be a part of the array. (you can remove it if you only need the children and grandchildren of a given post ID or slug).

    // grab the direct children of the post by given $post_slug or post ID.
    $direct_children = get_posts( 
        array( 
            'numberposts' => -1, 
            'post_status' => 'publish', 
            'post_type' => $CPT, 
            'post_parent' => $parent_id
        )
    );

    // now grab the grandchildren
    foreach( $direct_children as $child ){
        $grandchildren = get_posts_children($CPT, $child->ID); // call the same function again  for grandchildren   

        if( ! empty($grandchildren) ) {
            $group = array_merge($group, $grandchildren); // merge the grandchildren into the children array
        }
    }

    $group = array_merge($group, $direct_children); // merge in the $direct_children into the group array.

    return $group; //return an array of all the IDs found.
}

And then to target these posts/pages from anywhere and compare them to current displayed page/post you need:

global $post;

if(in_array($post->ID, get_posts_children('my_custom_post_type_name', 'first_page'))){
  //do something. If the current displayed page is 'first_page' OR 'first_page_child' OR 'first_page_grandchild' OR 'first_page_grandchild'.

  //But it will not affect if the current displayed page is 'second_page' OR 'second_page_child' OR 'second_page_grandchild'.

  //This is handy to do many things. 
  //I'm currently using it to redirect some specific custom user capabilities from restricted areas. 
  //But allowing them to access second_page and its children for that matter.    
}

I guess you want to check if the active page is a) of your custom post type and b) one of the ancestors is your parameter.

First of all, i would advise you to keep on using the ID, as slugs and titles can be non-unique, so one slug could be different posts.

Second, you can use wordpress' own get_post_ancestors-function. Use it like this:

function wpse365743_has_ancestor($postid,$ancestor_id){
  //get all parents and ancestors of $postid
  $ancestors = get_post_ancestors($postid);
  //add in the current post id
  $ancestors = array_merge(array($postid),$ancestors);
  //is the searched ancestor id in the ancestors?
  return in_array($ancestor_id,$ancestors); 
}

You can now use this anywhere on your page like this:

if(('my_post_type_name' == get_post_type($check_post_id)) && wpse365743_has_ancestor($check_post_id,$searched_ancestor_id)){
   //do something
}

Happy Coding!

About

Geeks Mental is a community that publishes articles and tutorials about Web, Android, Data Science, new techniques and Linux security.