Cách Phân trang Dữ liệu với PHP

Discussion in 'PHP, Java , JS ....' started by admin, Feb 21, 2018.

  1. admin

    admin Administrator Staff Member

    Tôi có thể nhớ những năm trước đây khi lần đầu tiên tôi bắt đầu viết code PHP và MySQL, tôi rất hào hứng khi lần đầu tiên tôi có được thông tin từ một cơ sở dữ liệu để hiển thị trên một trình duyệt web.

    Đối với những người có ít kiến thức về cơ sở dữ liệu và lập trình, việc nhìn thấy những dòng dữ liệu đó hiển thị trên màn hình dựa theo code mà tôi đã viết (được rồi, vì thế nên tôi đã sao chép một ví dụ từ một cuốn sách) đã cho tôi một sự hân hoan cao độ. Có thể tôi đã không hiểu hết tất cả mọi thứ lúc đó, nhưng thành công đầu tiên đó đã thúc đẩy tôi đến các dự án lớn hơn và tốt hơn.

    Mặc dù mức độ hồ hởi của tôi đối với cơ sở dữ liệu có thể không còn giống như trước,
    nhưng kể từ khi "hello world" đầu tiên của tôi với PHP và MySQL tôi đã bị cuốn hút vào
    sức mạnh trong việc làm cho mọi thứ trở nên đơn giản và dễ sử dụng.

    Là một nhà phát triển, một vấn đề mà tôi thường xuyên phải đối mặt đó là lấy một lượng lớn thông tin và làm cho nó dễ tiếp thu. Cho dù nó là danh sách khách hàng của một công ty lớn hoặc một danh sách mp3 của cá nhân, thì việc phải ngồi và nhìn chằm chằm vào từng dòng dữ liệu có thể gây nhàm chán
    và bực bội. Một nhà phát triển giỏi có thể làm được gì? Đó là phân trang!

    1. Phân trang
    Phân trang cơ bản là quá trình lấy một tập hợp các kết quả và phân chia
    chúng thành các trang để làm cho chúng dễ xem hơn.

    [​IMG]
    Tôi nhận ra ngay từ đầu rằng nếu tôi có 5000 dòng thông tin để hiển thị thì không chỉ
    làm người ta nhức đầu khi cố gắng đọc, mà hầu hết các trình duyệt sẽ mất
    nhiều thời gian (khoảng hơn 5 giây) để hiển thị nó.

    Để giải quyết vấn đề này tôi sẽ viết các lệnh SQL khác nhau để kéo ra các khối dữ liệu, và nếu tôi
    đang ở trong một tâm trạng tốt tôi thậm chí có thể đưa ra thêm một vài nút "next" và "previous".
    Sau một thời gian, việc phải đặt code này vào mọi dự án tương tự và tùy biến
    nó để phù hợp đã trở nên lỗi thời. Nhanh. Và như mọi nhà phát triển giỏi đều biết, sự lười biếng sinh ra sự sáng tạo
    hay đại loại thế. Vì vậy, một ngày nọ tôi ngồi xuống và quyết định nghĩ ra một
    lớp PHP đơn giản, linh hoạt và dễ sử dụng sẽ tự động thực hiện công việc nặng nhọc
    cho tôi.

    Giới thiệu về tôi và các lớp PHP. Tôi không hướng đối tượng. Thực tế, tôi hầu như không
    bao giờ sử dụng những thứ đó. Nhưng sau khi đọc một số hướng dẫn và ví dụ về OOP, và một số
    thử nghiệm đơn giản và các ví dụ lỗi, tôi quyết định thử nó và bạn biết gì không?
    Nó làm việc rất tốt với phân trang. Code được sử dụng ở đây được viết bằng PHP 4 nhưng sẽ
    làm việc trong PHP 5.

    2. Cơ sở dữ liệu
    Cần phải yêu MySQL. Không có ý xúc phạm đến các hệ thống cơ sở dữ liệu khác hiện có, nhưng đối với tôi,
    tất cả những gì tôi cần là MySQL. Và một tính năng tuyệt vời của MySQL là chúng cung cấp cho bạn một số
    cơ sở dữ liệu mẫu miễn phí để khám phá tại http://dev.mysql.com/doc/#sampledb.

    Đối với các ví dụ của tôi, tôi sẽ sử dụng cơ sở dữ liệu world (~ 90k nén) có chứa trên
    4000 bản ghi để bạn thực hành, nhưng vẻ đẹp của script PHP mà chúng ta sẽ tạo ra là
    nó có thể được sử dụng với bất kỳ cơ sở dữ liệu nào. Bây giờ tôi nghĩ rằng tất cả những gì chúng ta có thể đồng ý với nhau đó là nếu chúng ta quyết định
    không phân trang các kết quả của chúng ta thì chúng ta sẽ gặp rắc rối với một số
    kết quả rất dài và khó sử dụng như sau:

    [​IMG]
    (nhấp vào để xem kích thước đầy đủ, khoảng 338k)

    Vì vậy, hãy chia nhỏ dữ liệu của chúng ta thành những trang dễ tiếp nhận như thế này:

    [​IMG]
    Đẹp chứ nhỉ? Một khi bạn bỏ lớp Pagination vào code của bạn, bạn có thể
    chuyển đổi một cách nhanh chóng và dễ dàng một bộ dữ liệu khổng lồ thành các trang dễ dàng điều hướng với
    chỉ một vài dòng code. Thật đấy.

    3. Paginator
    Ví dụ này sẽ bao gồm hai script, lớp Paginator có thể tái sử dụng và tập tin index sẽ hiển thị các phần tử của bảng và các điều khiển.

    Paginator.class.php
    Lớp Paginator sẽ chỉ có hai phương thức và hàm xây dựng, chúng ta sẽ xây dựng nó dần dần, làm đến đâu giải thích đến đó.

    PHP:
    <?php

    class Paginator {

    private $_conn;
    private $_limit;
    private $_page;
    private $_query;
    private $_total;

    }
    Định nghĩa này chỉ thiết lập cho Paginator các biến thành viên cần thiết, vì đây là lớp trợ giúp và nó chỉ dành cho phân trang, nó sẽ dựa vào một kết nối hợp lệ đến máy chủ MySQL và một truy vấn đã được xác định trước, cái mà chúng ta sẽ nối các tham số cần thiết để phân trang kết quả. Chúng ta sẽ bắt đầu với phương thức xây dựng.

    PHP:
    <?php

    public function __construct( $conn, $query ) {

    $this->_conn = $conn;
    $this->_query = $query;

    $rs= $this->_conn->query( $this->_query );
    $this->_total = $rs->num_rows;

    }
    Khá đơn giản phải không? Phương thức này chỉ thiết lập kết nối cơ sở dữ liệu và truy vấn cần thiết của đối tượng, sau đó nó tính tổng số hàng truy xuất được bởi truy vấn đó mà không có bất kỳ giới hạn nào cũng không bỏ qua các tham số, số tổng này là cần thiết để tạo ra các liên kết cho phân trang.

    Xin lưu ý rằng, để đơn giản chúng tôi không kiểm tra lỗi hoặc bất kỳ kiểm tra tính hợp lệ nào của các tham số đã cho, nhưng trong một ứng dụng thực tế, việc kiểm tra này sẽ là cần thiết.

    Lấy về Kết quả
    Bây giờ hãy tạo ra phương thức mà sẽ thật sự phân trang dữ liệu và trả về kết quả.

    PHP:
    <?php
    public function getData( $limit = 10, $page = 1 ) {

    $this->_limit = $limit;
    $this->_page = $page;

    if ( $this->_limit == 'all' ) {
    $query = $this->_query;
    } else {
    $query = $this->_query . " LIMIT " . ( ( $this->_page - 1 ) * $this->_limit ) . ", $this->_limit";
    }
    $rs = $this->_conn->query( $query );

    while ( $row = $rs->fetch_assoc() ) {
    $results[] = $row;
    }

    $result = new stdClass();
    $result->page = $this->_page;
    $result->limit = $this->_limit;
    $result->total = $this->_total;
    $result->data = $results;

    return $result;
    }
    Hãy phân tích hàm này từng bước một, trước tiên chúng ta thiết lập các tham số limit (giới hạn) và page (trang), mặc định được thiết lập lần lượt là 10 và 1. Sau đó, chúng ta kiểm tra xem người dùng đang yêu cầu một số hàng hay tất cả chúng, dựa trên điều này và tham số page chúng ta thiết lập tham số LIMITcủa truy vấn, "- 1" của page đang tính đến việc chúng ta đang bắt đầu các trang bằng 1 thay vì 0.

    Sau đó, chúng ta chỉ cần đánh giá truy vấn và nhận về kết quả, cuối cùng chúng ta tạo ra một đối tượng kết quả mới có chứa các tham số limit, page và total của truy vấn đã được thực thi cũng như dữ liệu cho mỗi dòng đã truy xuất được.

    Hiện thị các Liên kết Phân trang
    Bây giờ hãy viết phương thức được sử dụng để tạo ra các liên kết phân trang.

    PHP:
    <?php
    public function createLinks( $links, $list_class ) {
    if ( $this->_limit == 'all' ) {
    return '';
    }

    $last = ceil( $this->_total / $this->_limit );

    $start = ( ( $this->_page - $links ) > 0 ) ? $this->_page - $links : 1;
    $end = ( ( $this->_page + $links ) < $last ) ? $this->_page + $links : $last;

    $html = '<ul class="' . $list_class . '">';

    $class = ( $this->_page == 1 ) ? "disabled" : "";
    $html .= '<li class="' . $class . '"><a href="?limit=' . $this->_limit . '&page=' . ( $this->_page - 1 ) . '">&laquo;</a></li>';

    if ( $start > 1 ) {
    $html .= '<li><a href="?limit=' . $this->_limit . '&page=1">1</a></li>';
    $html .= '<li class="disabled"><span>...</span></li>';
    }

    for ( $i = $start ; $i <= $end; $i++ ) {
    $class = ( $this->_page == $i ) ? "active" : "";
    $html .= '<li class="' . $class . '"><a href="?limit=' . $this->_limit . '&page=' . $i . '">' . $i . '</a></li>';
    }

    if ( $end < $last ) {
    $html .= '<li class="disabled"><span>...</span></li>';
    $html .= '<li><a href="?limit=' . $this->_limit . '&page=' . $last . '">' . $last . '</a></li>';
    }

    $class = ( $this->_page == $last ) ? "disabled" : "";
    $html .= '<li class="' . $class . '"><a href="?limit=' . $this->_limit . '&page=' . ( $this->_page + 1 ) . '">&raquo;</a></li>';

    $html .= '</ul>';

    return $html;
    }
    Đây là một phương thức khá dài, khoảng 34 dòng code, vì vậy bây giờ hãy làm rõ những gì đang diễ ra trong phương thức này.

    1. Trước tiên chúng ta đánh giá xem liệu người dùng đang yêu cầu một số liên kết nhất định hay là tất cả, trong trường hợp thứ hai chúng ta chỉ cần trả về một chuỗi rỗng vì không cần phải phân trang.
    2. Sau đó, chúng ta tính toán trang cuối dựa trên tổng số dòng hiện có và các phần tử cần thiết trên mỗi trang.
    3. Sau đó, chúng ta lấy tham số links (liên kết) đại diện cho số liên kết để hiển thị bên dưới và bên trên trang hiện tại, và tính toán liên kết start và end.
    4. Bây giờ chúng ta tạo ra thẻ mở cho danh sách và thiết lập lớp của nó bằng tham số list_class và thêm liên kết "previous page", lưu ý rằng, đối với liên kết này chúng ta kiểm tra xem trang hiện tại có là trang đầu hay không, và nếu như vậy, chúng ta thiết lập thuộc tính disabled cho liên kết.
    5. Tại thời điểm này, chúng ta hiển thị một liên kết đến trang đầu tiên và một biểu tượng dấu chấm lửng trong trường hợp liên kết start không phải là liên kết đầu tiên.
    6. Tiếp theo, chúng ta thêm các liên kết bên dưới và bên trên trang hiện tại dựa trên các tham số start và end được tính toán trước đó, trong mỗi bước chúng ta đánh giá một lần nữa trang hiện tại mà trang liên kết hiển thị và thiết lập lớp active cho phù hợp.
    7. Sau đó, chúng ta sẽ hiển thị một biểu tượng dấu chấm lửng khác và liên kết đến trang cuối trong trường hợp liên kết end không phải là cuối cùng.
    8. Cuối cùng, chúng ta hiển thị liên kết "next page" và thiết lập trạng thái disabled khi người dùng đang xem trang cuối, đóng danh sách và trả về chuỗi HTML được tạo ra.
    Đó là tất cả những gì có trong lớp Paginator, đương nhiên chúng ta có thể thêm các hàm setter và getter cho các tham số kết nối cơ sở dữ liệu, limit, page, query và total, nhưng để đơn giản chúng ta sẽ làm nó theo cách này.

    4. Index.php
    Bây giờ chúng ta sẽ tạo ra tập tin chịu trách nhiệm sử dụng lớp Paginator và hiển thị dữ liệu, vì vậy trước hết hãy để tôi trình bày cho bạn thấy HTML cơ bản.

    PHP:
    <!DOCTYPE html>
    <head>
    <title>PHP Pagination</title>
    <link rel="stylesheet" href="css/bootstrap.min.css">
    </head>
    <body>
    <div class="container">
    <div class="col-md-10 col-md-offset-1">
    <h1>PHP Pagination</h1>
    <table class="table table-striped table-condensed table-bordered table-rounded">
    <thead>
    <tr>
    <th>City</th>
    <th width="20%">Country</th>
    <th width="20%">Continent</th>
    <th width="25%">Region</th>
    </tr>
    </thead>
    <tbody></tbody>
    </table>
    </div>
    </div>
    </body>
    </html>
    Khá đơn giản, tập tin này chỉ hiển thị một bảng mà chúng ta sẽ điền vào bằng các thông tin lấy được từ cơ sở dữ liệu, lưu ý rằng, đối với ví dụ này tôi đang sử dụng bootstrap để định phong cách cho trang.

    Sử dụng Paginator
    PHP:
    <?php for( $i = 0; $i < count( $results->data ); $i++ ) : ?>
    <tr>
    <td><?php echo $results->data[$i]['Name']; ?></td>
    <td><?php echo $results->data[$i]['Country']; ?></td>
    <td><?php echo $results->data[$i]['Continent']; ?></td>
    <td><?php echo $results->data[$i]['Region']; ?></td>
    </tr>
    <?php endfor; ?>
    Bây giờ để tận dụng lớp Paginator của chúng ta, hãy thêm code php sau đây ở phía trên cùng của tài liệu.

    PHP:
    <?php
    require_once 'Paginator.class.php';

    $conn = new mysqli( '127.0.0.1', 'root', 'root', 'world' );

    $limit = ( isset( $_GET['limit'] ) ) ? $_GET['limit'] : 25;
    $page = ( isset( $_GET['page'] ) ) ? $_GET['page'] : 1;
    $links = ( isset( $_GET['links'] ) ) ? $_GET['links'] : 7;
    $query = "SELECT City.Name, City.CountryCode, Country.Code, Country.Name AS Country, Country.Continent, Country.Region FROM City, Country WHERE City.CountryCode = Country.Code";

    $Paginator = new Paginator( $conn, $query );

    $results = $Paginator->getData( $page, $limit );
    ?>
    Script này khá đơn giản, chúng ta chỉ yêu cầu lớp Paginator, lưu ý code này giả định rằng tập tin này nằm trong cùng một thư mục với tập tin index.php, nếu không bạn nên cập nhật đường dẫn cho phù hợp.

    Sau đó, chúng ta tạo kết nối đến cơ sở dữ liệu bằng cách sử dụng thư viện MySQLi, truy xuất các tham số của paginator từ yêu cầu GET và thiết lập truy vấn, vì đây không phải là một bài viết về MySQL hay bất kỳ một cơ sở dữ liệu nào nên tôi sẽ không đi vào chi tiết về kết nối hoặc truy vấn được sử dụng ở đây.

    Cuối cùng, chúng ta tạo ra đối tượng Paginator và truy xuất kết quả cho trang hiện tại.

    Hiển thị Kết quả
    Bây giờ để hiển thị kết quả đã lấy được, hãy thêm code sau vào phần thân của bảng (table).

    PHP:
    <?php for( $i = 0; $i < count( $results->data ); $i++ ) : ?>
    <tr>
    <td><?php echo $results->data[$i]['Name']; ?></td>
    <td><?php echo $results->data[$i]['Country']; ?></td>
    <td><?php echo $results->data[$i]['Continent']; ?></td>
    <td><?php echo $results->data[$i]['Region']; ?></td>
    </tr>
    <?php endfor; ?>
    Ở đây, chúng ta đơn giản là lặp qua thuộc tính data của kết quả (results) chứa các bản ghi thành phố và tạo ra một hàng cho mỗi một trong số chúng.

    Các Liên kết Phân trang
    Bây giờ để hiển thị các liên kết phân trang hãy thêm code dưới đây vào bên dưới bảng.

    PHP:
    <?php echo $Paginator->createLinks( $links, 'pagination pagination-sm' ); ?>
    Đối với phương thức createLinks của Paginator chúng ta truyền tham số links và lớp css có được cho các liên kết phân trang được sử dụng từ bootstrap. Đây là kết quả trang đã được tạo ra.

    [​IMG]
    Tóm tắt
    Bài này hẳn sẽ cung cấp cho bạn tất cả mọi thứ mà bạn cần phải biết để làm việc với phân trang trong ứng dụng của bạn.

    [email protected]
    My PGP key: https://goo.gl/triziq
     

Share This Page