Phân trang cho các vòng lặp tùy chỉnh WordPress custom loop
Contents
Câu hỏi
Tôi có thêm một truy vấn tùy chỉnh/ truy vấn phụ vào mẫu file tùy chỉnh. Làm cách nào để WordPress dùng truy vấn tùy chỉnh của tôi để đánh trang, thay vì sử dụng phân trang của vòng lặp truy vấn chính (main query loop).
Phụ lục
Tôi đã sửa truy vấn vòng lặp chính (main loop query) bằng query_posts(). Nhưng tại sao phân trang vẫn không hoạt động, tôi phải sửa nó thế nào?
Chip Bennett
Câu trả lời chính xác nhất
Vấn đề
Được mặc định sẵn, WordPress 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 global $wp_query. Nó cũng được dùng để xuất vòng lặp truy vấn chính:
if ( have_posts() ) : while ( have_posts() ) : the_post();
Khi bạn dùng truy vấn tùy chỉnh (custom query), bạn tạo ra một đối tượng truy vấn hoàn toàn riêng biệt:
$custom_query = new WP_Query( $custom_query_args );
if ( $custom_query->have_posts() ) :
while ( $custom_query->have_posts() ) :
$custom_query->the_post();
Giải pháp
Chuyển tham số phân trang chính xác tới truy vấn tùy chỉnh
Giả sử truy vấn tùy chỉnh sử dụng mảng args:
$custom_query_args = array(
// Custom query parameters go here
);
Bạn phải chuyển tham số phân trang đúng tới mảng (array). Có thể thực hiện bằng cách nạp biến truy vấn URL, biến này được sử dụng để xác định trang hiện tại, thông qua thông qua get_query_var ():
get_query_var( ‘paged’ );
Sau đó bạn có thể nối tham số đó vào mảng truy vấn tùy chỉnh args của bạn:
$custom_query_args[‘paged’] = get_query_var( ‘paged’ )
? get_query_var( ‘paged’ )
: 1;
Lưu ý: Nếu trang của bạn là trang tĩnh, hãy dùng page thay vì paged bởi vì trang tĩnh dùng page chứ không phải paged. Đối với trang tĩnh, bạn làm như sau:
$custom_query_args[‘paged’] = get_query_var( ‘page’ )
? get_query_var( ‘page’ )
: 1;
Giờ thì khi truy vấn tùy chỉnh được nạp, tập hợp đúng của 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 hàm phân trang
Để các hàm phân trang cho ra output chính xác – ví dụ liên kết trước/ tiếp theo/ trang liên quan đến truy vấn tùy chỉnh – WordPress bắt buộc phải nhận diện được truy vấn tùy chỉnh. Điều này đòi hỏi một chút “hack”: thay thế đối tượng $ wp_query 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
- Thực hiện null đố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;
“hack” phải được thực hiện trước khi gọi các hàm phân trang.
Reset đối tượng truy vấn chính
Khi hàm phân trang đã được xuất ra, tiến hành reset đối tượng truy vấn chính:
$wp_query = NULL;
$wp_query = $temp_query;
Sửa hàm phân trang
Hàm previous_posts_link() sẽ hoạt động bình thường, không bị ảnh hưởng bởi phân trang. Nó chỉ xác định trang hiện tại (current page), sau đó xuất link cho page – 1. Tuy nhiên, cần sửa next_posts_link() để xuất một cách chính xác, bởi vì next_posts_link() dùng tham số max_num_pages:
<?php next_posts_link( $label , $max_pages ); ?>
Như đối với các tham số truy vấn khác, hàm được mặc định sẽ dùng max_num_pages cho đối tượng $ wp_query chính. Để buộc next_posts_link () tính đến đối tượng $ custom_query, bạn cần phải chuyển max_num_pages vào hàm. Bạn có thể nạp giá trị này từ đối tượng $ custom_query: $custom_query->max_num_pages:
<?php next_posts_link( ‘Older Posts’ , $custom_query->max_num_pages ); ?>
Tập hợp chúng lại với nhau
Dưới đây là cấu trúc cơ bản của một vòng lặp truy vấn tùy chỉnh (custom query loop) với các hàm phân trang thích hợp:
// 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: Còn với query_posts()?
query_posts() cho các vòng lặp phụ
Nếu bạn đang dùng query_posts() để xuất vòng lặp tùy chỉnh, thay vì sau đó khởi tạo một đối tượng riêng biệt cho truy vấn tùy chỉnh qua WP_Query (), thì bạn lại dùng _doing_it_wrong (). Và rồi bạn sẽ gặp phải nhiều vấn đề (ít nhất bạn sẽ gặp phải vấn đề về phân trang). Bước đầu tiên để giải quyết những vấn đề này là chuyển từ việc sử dụng query_posts() sang WP_Query ().
Sử dụng query_posts() để chỉnh sửa vòng lặp chính
Nếu bạn chỉ muốn chỉnh sửa các tham số cho truy vấn vòng lặp chính (main loop query) – ví dụ như thay đổi các bài trên một trang, hoặc bỏ đi một danh mục – bạn có thể muốn sử dụng query_posts(). Nhưng vẫn không nên làm như vậy. Khi bạn sử dụng query_posts (), bạn buộc WordPress thay thế đối tượng truy vấn chính. (Chính xác thì WordPress sẽ tạo một truy vấn thứ hai và ghi đè lên $ wp_query). Vấn đề ở đây là nó thực hiện việc thay thế quá muộn và phân trang sẽ không được cập nhật.
Giải pháp cho vấn đề này là lọc truy vấn chính trước khi bài đăng được nạp vào. Bằng cách sử dụng hook pre_get_posts.
Thay vì thêm cái này vào tệp danh mục mẫu (category template file) (category.php):
query_posts( array(
‘posts_per_page’ => 5
) );
Hãy thêm phần sau vào 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 cái này vào file mục lục mẫu của các bài đăng trên blog (blog posts index template file) (home.php):
query_posts( array(
‘cat’ => ‘-5’
) );
Thêm phần sau vào 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’ );
Với cách này, WordPress sẽ sử dụng đối tượng $wp_query đã được chỉnh sửa khi tiến hành xác định phân trang. Nó sẽ không yêu cầu sửa đổi mẫu.
Làm sao để biết lúc nào thì cần dùng hàm nào
Hãy tìm hiểu ở this question and answer và this question and answer để hiểu cách dùng và khi nào thì nên sử dụng WP_Query, pre_get_posts, and query_posts().
Câu trả lời của Chip Bennett