Trang chủ WordpressWordpress Development Sử dụng WP Query và Loop (vòng lặp) để lấy bài viết

Sử dụng WP Query và Loop (vòng lặp) để lấy bài viết

bởi Thạch Phạm
18 bình luận 24,5K views

Tham gia nhóm hỗ trợ WordPress

Tham gia nhóm Hỗ trợ Server - Hosting & WordPress để cùng nhau hỏi đáp và hỗ trợ các vấn đề về WordPress, tối ưu máy chủ/server.

Tham gia ngay
Bài này thuộc phần 5 của 10 phần trong serie WordPress nâng cao



Query (truy vấn) là một thuật ngữ hay dùng nhất trong WordPress để chỉ các truy vấn gửi đến cơ sở dữ liệu (database) để lấy thông tin của Post (hoặc Page, hoặc bất cứ một cái gì). Nếu giải thích cặn kẽ về truy vấn thì rất là dài dòng, nhưng trong bài này bạn chỉ tạm hiểu rằng truy vấn là một tập hợp các lệnh SQL gửi đến MySQL Server nhằm lấy dữ liệu của các bài viết trên WordPress.

Trong bài viết này, mình sẽ hướng dẫn bạn đi tìm hiểu về các query mặc định trong WordPress ở mỗi trang, và cách tạo mới một query đến database thông qua class WP_Query.

Cách query  ở mỗi trang

Mặc định mỗi trang trên WordPress đều có chứa query, ví dụ ở trang chủ nó sẽ gửi query để lấy danh sách bài viết mới nhất, khi vào trang nội dung bài viết thì nó sẽ gửi query để lấy thông tin của bài viết đó dựa vào ID của bài viết (ở phần trước mình đã có nói là WordPress có thể phân tích truy vấn ở một trang dựa vào đường dẫn truy cập).

Nhưng để xem chính xác trên trang của bạn có bao nhiêu query, và nó gửi như thế nào thì chúng ta phải debug mới thấy được. Cách đơn giản nhất là bạn hãy cài plugin Debug Bar rồi chèn đoạn này vào wp-config.php:

define( ‘SAVEQUERIES’, true );
Khuyến mãi Black Friday

Sau đó bạn ra trang chủ, ấn vào nút Debug trên Admin Bar và xem ở phần WP Query.

Xem query của trang với Debug Bar

Xem query của trang với Debug Bar

Trong đó, phần ở trên là những thông tin quan trọng về query của trang hiện tại như template đang thực thi query (các tập tin trong theme), loại query. Còn phần Query SQL là lệnh SQL thực thi query đó. Nếu bạn đã từng làm việc với SQL thì chắc chắn sẽ hiểu đoạn đó có nghĩa là gì. Như trong hình thì nó lấy dữ liệu trong bảng wp_posts, số thứ tự là 1 và cột post_type là post và post_status  là publish và sắp xếp dựa theo giá trị cột post_date theo thứ tự tăng dần với số lượng 10 đối tượng.  Nhưng mấy cái này bạn đừng lo vì WordPress có hỗ trợ chúng ta tạo query nhanh nhất mà không cần phải nhớ tên bảng, tên cột gì hết.

Tương tự, bạn có thể vào các trang khác rồi xem query của nó.

Kết quả của query lưu vào đâu?

Sau khi gửi query, thì chắc chắn phải có kết quả trả về của query đó. Ví dụ khi nó gửi query để lấy bài mới thì chắc chắn nó sẽ trả về các thông tin của bài mới mà query đó đang cần tìm. Và các kết quả của query sẽ được lưu vào đối tượng $wp_query.

Bây giờ, bạn hãy thử lookup cái đối tượng $wp_query bằng cách chèn đoạn code dưới đây vào cuối template footer.php trong theme:


<?php
    echo ‘<pre>’;
    print_r ($wp_query);
    echo ‘</pre>’;
?>

Và bây giờ, bạn có thể đi qua các bài viết hoặc các trang nào đó để xem thông tin của đối tượng $wp_query sau khi nó đã gửi query, nó sẽ bao gồm tất cả các thông tin mà nó nhận được (trường hợp nó để trống là không có).

Tuy nhiên, các bạn lưu ý nhất cho mình ở chỗ thuộc tính posts.

wp-query-postobject

Và nếu bây giờ bạn chỉ cần xem dữ liệu của thuộc tính posts thì sửa lại code debug trỏ đến thuộc tính posts trong đối tượng $wp_query.


<?php
    echo ‘<pre>’;
    print_r ($wp_query->posts);
    echo ‘</pre>’;
?>

Nhưng đáng mừng cho bạn, là cái $wp_query->posts có một đối tượng riêng tên là $posts, nên có thể bạn chỉ cần lookup cái đối tượng $posts là ra.


<?php
    echo ‘<pre>’;
    print_r ($posts);
    echo ‘</pre>’;
?>

Và như bạn thấy, chúng ta đã có thể xem được các thuộc tính trong đối tượng $posts là các thông tin về bài viết.

Các thuộc tính trong đối tượng $posts

Các thuộc tính trong đối tượng $posts

Tuy nhiên nếu bạn cần lấy ra, chúng ta nên lấy bằng đối tượng $post (không có chữ s ở cuối). Bây giờ bạn thử mở lại template footer.php, thử lookup cái thuộc tính post_content trong đối tượng $post xem sao nhé. Hoặc bạn có thể đặt nó ở một template bất kỳ trong theme, vào vị trí bất kỳ.


<?php
    echo ‘<pre>’;
    print_r ($post->post_content);
    echo ‘</pre>’;
?>

Sử dụng vòng lặp (loop) cho query

Và nếu bạn làm đúng khi in $post->post_content thì nó sẽ trả về nội dung của bài viết hiện tại nếu bạn vào xem nội dung một post. Nhưng nếu bạn xem ngoài trang index, hay các trang lưu trữ như tag, category,…thì nó chỉ hiển thị nội dung bài viết đầu tiên. Tại sao?

Bởi vì cái $post thực chất là một đối tượng trong một mảng (array), nên nếu gọi như thế thì PHP sẽ tự hiểu chúng ta chỉ muốn gọi đối tượng đầu tiên trong mảng nên khi xem ở các trang có nhiều bài viết thì nó cũng chỉ hiển thị có một bài. Và để cho nó hiển thị toàn bộ, chúng ta phải tạo vòng lặp (loop) cho cái mảng đối tượng đó để nó lấy ra nhiều bài viết. Và chúng ta sẽ lặp thông qua phương thức have_posts() trong đối tượng $wp_query.

Trước khi tìm hiểu sâu xa, chúng ta hãy tìm hiểu cấu trúc của một đoạn vòng lặp trong WordPress, và cấu trúc của nó như sau:


<?php
    if( $wp_query->have_posts() ) { // Nếu phương thức have_posts() trả về TRUE thì mới chạy code bên trong
        while( $wp_query->have_posts() ) { // Nếu have_posts() == TRUE thì nó mới lặp, không thì ngừng
            $wp_query->the_post(); // Thiết lập số thứ tự bài viết trong chỉ mục của query

            /*
             * Nội dung hiển thị bài viết
             */
            echo $post->post_title . ‘<br>’;

        }
    }
?>

Mình viết vậy cho các bạn dễ hiểu chứ thường thì WordPress nó hay viết ngắn gọn thành như thế này:


<?php
    if( $wp_query->have_posts() ) : while( $wp_query->have_posts() ) : $wp_query->the_post();

            /*
             * Nội dung hiển thị bài viết
             */
            echo $post->post_title . ‘<br>’;

        endwhile; endif;
?>

Bạn đặt cái này vào vị trí nào đó trong template như ở footer.php chẳng hạn, rồi ra ngoài trang chủ hoặc trang lưu trữ xem là bạn sẽ thấy nó gọi ra tiêu đề bài viết của nhiều bài khác nhau.

Ý nghĩa ra sao thì mời bạn đọc comment và xem thêm chi tiết các phương thức bên dưới.

Ý nghĩa phương thức have_posts()

Phương thức have_posts() là một phương thức của class WP_Query vốn tạo ra đối tượng $wp_query. Phương thức này được khai báo trong tập tin /wp-includes/query.php tại dòng 3739 – 3767.


    /**
     * Whether there are more posts available in the loop.
     *
     * Calls action ‘loop_end’, when the loop is complete.
     *
     * @since 1.5.0
     * @access public
     *
     * @return bool True if posts are available, false if end of loop.
     */
    public function have_posts() {
        if ( $this->current_post + 1 < $this->post_count ) {
            return true;
        } elseif ( $this->current_post + 1 == $this->post_count && $this->post_count > 0 ) {
            /**
             * Fires once the loop has ended.
             *
             * @since 2.0.0
             *
             * @param WP_Query &$this The WP_Query instance (passed by reference).
             */
            do_action_ref_array( ‘loop_end’, array( &$this ) );
            // Do some cleaning up after the loop
            $this->rewind_posts();
        }

        $this->in_the_loop = false;
        return false;
    }

Ý nghĩa của phương thức này rất đơn giản, đó là nó sẽ kiểm tra xem còn bài viết nào trong query hay không. Sở dĩ nó có thể kiểm tra được là nó lấy số thứ tự của bài viết hiện tại (thuộc tính current_post) cộng thêm 1 đơn vị mà nhỏ hơn tổng số bài viết trong query (thuộc tính post_count) thì nó sẽ trả kết quả về là true. Ngược lại là FALSE.

Và đoạn này:

if( $wp_query->have_posts() )

nghĩa là nó kiểm tra xem nếu nó có bài viết trong query hiện tại ($wp_query) thì nó sẽ thực hiện vòng lặp bên dưới.

while( $wp_query->have_posts() )

Và trong vòng lặp này, nó sẽ lặp nếu thằng have_posts() trả về kết quả là true. Còn false thì nó sẽ dừng.

Phương thức the_post()

Sau khi gọi vòng lặp ra, các bạn có thể thấy nó có thêm phương thức the_post() bên trong. Vậy cái này là gì?

Phương thức này được khai báo ở /wp-includes/query.php tại dòng 3712 – 3737.


    /**
     * Sets up the current post.
     *
     * Retrieves the next post, sets up the post, sets the ‘in the loop’
     * property to true.
     *
     * @since 1.5.0
     * @access public
     */
    public function the_post() {
        global $post;
        $this->in_the_loop = true;

        if ( $this->current_post == -1 ) // loop has just started
            /**
             * Fires once the loop is started.
             *
             * @since 2.0.0
             *
             * @param WP_Query &$this The WP_Query instance (passed by reference).
             */
            do_action_ref_array( ‘loop_start’, array( &$this ) );

        $post = $this->next_post();
        $this->setup_postdata( $post );
    }

Phương thức này nghĩa là nó sẽ đánh chỉ mục thứ tự cho bài viết trong vòng lặp và chức năng cụ thể là sẽ thiết lập thuộc tính in_the_loop sang true vì mặc định là false, mà nếu nó là true thì phương thức next_post() có thể thực thi nhằm lấy bài viết tiếp theo.

Vậy kết luận, phương thức này sẽ có chức năng đếm chỉ mục để gọi bài kế tiếp trong vòng lặp, nếu bạn không khai báo phương thức này trong vòng lặp thì bài viết đầu tiên sẽ bị lặp đi lặp lại hoài mà không có điểm dừng.

$wp_query không cần khai báo trong vòng lặp

$wp_query là đối tượng chứa query mặc định trong trang, nên bạn có thể không cần gọi ra trong vòng lặp mà nó sẽ tự hiểu là bạn đang lặp query mặc định.


<?php
    if( have_posts() ) : while( have_posts() ) : the_post();

            /*
             * Nội dung hiển thị bài viết
             */
            echo $post->post_title . ‘<br>’;

        endwhile; endif;
?>

Sử dụng template tags trong vòng lặp

Trong vòng lặp, nếu bạn cần lấy thông tin bài viết ra thì có thể không cần dùng đến đối tượng $post, mà chỉ cần khai báo các hàm template tag (xem bài trước) mà thôi. Nó sẽ tự hiểu bạn lấy đối tượng trong query mà nó đang lặp (xem nội dung file /wp-includes/post-template.php để biết thêm chi tiết).

<?php
    if( have_posts() ) : while( have_posts() ) : the_post();

            /*
             * Nội dung hiển thị bài viết
             */
            the_title();

        endwhile; endif;
?>

Ví dụ một đoạn vòng lặp hoàn chỉnh

Bằng cách kết hợp với HTML, chúng ta có thể tạo ra một vòng lặp để lấy danh sách bài viết như thế này:


<?php
    if( have_posts() ) : while ( have_posts() ) : the_post();
?> <!–Đóng PHP để viết HTML tiện hơn ở dưới–>

    <!–Nội dung một bài viết–>
    <article <?php post_class() ?> >
        <h1><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h1>
        <summary>
            <?php the_content(); ?>
        </summary>
        <footer>
            <?php the_tags(); ?>
        </footer>
    </article>
    <!–kết thúc nội dung–>

<?php endwhile; endif; // Kết thúc vòng lặp ?>

Sẵn đây mình nói luôn, các thẻ kiểu <article>,<summary>,<footer> cũng giống thẻ <div> thôi, đó là các thẻ HTML5 nên nhìn có cấu trúc hơn.

Tạo một đối tượng query mới với WP_Query

Sau khi xem phần trên thì bạn đã biết WordPress có một truy vấn mặc định trên mỗi trang được lưu trong đối tượng $wp_query. Nhưng đáng mừng thay, bạn có thể tạo ra nhiều đối tượng query khác với các tham số tuỳ chỉnh lấy bài viết theo ý bạn muốn, đó là chúng ta sẽ sử dụng class WP_Query.

Cách sử dụng

Cách sử dụng class này khá đơn giản, đó là tạo mới nó vào một biến làm đối tượng và khai báo các tham số (dạng mảng) để tuỳ chỉnh lấy bài viết.

$my_query = new WP_Query( $args );

Trong đó, $args là biến chứa tham số. Ví dụ mình có thể khai báo tham số và tạo query thế này:


<?php
$args_my_query = array(
    ‘post_type’    =>    ‘post’,
    ‘orderby’    => ‘rand’
);
$my_query = new WP_Query( $args_my_query );
?>

Rồi sau đó, bạn mang cái đối tượng $my_query bỏ vào vòng lặp là được.


<?php
    if( $my_query->have_posts() ) : while( $my_query->have_posts() ) : $my_query->the_post();

            /*
             * Nội dung hiển thị bài viết
             */
            the_title();

        endwhile; endif;
?>

Ở đây mình sẽ không nói đến việc tạo một query mới kèm theo phân trang mà mình sẽ nói vấn đề này ở một bài viết riêng.

Danh sách các tham số của WP_Query

Sức mạnh của WP_Query là nằm ở các tham số. Với số lượng tham số đa dạng và phong phú, bạn có thể lấy bất cứ kiểu bài viết nào trong bất kỳ chỗ nào trên website. Các tham số mình đã gom lại thành một ghi chú, bạn có thể tham khảo tại https://gist.github.com/thachpham92/d57b18cf02e3550acdb5.

Lời kết

Bài viết này có lẽ hơi dài, nhưng đó là những gì quan trọng nhất về WP Query mà mình muốn nhắn đến bạn. Bằng việc nắm vững cách sử dụng WP Query và hiểu query trong WordPress, bạn có thể làm được rất nhiều việc vì hầu như trong WordPress chúng ta sử dụng WP Query rất nhiều.

4/5 - (8 bình chọn)
18 bình luận

Hiện tại blog tạm đóng bình luận vì mình cần tập trung thời gian vào cập nhật bài viết. Bình luận sẽ mở ra cho đến khi mình sẵn sàng.