Vấn đề
Theo mặc định, trong bất kỳ bối cảnh cụ thể nào, WordPress sử dụng truy vấn chính để xác định phân trang. Đối tượng truy vấn chính được lưu trữ trong $wp_query
toàn cục, cũng được sử dụng để xuất vòng lặp truy vấn chính:
if ( have_posts() ) : while ( have_posts() ) : the_post();
Khi bạn sử dụng truy vấn tùy chỉnh , bạn tạo một đối tượng truy vấn hoàn toàn riêng biệt:
$custom_query = new WP_Query( $custom_query_args );
Và truy vấn đó là đầu ra thông qua một vòng lặp hoàn toàn riêng biệt:
if ( $custom_query->have_posts() ) :
while ( $custom_query->have_posts() ) :
$custom_query->the_post();
Nhưng pagination mẫu thẻ, bao gồm previous_posts_link()
, next_posts_link()
, posts_nav_link()
, và paginate_links()
, căn đầu ra của họ trên đối tượng truy vấn chính , $wp_query
. Đó là truy vấn chính có thể hoặc không được phân trang. Ví dụ, nếu bối cảnh hiện tại là một mẫu trang tùy chỉnh, thì $wp_query
đối tượng chính sẽ chỉ bao gồm một bài đăng duy nhất - đó là ID của trang mà mẫu trang tùy chỉnh được gán.
Nếu bối cảnh hiện tại là một chỉ mục lưu trữ thuộc loại nào đó, thì chính $wp_query
có thể bao gồm đủ các bài đăng để gây ra phân trang, dẫn đến phần tiếp theo của vấn đề: đối với $wp_query
đối tượng chính , WordPress sẽ chuyển một paged
tham số cho truy vấn, dựa trên paged
Biến truy vấn URL. Khi truy vấn được tìm nạp, paged
tham số đó sẽ được sử dụng để xác định tập hợp các bài đăng được phân trang nào sẽ trả về. Nếu liên kết phân trang được hiển thị được nhấp và trang tiếp theo được tải, truy vấn tùy chỉnh của bạn sẽ không có cách nào để biết rằng phân trang đã thay đổi .
Giải pháp
Truyền tham số phân trang chính xác cho truy vấn tùy chỉnh
Giả sử rằng truy vấn tùy chỉnh sử dụng một mảng args:
$custom_query_args = array(
// Custom query parameters go here
);
Bạn sẽ cần phải truyền paged
tham số chính xác cho mảng. Bạn có thể làm như vậy bằng cách tìm nạp biến truy vấn URL được sử dụng để xác định trang hiện tại, thông qua get_query_var()
:
get_query_var( 'paged' );
Sau đó, bạn có thể nối tham số đó vào mảng args truy vấn tùy chỉnh của mình:
$custom_query_args['paged'] = get_query_var( 'paged' )
? get_query_var( 'paged' )
: 1;
Lưu ý: Nếu trang của bạn là một trang tĩnh , hãy chắc chắn để sử dụng page
thay vì paged
như một trang tĩnh sử dụng page
và không paged
. Đây là những gì bạn nên có cho một trang đầu tĩnh
$custom_query_args['paged'] = get_query_var( 'page' )
? get_query_var( 'page' )
: 1;
Bây giờ, khi truy vấn tùy chỉnh được tìm nạp, tập hợp chính xác các bài đăng được phân trang sẽ được trả về.
Sử dụng đối tượng truy vấn tùy chỉnh cho các chức năng phân trang
Để các chức năng phân trang mang lại đầu ra chính xác - tức là các liên kết trước / tiếp theo / trang liên quan đến truy vấn tùy chỉnh - WordPress cần phải buộc phải nhận ra truy vấn tùy chỉnh. Điều này đòi hỏi một chút "hack": thay thế $wp_query
đối tượng chính bằng đối tượng truy vấn tùy chỉnh , $custom_query
:
Hack đối tượng truy vấn chính
- Sao lưu đối tượng truy vấn chính:
$temp_query = $wp_query
- Không có đối tượng truy vấn chính:
$wp_query = NULL;
Hoán đổi truy vấn tùy chỉnh vào đối tượng truy vấn chính: $wp_query = $custom_query;
$temp_query = $wp_query;
$wp_query = NULL;
$wp_query = $custom_query;
Việc "hack" này phải được thực hiện trước khi gọi bất kỳ chức năng phân trang nào
Đặt lại đối tượng truy vấn chính
Khi các chức năng phân trang đã được xuất ra, hãy đặt lại đối tượng truy vấn chính:
$wp_query = NULL;
$wp_query = $temp_query;
Sửa lỗi chức năng
Các previous_posts_link()
chức năng sẽ hoạt động bình thường, bất kể phân trang. Nó chỉ xác định trang hiện tại và sau đó xuất liên kết cho page - 1
. Tuy nhiên, một sửa chữa là cần thiết next_posts_link()
để đầu ra đúng. Điều này là do next_posts_link()
sử dụng max_num_pages
tham số:
<?php next_posts_link( $label , $max_pages ); ?>
Cũng như các tham số truy vấn khác, theo mặc định, hàm sẽ sử dụng max_num_pages
cho $wp_query
đối tượng chính . Để buộc next_posts_link()
phải tính đến $custom_query
đối tượng, bạn sẽ cần truyền max_num_pages
hàm cho hàm. Bạn có thể lấy giá trị này từ $custom_query
đối tượng $custom_query->max_num_pages
::
<?php next_posts_link( 'Older Posts' , $custom_query->max_num_pages ); ?>
Để tất cả chúng cùng nhau
Sau đây là cấu trúc cơ bản của vòng lặp truy vấn tùy chỉnh với các chức năng phân trang hoạt động đúng:
// Define custom query parameters
$custom_query_args = array( /* Parameters go here */ );
// Get current page and append to custom query parameters array
$custom_query_args['paged'] = get_query_var( 'paged' ) ? get_query_var( 'paged' ) : 1;
// Instantiate custom query
$custom_query = new WP_Query( $custom_query_args );
// Pagination fix
$temp_query = $wp_query;
$wp_query = NULL;
$wp_query = $custom_query;
// Output custom query loop
if ( $custom_query->have_posts() ) :
while ( $custom_query->have_posts() ) :
$custom_query->the_post();
// Loop output goes here
endwhile;
endif;
// Reset postdata
wp_reset_postdata();
// Custom query loop pagination
previous_posts_link( 'Older Posts' );
next_posts_link( 'Newer Posts', $custom_query->max_num_pages );
// Reset main query object
$wp_query = NULL;
$wp_query = $temp_query;
Phụ lục: Thế còn query_posts()
?
query_posts()
cho vòng lặp thứ cấp
Nếu bạn đang sử dụng query_posts()
để xuất một vòng lặp tùy chỉnh, sau đó khởi tạo một đối tượng riêng cho truy vấn tùy chỉnh thông qua WP_Query()
, thì bạn _doing_it_wrong()
sẽ gặp phải một số vấn đề (không phải là ít nhất trong số đó là vấn đề phân trang). Bước đầu tiên để giải quyết những vấn đề đó sẽ là chuyển đổi việc sử dụng không đúng cách query_posts()
thành một WP_Query()
cuộc gọi thích hợp .
Sử dụng query_posts()
để sửa đổi vòng lặp chính
Nếu bạn chỉ muốn sửa đổi các tham số cho truy vấn vòng lặp chính - chẳng hạn như thay đổi bài đăng trên mỗi trang hoặc loại trừ một danh mục - bạn có thể bị cám dỗ sử dụng query_posts()
. Nhưng bạn vẫn không nên. Khi bạn sử dụng query_posts()
, bạn buộc WordPress thay thế đối tượng truy vấn chính. (WordPress thực sự tạo một truy vấn thứ hai và ghi đè lên $wp_query
.) Tuy nhiên, vấn đề là nó thay thế quá muộn trong quá trình cập nhật phân trang.
Giải pháp là lọc truy vấn chính trước khi bài viết được tìm nạp , thông qua pre_get_posts
hook.
Thay vì thêm phần này vào tệp mẫu danh mục ( category.php
):
query_posts( array(
'posts_per_page' => 5
) );
Thêm vào như sau functions.php
:
function wpse120407_pre_get_posts( $query ) {
// Test for category archive index
// and ensure that the query is the main query
// and not a secondary query (such as a nav menu
// or recent posts widget output, etc.
if ( is_category() && $query->is_main_query() ) {
// Modify posts per page
$query->set( 'posts_per_page', 5 );
}
}
add_action( 'pre_get_posts', 'wpse120407_pre_get_posts' );
Thay vì thêm phần này vào tệp mẫu chỉ mục bài đăng trên blog ( home.php
):
query_posts( array(
'cat' => '-5'
) );
Thêm vào như sau functions.php
:
function wpse120407_pre_get_posts( $query ) {
// Test for main blog posts index
// and ensure that the query is the main query
// and not a secondary query (such as a nav menu
// or recent posts widget output, etc.
if ( is_home() && $query->is_main_query() ) {
// Exclude category ID 5
$query->set( 'category__not_in', array( 5 ) );
}
}
add_action( 'pre_get_posts', 'wpse120407_pre_get_posts' );
Bằng cách đó, WordPress sẽ sử dụng $wp_query
đối tượng đã được sửa đổi khi xác định phân trang, không yêu cầu sửa đổi mẫu.
Khi nào sử dụng chức năng gì?
Nghiên cứu câu hỏi này và câu trả lời và câu hỏi này và câu trả lời để hiểu làm thế nào và khi nào thì sử dụng WP_Query
, pre_get_posts
và query_posts()
.