Làm cách nào tôi có thể ẩn con của các trang nháp bằng wp_list_pages ()?


7

Tôi đang hiển thị một sơ đồ trang web đơn giản với wp_list_pages ();

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

wp_list_pages( $args );

Vấn đề là theo mặc định, điều này cũng cho thấy những đứa trẻ được xuất bản của các trang nháp, như vậy:

Trang 1 (đã xuất bản) -> hiển thị

--- Trang 2 (bản nháp) -> không hiển thị

------ Trang 3 (đã xuất bản) -> hiển thị

Những gì tôi muốn đạt được là:

Trang 1 (đã xuất bản) -> hiển thị

--- Trang 2 (bản nháp) -> không hiển thị

------ Trang 3 (đã xuất bản) -> không hiển thị

Tôi nghi ngờ một Walker tùy chỉnh sẽ thực hiện mánh khóe, nhưng tôi không bao giờ có thể thực sự hiểu cách thức hoạt động của chúng ..

Có cách nào để ẩn những trang con đó mà không cần phải đặt tất cả chúng thành bản nháp không?

Chỉnh sửa :

Để làm rõ, chúng ta hãy thử một số hình ảnh. Vì vậy, bạn có một cây với hệ thống phân cấp đầy đủ của các trang của bạn. Chúng tôi đang trèo lên cây. Khoảnh khắc chúng tôi bắt gặp một nhánh nháp, chúng tôi đã cắt nó xuống. Đương nhiên, tất cả các nhánh khác gắn liền với nó cũng bị loại bỏ (bất kể chúng có phải là bản nháp hay không). Tôi hy vọng điều đó giải thích nó tốt hơn.

Dưới đây là một ví dụ với hệ thống phân cấp hơi sâu:

Trang 1 (đã xuất bản) -> hiển thị

--- Trang 2 (bản nháp) -> không được hiển thị <- Cắt ở đây và loại trừ tất cả trẻ em khác

------ Trang 3 (đã xuất bản) -> không hiển thị

--------- Trang 4 (đã xuất bản) -> không hiển thị

------------ Trang 5 (bản nháp) -> không hiển thị

--------------- Trang 6 (đã xuất bản) -> không hiển thị

Câu trả lời:


4

Câu trả lời tuyệt vời ở trên. Tôi chấp nhận thử thách cố gắng tìm một cách khác để giải quyết vấn đề này.

Các excludetham số:

Chúng ta có thể thử:

'exclude' => wpse_exclude_drafts_branches()

Ở đâu:

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 );
}

và số lượng truy vấn phụ thuộc vào độ sâu của cây.

Cập nhật:

Các exclude_treetham số:

Tôi chỉ nhận thấy exclude_treetham số được đề cập trên trang Codex , vì vậy tôi tự hỏi liệu điều này có hoạt động (chưa được kiểm tra) để loại trừ toàn bộ các nhánh dự thảo nút:

$exclude = get_posts(
    array( 
        'post_type'      => 'page',
        'fields'         => 'ids',
        'post_status'    => 'draft',
        'posts_per_page' => -1,
   )
);

và sau đó sử dụng:

'exclude_tree' => join( ',', $exclude ),

với wp_list_pages().


1
Tôi tự hỏi nếu chúng ta có thể đơn giản hóa điều này nhiều hơn với exclude_treetham số, để loại trừ toàn bộ các nhánh dự thảo.
bạch dương

1
Tôi có thể xác nhận rằng exclude_treetùy chọn mà bạn đề xuất trong bản cập nhật của bạn không hoạt động. Điều thực sự là buồn cười, xem xét số lượng mã đã được sản xuất để trả lời câu hỏi này. Tôi cảm thấy đây nên là câu trả lời được chấp nhận.
Nicolai

1
@ialocin oh, nó có thể không thanh lịch và sâu sắc như các giải pháp khác mà bạn và GM cung cấp, nhưng tôi rất vui khi biết nó hoạt động ;-) Tôi không chắc tại sao tôi không nhận thấy thông số này trước đây trên trang Codex , nhưng tôi đoán điều đó có thể xảy ra khi đưa mũi quá sâu vào mã nguồn thay vào đó ;-)
birgire

1
Ok, có lẽ nó không phải là một giải pháp ưa thích :) Nhưng nó được tích hợp sẵn và chỉ cần khoảng 8 dòng mã bổ sung, vì vậy điều đó thực sự không tệ. Tôi đoán bạn đúng, đó là những gì xảy ra ..;)
Nicolai

2
Tôi đã xóa bình luận trước bởi vì bây giờ điều này hoạt động. Và +1. Những exclude_treegì @ialocin đã làm trong mã của mình: gọi get_page_childrenbên trong một vòng lặp. Bây giờ chức năng đó [gọi đệ quy chính nó]. ( Developer.wordpress.org/reference/fifts/wp_parse_id_list ). Bên trong một vòng lặp của các bài viết có thể có rất nhiều. Kết quả: để thực hiện nhiệm vụ này, 8 dòng được yêu cầu, nhưng có thể hàng tá hàm hundrends được gọi. Tôi không nghi ngờ gì rằng đây là cách WordPress đúng đắn, nhưng như ai đó đã nói trong The Loop: "không ai làm mọi thứ theo cách của vua WordPress ... và cảm ơn Chúa vì điều đó!" *
gmazzap

4

Trân trọng, tôi thấy những người đi bộ tùy chỉnh gây phiền nhiễu: đôi khi những gì có thể được thực hiện với một bộ lọc đơn giản đòi hỏi cả lớp phải mã hóa và, nhưng có lẽ đó là tôi, tôi không thực sự thích logic đằng sau những người đi bộ WordPress.

Đây là lý do tại sao tôi thường sử dụng một mẹo để lọc các yếu tố trước khi chúng được đi . Đây là một lớp Walker thực sự đơn giản:

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() );
  }
}

Đây là một máy đi bộ đa năng, có thể tái sử dụng, cho phép lọc các mục trước khi chúng được chuyển đến máy đi bộ thực sự phải được chuyển qua trong hàm tạo.

Trong trường hợp của bạn, bạn nên làm một cái gì đó như:

$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 );

Bây giờ bạn có thể mã hóa một cuộc gọi lại bộ lọc để lọc các trang bằng cách sử dụng 'filterable_walker_elements'hook được chạy bởi FilterableWalkerlớp:

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 );

1
Cảm ơn câu trả lời chi tiết của bạn! Mặc dù nó dường như không làm những gì tôi đang theo đuổi. Tôi đang hiển thị sơ đồ trang web của các trang (như một cái cây), bất cứ khi nào chúng tôi đến một trang đang trong bản nháp, tôi muốn dừng hiển thị bất kỳ trang nào khác trong phân cấp của chi nhánh cụ thể đó. Tôi sẽ cố gắng tìm hiểu mã của bạn để xem những gì tôi có thể tìm thấy.
mike23

1
@ mike23 Tôi đã chỉnh sửa câu trả lời, bây giờ nó sẽ hoạt động. Trước khi chỉnh sửa chỉ có phần con trực tiếp của một trang nháp đã bị xóa, bây giờ bất kỳ trang nào trong hệ thống phân cấp của trang nháp nên được xóa.
gmazzap

@GM Điều này có hoạt động cho dù trang dự thảo đầu tiên ở độ sâu nào không?
mike23

@ mike23 có, hoàn toàn
gmazzap

3

Việc sử dụng một tùy chỉnh Walkerthực sự không khó lắm, về cơ bản nó diễn ra như sau:

  • Tạo một lớp học ;

    Một lớp là một tập hợp các biến và hàm làm việc với các biến này.

  • Bằng cách mở rộng một cái khác;

    Lớp mở rộng hoặc dẫn xuất có tất cả các biến và hàm của lớp cơ sở [...] và những gì bạn thêm vào trong định nghĩa mở rộng.

  • Như thế này:

    class Extended_Class extends Base_Class {
       // code
    }
  • Điều này cho bạn khả năng thay đổi / mở rộng các phương thức aka các hàm của lớp cơ sở đã được mở rộng. Ngoài ra, bạn có thể / có thể mở rộng bằng cách thêm các phương thức hoặc biến vào lớp mở rộng.

  • Để hiểu đầy đủ và sử dụng các khả năng, cần phải tìm hiểu sâu hơn về các khía cạnh OOP: Classes và Object của PHP. Nhưng đó sẽ là quá nhiều ở đây và không phải là nơi thích hợp.

Vì vậy, hãy quay trở lại với WordPress và wp_list_pages(). Lớp mà chúng ta muốn mở rộng để sử dụng với wp_list_pages(), Walker_Pagelớp - nguồn -, chính nó đã được bắt nguồn bằng cách mở rộng nguồnWalker - lớp .

Theo lược đồ được giải thích ở trên, chúng ta sẽ làm tương tự:

class Wpse159627_Walker_Page extends Walker_Page {
    // code
}

Bây giờ Walker_Pagecó hai biến - $tree_type$db_fields- và bốn phương pháp - start_lvl(), end_lvl(), start_el()end_el(). Các biến sẽ không liên quan đến chúng tôi, liên quan đến các phương pháp mà chúng tôi ít nhất phải xem xét kỹ hơn start_el()end_el().

Điều đầu tiên cần thấy là hai phương thức đó có tham số $page:

@param object $ page Trang đối tượng dữ liệu.

Trong đó có chứa tất cả các dữ liệu có liên quan, chúng ta cần, giống như post_parent, và khá nhiều WP_Post/ $post/" $page" đối tượng. Trả lại bằng cách get_pages()trả lại

Một mảng chứa tất cả các Trang phù hợp với yêu cầu hoặc sai khi thất bại. Mảng trả về là một mảng các đối tượng "trang".

bên trong các wp_list_pages()chức năng.

Những gì chúng ta cần kiểm tra là trạng thái bài đăng của phụ huynh trang hiện tại, để thực hiện chức năng get_post_status()này có sẵn. Giống như đã xác định, chúng ta có thể sử dụng đối tượng $ page có sẵn để làm như vậy.

$page_parent_id     = $page->post_parent;
$page_parent_status = get_post_status( $page_parent_id );

Bây giờ chúng ta có thể sử dụng điều này để kiểm tra trạng thái của trang hiện tại:

if ( $page_parent_status != 'draft' ) {
    // code
}

Hãy thực hiện nó trong lớp Walker mở rộng của chúng tôi:

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";
        }
    }
}

Lớp mới có thể được sử dụng wp_list_pages()như thế này:

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



Biên tập:

Thêm điều này vì lý do đầy đủ, vì vậy để làm cho công việc này cho cây, tất cả con cháu, không chỉ trẻ em. Nó không phải là cách tối ưu để làm điều đó, đủ gợi ý khác đã được thực hiện.

Vì WordPress get_ancestors()và các get_post_ancestors()chức năng không được tạo để nhận bản nháp, tôi đã xây dựng một chức năng để nhận mọi tổ tiên:

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;
}

Ngoài ra, nó là cần thiết để có được trạng thái của những tổ tiên. Có thể được thực hiện với chức năng sau:

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;
}

Điều này có thể được sử dụng để thay thế điều kiện được giải thích ở trên:

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

Vấn đề với cách tiếp cận này là việc get_post_statusđược gọi cho mỗi phần tử sẽ kích hoạt truy vấn DB. Và một truy vấn DB cho mỗi trang không phải là một điều tuyệt vời nếu có rất nhiều trang ...
gmazzap

Chắc chắn, nó không hoàn toàn tối ưu, nhưng tin tôi đi nếu tôi có thể hiểu biết như bạn thì tôi đã đề xuất một giải pháp thông minh như của bạn. @GM
Nicolai

Tôi chỉ ghét người đi bộ, còn bạn thì không, vì vậy tôi phải tìm một giải pháp để không sử dụng chúng :) Và hãy tin tôi, bạn đủ hiểu biết để trả lời các câu hỏi tốt hơn tôi nhiều :)
gmazzap

Ok, hãy đồng ý rằng cả hai chúng tôi đều có khả năng :) Cảm giác của tôi đối với người đi bộ chỉ thờ ơ :) Ngoài việc đọc qua nguồn cộng với tài liệu và trả lời câu hỏi chỉ là một cách khác để tôi tìm hiểu thêm một chút về vấn đề này. @GM
Nicolai

Đó là toàn bộ hướng dẫn bạn đã viết! :) Tôi sẽ xem liệu tôi có thể làm cho nó hoạt động được không, bây giờ chỉ cần copypasta không giải quyết được vấn đề .. Tôi vẫn thấy các trang được xuất bản có bố mẹ đang trong bản nháp.
mike23

2

Câu trả lời này được cung cấp một cách khác để làm điều này. Các mã là khá nhiều tự giải thích, tôi đặt tên mọi thứ khá theo nghĩa đen để làm cho nó dễ hiểu hơn. Những gì tôi đã làm là xây dựng một hàm xác định các trang nháp và hậu duệ của chúng, có thể được sử dụng với excludetham số của wp_list_pages().

Chức năng trợ giúp:

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;
}

Sử dụng:

$args = array(
    'sort_column' => 'menu_order',
    'title_li'    => '',
    'post_status' => 'publish',
    'exclude'     => wpse159627_exclude_draft_sub_trees()
);
wp_list_pages( $args );

Nếu bạn đang ở phiên bản PHP nhỏ hơn 5,3, bạn cần một phiên bản không có bao đóng. Trong cuốn sách của tôi, để nói rằng rõ ràng, đó là một sai lầm khi hoạt động trên bất cứ điều gì dưới 5,4. Nhưng tôi nhận thức rất rõ về các yêu cầu của WordPress, PHP 5.2.4, vì vậy bạn hãy vào đây:

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;
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.