Bootstrap Skip to main content
Use code LAMVT for an extra 10% off!

From the Firehose

3.7/5 - (3 bình chọn)

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 );
Và truy vấn đó được xuất ra bởi 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 thẻ phân trang mẫu (pagination template tag), bao gồm previous_posts_link(), next_posts_link(), posts_nav_link(), và paginate_links(), output của chúng đều dựa vào đối tượng truy vấn chính, $wp_query. Truy vấn này có thể hoặc không thể phân trang được. Ví dụ, nếu current context là một mẫu trang tùy chỉnh, đối tượng $wp_query chính sẽ chỉ bao gồm một post đơn lẻ. Và ID của trang đó sẽ là ID mà mẫu trang tùy chỉnh được chỉ định.
Còn nếu current context là một chỉ số lưu trữ (archive index) thuộc loại nào đấy, $wp_query chính có thể có đủ trang để thực hiện phân trang. Và những vấn đề lại phát sinh: Đối với đối tượng $wp_query chính, WordPress sẽ chuyển một tham số phân trang đến truy vấn, dựa vào biến truy vấn URL phân trang. Khi truy vấn được nạp, tham số phân trang (paged parameter) sẽ được dùng để xác định tập hợp bài đăng được phân trang trả về. Nếu click vào link phân trang được hiện thị, trang tiếp theo được tải lên, truy vấn tùy chỉnh của bạn sẽ không thể biết được phân trang đã thay đổi.

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
  1. Sao lưu đối tượng truy vấn chính: $temp_query = $wp_query
  2. Thực hiện null đối tượng truy vấn chính: $wp_query = NULL;
  3. 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

About

Chào bạn, mình là Vũ Thành Lâm.
Tri Thức là Sức Mạnh, Tri thức không của riêng ai, hãy chia sẻ nó!

Recent posts