如何使用wp_list_ages()隐藏草稿页的子页?

时间:2014-08-30 作者:mike23

我正在用wp\\u list\\u pages()显示一个简单的网站地图

$args = array(
    \'sort_column\' => \'menu_order\',
    \'title_li\' => \'\',
    \'post_status\'  => \'publish\'
);

wp_list_pages( $args );
问题是,默认情况下,这还会显示草稿页的已发布子级,如下所示:

第1页(已发布)->显示

---第2页(草稿)->未显示

------第3页(已发布)->显示

我想要实现的是:

第1页(已发布)->显示

---第2页(草稿)->未显示

------第3页(已发布)->not 已显示

我怀疑一个定制的步行者会这样做,但我永远无法真正理解它们是如何工作的。。

有没有办法隐藏这些子页面而不必将它们全部设置为草稿?

Edit:

为了澄清,让我们尝试一些图像。因此,您有一个包含页面完整层次结构的树。我们正在爬树。当我们遇到一根树枝时,我们就把它砍倒了。当然,所有其他附加到它的分支也会被丢弃(无论它们是否是草稿)。我希望这能更好地解释它。

下面是一个层次较深的示例:

第1页(已发布)->显示

---第2页(草稿)->未显示<;-Cut here and exclude all further children

------第3页(已发布)->未显示

---------第4页(已发布)->未显示

------------第5页(草稿)->未显示

---------------第6页(已发布)->未显示

4 个回复
最合适的回答,由SO网友:birgire 整理而成

以上回答很好。我接受了挑战,试图找到另一种方法来解决这个问题。

Theexclude 参数:

我们可以尝试:

\'exclude\' => wpse_exclude_drafts_branches()
其中:

function wpse_exclude_drafts_branches()
{
    global $wpdb;
    $exclude = array();
    $results = $wpdb->get_col( "SELECT ID FROM {$wpdb->posts} where post_status = \'draft\' AND post_type = \'page\' " );
    $exclude = array_merge( $exclude, $results) ;
    while ( $results ):
        $results = $wpdb->get_col( "SELECT DISTINCT ID FROM {$wpdb->posts} WHERE post_type = \'page\' AND post_status = \'publish\' AND post_parent > 0 AND post_parent IN (" .  join( \',\', $results ) . ") " );
        $exclude = array_merge( $exclude, $results) ;
    endwhile;
    return join( \',\', $exclude );
}
查询的数量取决于树的深度。

Update:

Theexclude_tree 参数:

我刚刚注意到exclude_tree 中提到的参数Codex page, 因此,我想知道这是否可以(未经测试)排除整个draft节点分支:

$exclude = get_posts(
    array( 
        \'post_type\'      => \'page\',
        \'fields\'         => \'ids\',
        \'post_status\'    => \'draft\',
        \'posts_per_page\' => -1,
   )
);
然后使用:

\'exclude_tree\' => join( \',\', $exclude ),
使用wp_list_pages().

SO网友:gmazzap

诚然,我发现自定义Walker很烦人:有时用一个简单的过滤器可以完成的事情需要对整个类进行编码,但可能是我,我真的不喜欢WordPress Walker背后的逻辑。

这就是为什么我经常在元素被移动之前使用技巧对其进行过滤的原因。这是一门非常简单的学步课程:

class FilterableWalker extends Walker {

  private $walker;

  function __construct( Walker $walker ) {
    $this->walker = $walker;
  }

  function walk( array $elements = null, $max_depth = null ) {
    $args = func_get_arg( 2 );
    $filtered = apply_filters( \'filterable_walker_elements\', $elements, $args, $this );
    if ( is_array( $filtered ) ) {
      $walk_args = func_get_args();
      $walk_args[0] = $filtered ;
      return call_user_func_array( array( $this->walker, \'walk\' ), $walk_args );
    }
    return call_user_func_array( array( $this->walker, \'walk\' ), func_get_args() );
  }

  function getWalker() {
    return $this->walker;
  }

  function getWalkerClass() {
    return get_class( $this->getWalker() );
  }
}
这是一个多用途的、可重用的walker,可以在将项传递给必须在构造函数中传递的real walker之前对其进行过滤。

在您的情况下,您应该这样做:

$args = array(
  \'sort_column\' => \'menu_order\',
  \'title_li\' => \'\',
  \'post_status\'  => \'publish\',
  \'skip_draft_children\' => 1, // <- custom argument we will use in filter callback
  \'walker\' => new FilterableWalker( new Walker_Page ) // <-- our walker
);

$pages = wp_list_pages( $args );
现在,您可以编写一个过滤器回调来使用\'filterable_walker_elements\' 钩子发射者FilterableWalker 类别:

add_filter( \'filterable_walker_elements\', function( $elements, $args, $filterable ) {

  $walker = $filterable->getWalkerClass();

  if (
    $walker === \'Walker_Page\'
    && isset( $args[\'skip_draft_children\'] )
    && $args[\'skip_draft_children\'] // <-- our custom argument
  ) {
    $ids = array_filter( array_unique( wp_list_pluck( $elements, \'post_parent\' ) ) );
    $parents = get_posts(
      array(
        \'post__in\' => $ids,  \'post_status\' => \'publish\',
        \'fields\'   => \'ids\', \'post_type\'   => \'page\',
        \'nopaging\' => true
      )
    );
    $pages = $elements;
    foreach( $pages as $i => $page ) {
      if ( $page->post_parent !== 0 && ! in_array( $page->post_parent, $parents, true ) ) {
        unset($elements[$i]);
        $self_i = array_search( $page->ID, $parents, true );
        if ( $self_i !== FALSE ) unset( $parents[$self_i] );
      }
    }
  }
  return $elements;

}, 10, 3 );

SO网友:Nicolai Grossherr

利用习惯Walker 其实没那么难,基本上是这样的:

创建class;

类是使用这些变量的变量和函数的集合。

作者extending 另一个;

扩展类或派生类具有基类[…]的所有变量和函数以及您在扩展定义中添加的内容。

像这样:

class Extended_Class extends Base_Class {
   // code
}
这使您可以更改/扩展methods aka functions 已扩展的基类的。此外,您还可以通过向扩展类添加方法或变量来进行扩展。

要充分理解和利用这些可能性,有必要深入了解OOP: Classes and Objects PHP的各个方面。但这在这里太多了,无论如何都不是正确的地方wp_list_pages(). 我们要扩展以使用的类wp_list_pages(), 这个Walker_Page 类别-source -, 本身是通过扩展类派生的Walker - source.

按照上述模式,我们将执行相同的操作:

class Wpse159627_Walker_Page extends Walker_Page {
    // code
}
现在Walker_Page 有两个变量-$tree_type$db_fields - 和四种方法-start_lvl(), end_lvl(), start_el()end_el(). 这些变量与我们无关,关于这些方法,我们至少需要仔细研究一下start_el()end_el().

首先要看到的是,这两种方法都有参数$page:

@param object$页面数据对象。

包含我们需要的所有相关数据,如post_parent, 而且几乎是WP_Post/$post/"$page" 对象由get_pages() return

包含与请求匹配的所有页面的数组,如果失败,则为false。返回的数组是“page”对象的数组。

inside 这个wp_list_pages() 作用

我们需要检查的是当前页面父级的post状态,为此函数get_post_status() 可用。像determined一样,我们可以使用可用的$page对象来执行此操作。

$page_parent_id     = $page->post_parent;
$page_parent_status = get_post_status( $page_parent_id );
现在,我们可以使用它来检查当前页面父级的状态:

if ( $page_parent_status != \'draft\' ) {
    // code
}
让我们在扩展的Walker类中实现它:

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";
        }
    }
}
新类可用于wp_list_pages() 像这样:

$args = array(
    \'sort_column\' => \'menu_order\',
    \'title_li\'    => \'\',
    \'post_status\' => \'publish\',
    \'walker\'      => new Wpse159627_Walker_Page
);
wp_list_pages( $args );

<小时>Edit:

出于完整性原因添加此项,以便使此项适用于树、所有子代,而不仅仅是子代。虽然这不是最好的方法,但已经提出了足够多的其他建议。

因为WordPress\'get_ancestors()get_post_ancestors() 函数不是用来获取草稿的,我构建了一个函数来获取每个祖先:

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;
}
此外,有必要了解这些祖先的身份。可通过以下功能完成:

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;
}
这可用于替换上述条件:

$ancestors = wpse159627_get_all_post_ancestors( $page->ID );
$ancestors_status = wpse159627_get_all_status( $ancestors );
if ( ! in_array( \'draft\', $ancestors_status ) ) {
    // code
}

SO网友:Nicolai Grossherr

这个答案提供了另一种方法。代码非常自我解释,我将所有内容都命名为非常字面,以便更好地理解。我所做的是构造一个函数来确定草稿页及其子代,它可以与exclude 的参数wp_list_pages().

Helper Function:

function wpse159627_exclude_draft_sub_trees() {
    $pages_any_status = get_posts(
        array(
            \'post_type\'              => \'page\',
            \'post_status\'            => \'any\',
            \'posts_per_page\'         => -1,
            // make this as inexpensive as possible
            \'cache_results\'          => false,
            \'update_post_meta_cache\' => false,
            \'update_post_term_cache\' => false
        )
    );
    $draft_posts_ids = array_filter(
        array_map(
            function ( $array_to_map ) {
                if( $array_to_map->post_status == \'draft\' ) {
                    return $array_to_map->ID;
                } else {
                    return null;
                }
            },
            $pages_any_status
        )
    );
    $children_of_draft_posts_arr_of_obj = array();
    foreach ( $draft_posts_ids as $draft_id ) {
        $children_of_draft_posts_arr_of_obj[] = get_page_children(
            $draft_id,
            $pages_any_status
        );
    }
    $children_of_draft_posts = array();
    foreach ( $children_of_draft_posts_arr_of_obj as $object ) {
        foreach ( $object as $key => $value ) {
            $children_of_draft_posts[] = $value;
        }
    }
    $children_of_draft_posts_ids = array_map(
        function ( $array_to_map ) {
            return $array_to_map->ID;
        },
        $children_of_draft_posts
    );
    $exclude_from_list_pages = array_merge(
        $draft_posts_ids,
        $children_of_draft_posts_ids
    );
    $exclude_comma_sep_list = implode(\',\',$exclude_from_list_pages);
    return $exclude_comma_sep_list;
}

Usage:

$args = array(
    \'sort_column\' => \'menu_order\',
    \'title_li\'    => \'\',
    \'post_status\' => \'publish\',
    \'exclude\'     => wpse159627_exclude_draft_sub_trees()
);
wp_list_pages( $args );
如果您使用的是小于5.3的PHP版本,则需要一个没有闭包的版本。在我的书中,明确地说,对5.4以下的任何东西进行操作都是错误的。但我非常清楚WordPress的要求,即PHP 5.2.4,因此,您可以这样做:

function wpse159627_extract_ids( $array_to_map ) {
    return $array_to_map->ID;
}
function wpse159627_extract_ids_of_drafts( $array_to_map ) {
    if( $array_to_map->post_status == \'draft\' ) {
        return $array_to_map->ID;
    } else {
        return null;
    }
}
function wpse159627_exclude_draft_sub_trees_old_php() {
    $pages_any_status = get_posts(
        array(
            \'post_type\'              => \'page\',
            \'post_status\'            => \'any\',
            \'posts_per_page\'         => -1,
            // make this as inexpensive as possible
            \'cache_results\'          => false,
            \'update_post_meta_cache\' => false,
            \'update_post_term_cache\' => false
        )
    );
    $draft_posts_ids = array_filter(
        array_map(
            \'wpse159627_extract_ids_of_drafts\',
            $pages_any_status
        )
    );
    $children_of_draft_posts_arr_of_obj = array();
    foreach ( $draft_posts_ids as $draft_id ) {
        $children_of_draft_posts_arr_of_obj[] = get_page_children(
            $draft_id,
            $pages_any_status
        );
    }
    $children_of_draft_posts = array();
    foreach ( $children_of_draft_posts_arr_of_obj as $object ) {
        foreach ( $object as $key => $value ) {
            $children_of_draft_posts[] = $value;
        }
    }
    $exclude_from_list_pages = array_merge(
        $draft_posts_ids,
        $children_of_draft_posts_ids
    );
    $exclude_comma_sep_list = implode(\',\',$exclude_from_list_pages);
    return $exclude_comma_sep_list;
}

结束

相关推荐

Taxonomy filter all children

我有一个自定义分类过滤器,它将过滤选定分类中的所有页面。我希望代码能够选择该分类法中的页面,以及千页的子页面。这是密码。add_action(\'restrict_manage_posts\', \'restrict_manage_posts_section\'); function restrict_manage_posts_section() { global $post_type; if ( is_object_in_taxonomy( $post_t