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 7171 views

https://www.youtube.com/watch?v=JMf9Eg4et04&list=PLl4nkmb3a8w3qzoFaXLsPohofWUMTOHBU&index=4

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

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.

18 bình luận
0 0 vote
Article Rating
guest
18 Comments
mới nhất
cũ nhất đánh giá nhiều
Inline Feedbacks
View all comments
tunguyen

Cảm ơn @Thạch Phạm, Các bài viết của bạn rất hữu ích!

dương

anh cho em hỏi posts_per_page và showpost khác nhau như nào

Phúc

Anh Thạch cho em hỏi,em có 1 bảng trong csdl,query ra thành 1 mảng đa chiều,nhưng để xuất ra thì em dùng “print_r($sheet1[1]);” ra được giá trị đầu tiên,nhưng em làm “print_r($sheet1[1][1]);” để lấy 1 giá trị nhỏ trong cột đó nữa, thì nó báo lỗi,anh góp ý giúp em với ạ,thank anh 🙂

MHD Trung Văn

lâu lắm mới đọc lại mấy cái cơ bản này. cảm ơn bác thạch

Neo An

Cảm ơn Mr.Thạch phạm!

Jacker Tân

Em đã dùng vòng lặp này để giới hạng số lượng bài viết lại nhưng khi giới hạn số lượng bài viết lại thì nó tự động phân thành nhiều trang khi e click vào trang 2 thì nó ra 1 đường dẫn https://url/page/2 nhưng lại thông báo không có thông tin 404 The requested page could not be found.Không biết đây là hàm gì ạ.Nó tự động phân trang ra em chưa làm gì hết nhưng trang mới lại không có dữ liệu.

tinhbeng

Cho em hỏi 1 vấn đề về Query Search:
Em muốn Search một “post” dựa trên “title”, nhưng không muốn search trên toàn bộ “posttype” mà search theo “category” hoặc “tag”. Anh Thạch giúp em với ạ!

giangsaker

@Thạch Phạm cho em hỏi lấy link chuyên mục của bài viết thì dùng hàm nào ak. Tức là e đang muốn làm phần “Bài viết cùng chuyên mục” bên dưới sẽ có 1 nút “Xem tiếp” khi click vào đó thì dẫn đến chuyên mục của bài viết đấy ak.

Dan

A cho e hỏi cái hàm the_post tai sao lại bắt đầu = -1 ạ

Bang

Lam the nao de xay dung chuc nang pót bai tren wep ha a

thu thao

anh cho em hỏi nếu em muốn lấy ra 4 bài viết đầu tiên xuất ra trong tất cả các bài viết thì làm thế nào ạ, em cảm ơn a!

Thanh

E muốn hỏi là mình muốn tạo một bảng mới ví dụ như quản lý sản phẩm chẳng hạn, và khi truy vấn ra sản phẩm thì mình sẽ thực hiện thế nào ạ, lúc đó có còn sử dụng have_posts() nữa k hay sẽ là have_sanpham() và thay the_title() thành the_tensanpham(). Rất mong nhận được bài hướng dẫn của anh về tạo bảng mới và tạo các chức năng quản lý trong đó ạ. Thanks!

Cao Vương

have_posts() là hàm mặc định trong wp bạn nhé. Để truy vấn vào custom post type thì bạn chỉ cần tạo 1 đối tượng query mới sau đó dùng cách truy vấn nội dung bằng have_posts(), the_title() như bình thường nhé. :
<?php
$args_my_query = array(
'post_type' => 'ten_post_type',
'orderby' => 'rand'
);
$my_query = new WP_Query( $args_my_query );
// khi gọi: $my_query->have_posts()
?>

Duc

chỗ tạo mới query anh viết sai cái đối số $args_my_query ,

18
0
Would love your thoughts, please comment.x
()
x