Making use of a custom Walker
is actually not that hard, it basically goes like this:
Create a class;
A class is a collection of variables and functions working with these variables.
By extending another one;
The extended or derived class has all variables and functions of the base class [...] and what you add in the extended definition.
Like this:
class Extended_Class extends Base_Class {
// code
}
Which gives you the possibility to change/extend the methods aka functions of the base class that has been extended. Additionally you can/could extend by adding methods or variables to the extended class.
- To fully understand and make use of the possibilities it is necessary to get deeper into OOP: Classes and Objects aspects of PHP. But that would be too much here and not the rightplace anyway.
So lets get back to WordPress and wp_list_pages()
. The class we want to extend to make use of with wp_list_pages()
, the Walker_Page
class - source -, itself has been derived by extending the class Walker
- source.
Following the above explained schema we are going to do the same:
class Wpse159627_Walker_Page extends Walker_Page {
// code
}
Now Walker_Page
has two variables - $tree_type
and $db_fields
- and four methods - start_lvl()
, end_lvl()
, start_el()
and end_el()
. The variables won't concern us, regarding the methods we are at least have to take a closer look at start_el()
and end_el()
.
The first thing to see is that those two methods have the parameter $page
:
@param object $page Page data object.
Which contains all the relevant data we need, like the post_parent
, and is pretty much a WP_Post
/$post
/"$page
" object. Given back by the get_pages()
return
An array containing all the Pages matching the request, or false on failure. The returned array is an array of "page" objects.
inside the wp_list_pages()
function.
What we need to check is the post status of the current page parent, for doing this the function get_post_status()
is available. Like determined we can use the $page object available to do so.
$page_parent_id = $page->post_parent;
$page_parent_status = get_post_status( $page_parent_id );
Now we can use this to check against the status of the currents page parent:
if ( $page_parent_status != 'draft' ) {
// code
}
Lets implement it in our extended Walker class:
class Wpse159627_Walker_Page extends Walker_Page {
function start_el( &$output, $page, $depth = 0, $args = array(), $current_page = 0 ) {
$page_parent_id = $page->post_parent;
$page_parent_status = get_post_status( $page_parent_id );
if ( $page_parent_status != 'draft' ) {
if ( $depth )
$indent = str_repeat("\t", $depth);
else
$indent = '';
extract($args, EXTR_SKIP);
$css_class = array('page_item', 'page-item-'.$page->ID);
if( isset( $args['pages_with_children'][ $page->ID ] ) )
$css_class[] = 'page_item_has_children';
if ( !empty($current_page) ) {
$_current_page = get_post( $current_page );
if ( in_array( $page->ID, $_current_page->ancestors ) )
$css_class[] = 'current_page_ancestor';
if ( $page->ID == $current_page )
$css_class[] = 'current_page_item';
elseif ( $_current_page && $page->ID == $_current_page->post_parent )
$css_class[] = 'current_page_parent';
} elseif ( $page->ID == get_option('page_for_posts') ) {
$css_class[] = 'current_page_parent';
}
$css_class = implode( ' ', apply_filters( 'page_css_class', $css_class, $page, $depth, $args, $current_page ) );
if ( '' === $page->post_title )
$page->post_title = sprintf( __( '#%d (no title)' ), $page->ID );
$output .= $indent . '<li class="' . $css_class . '"><a href="' . get_permalink($page->ID) . '">' . $link_before . apply_filters( 'the_title', $page->post_title, $page->ID ) . $link_after . '</a>';
if ( !empty($show_date) ) {
if ( 'modified' == $show_date )
$time = $page->post_modified;
else
$time = $page->post_date;
$output .= " " . mysql2date($date_format, $time);
}
}
}
function end_el( &$output, $page, $depth = 0, $args = array() ) {
$page_parent_id = $page->post_parent;
$page_parent_status = get_post_status( $page_parent_id );
if ( $page_parent_status != 'draft' ) {
$output .= "</li>\n";
}
}
}
The new class can be used with wp_list_pages()
like this:
$args = array(
'sort_column' => 'menu_order',
'title_li' => '',
'post_status' => 'publish',
'walker' => new Wpse159627_Walker_Page
);
wp_list_pages( $args );
Edit:
Adding this for completeness reasons, so to make this work for trees, all descendants, not just children. It is not the optimal way to do it though, enough other suggestion have been made.
Because WordPress' get_ancestors()
and get_post_ancestors()
functions aren't made to get drafts too, I constructed a function to get every ancestor:
function wpse159627_get_all_post_ancestors( $post_id ) {
$post_type = get_post_type( $post_id );
$post = new WP_Query(
array(
'page_id' => $post_id,
'include' => $post_id,
'post_type' => $post_type,
'post_status' => 'any',
'cache_results' => false,
'update_post_meta_cache' => false,
'update_post_term_cache' => false
)
);
$post = $post->posts[0];
if (
! $post
|| empty( $post->post_parent )
|| $post->post_parent == $post->ID
) {
return array();
}
$ancestors = array();
$id = $ancestors[] = $post->post_parent;
while (
$ancestor = new WP_Query(
array(
'page_id' => $id,
'include' => $id,
'post_type' => $post_type,
'post_status' => 'any',
'cache_results' => false,
'update_post_meta_cache' => false,
'update_post_term_cache' => false
)
)
) {
$ancestor = $ancestor->posts[0];
if (
empty( $ancestor->post_parent )
|| ( $ancestor->post_parent == $post->ID )
|| in_array( $ancestor->post_parent, $ancestors )
) {
break;
}
$id = $ancestors[] = $ancestor->post_parent;
}
return $ancestors;
}
Additionally it is necessary to get the status of those ancestors. Which can be done with the following function:
function wpse159627_get_all_status( $ids ) {
$status_arr = array();
foreach ( $ids as $id ) {
$post_type = get_post_type( $id );
$post = new WP_Query(
array(
'page_id' => $id,
'include' => $id,
'post_type' => $post_type,
'post_status' => 'any',
'cache_results' => false,
'update_post_meta_cache' => false,
'update_post_term_cache' => false
)
);
$post = $post->posts[0];
$status_arr[] = $post->post_status;
}
return $status_arr;
}
This can be used to replace above explained conditional:
$ancestors = wpse159627_get_all_post_ancestors( $page->ID );
$ancestors_status = wpse159627_get_all_status( $ancestors );
if ( ! in_array( 'draft', $ancestors_status ) ) {
// code
}