12/08/2018, 13:08

Laravel Pagination

1. Giới thiệu Ngày nay, trong quá trình lập trình web công việc phân trang dữ liệu là không thể thiếu. Nó giúp chúng ta xử lý dữ liệu hợp lý hơn, đồng thời quản lý việc truy suất dữ liệu hợp lý hơn. Các framework PHP hầu như đều hỗ trợ phân trang, Laravel cũng vậy. Laravel cung cấp cho chúng ...

laravel_logo.jpg

1. Giới thiệu

Ngày nay, trong quá trình lập trình web công việc phân trang dữ liệu là không thể thiếu. Nó giúp chúng ta xử lý dữ liệu hợp lý hơn, đồng thời quản lý việc truy suất dữ liệu hợp lý hơn. Các framework PHP hầu như đều hỗ trợ phân trang, Laravel cũng vậy. Laravel cung cấp cho chúng ta nhiều phương thức phân trang đơn giản cùng những phương thức đi kèm như render(), total(),...

2. Phân trang trong Laravel

Phân trang trong laravel được kết hợp với bootstrap css nên việc tạo ra phần quản lý trang với hàm render() thực sự sẽ rất dễ dàng và đơn giản. Tuy nhiên để custom lại cách thức render phân trang của nó, chúng ta cần phải hiểu sâu hơn về pagination trong laravel.

2.1 Phân trang query

<?php

namespace AppHttpControllers;

use DB;
use AppHttpControllersController;

class UserController extends Controller
{
    /**
     * Show all of the users for the application.
     *
     * @return Response
     */
    public function index()
    {
        $users = DB::table('users')->paginate(15);

        return view('user.index', ['users' => $users]);
    }
}

Phía trên là đoạn code đơn giản phân trang dữ liệu từ bảng user và mỗi trang sẽ có 15 items. Hiện tại, laravel chưa hỗ trợ groupBy trực tiếp trong câu lệnh cùng với hàm paginate, vậy nên để group dữ liệu chúng ta phải query dữ liệu sau đó tạo paginator riêng sau đó.

Nếu chúng ta chỉ cần hiển thị 2 button next và previous trong phân trang, simplePaginate là một lựa chọn tốt hơn thay thế cho phương thức paginate mặc định, simplePaginate rất hữu dụng cho cơ sở dữ liệu lớn với nhiều bản ghi và chúng ta không cần hiển thị link cho mỗi trang (page number khi render trong view:

$users = DB::table('users')->simplePaginate(15);

2.2 Phân trang Eloquent

Ngoài cách phân trang trực tiếp cùng query builder, chúng ta có thể phân trang bằng một eloquent query. Tương tự với ví dụ ở trên, chúng ta phân trang model User với 15 items trên một trang:

$users = AppUser::paginate(15);

$users = User::where('votes', '>', 100)->paginate(15);

$users = User::where('votes', '>', 100)->simplePaginate(15);

Với 3 câu lệnh trên, câu lệnh thứ nhất thực hiện lấy dữ liệu rồi phân trang. Câu lệnh thứ 2 được kết hợp với một query khác trong quá trình phân trang, câu lệnh thứ 3 thực hiện tương tự câu lệnh 2 nhưng sẽ tạo ra simplePaginate đã được nói ở trên.

2.5 Tự tạo Paginator

Thi thoảng chúng ta không muốn sử dụng trực tiếp paginator mặc định của laravel chúng ta có thể tạo một paginator của riêng mình bằng cách extend IlluminatePaginationPaginator hoặc IlluminatePaginationLengthAwarePaginator, phụ thuộc vào việc chúng ta cần gì. IlluminatePaginationLengthAwarePaginator sẽ tương tự với paginate mặc định của laravel, còn IlluminatePaginationPaginator tương tự với simplePaginate. LengthAwarePagitator cung cấp nhiều phương thức hơn Paginator mặc định.

<?php

namespace AppPaginator;

use  IlluminatePaginationLengthAwarePaginator as Paginator

class CustomPaginator extends Paginator
{
	// Các phương thức mở rộng
}

3. Hiển thị kết qủa phân trang

Laravel cung cấp phương thức render(), nó sẽ tạo ra view cơ bản được kết hợp với bootstrap 3, tùy vào chúng ta sử dụng paginate hay simplePaginate sẽ có các view khác nhau được render.

    <div class="container">
        @foreach ($users as $user)
            {{ $user->name }}
        @endforeach
    </div>

    {!! $users->render() !!}

Chú ý: phải đặt $$sers->render() trong {!! !!}

3.1 Tùy chỉnh url của trang

Phương thức setPath của kết quả phân trang cho phép chúng ta tùy chỉnh lại URL khi paginator tạo link cho mỗi trang. Tùy thuộc vào yêu cầu mà thúng ta có thể tùy chỉnh lại nó, ví dụ:

Route::get('users', function () {
	$users = AppUser::paginate(15);

	$users->setPath('custom/url');
    // ...
});

Đoạn code trên đặt lại đường link theo dạng http://example.com/custom/url?page=N

3.2 Mở rộng link

Phương thức append cho phép chúng ta thêm query string vào đường link khi phân trang, ví dụ: chúng ta muốn thêm query &sort=votes vào mỗi đường link khi render, chúng ta dùng:

{!! $users->appends(['sort' => 'votes'])->links() !!}

Nếu chúng ta muốn thêm hash fragment vào đường link cho mỗi trang, chúng ta sử dụng phương thức fragment. Ví dụ:

{!! $users->fragment('foo')->links() !!}

3.3 Các phương thức hỗ trợ

Tùy vào chúng ta chọn paginate hay simplePaginate mà có các phương thức hỗ trợ khác nhau:

  • $$esults->count()
  • $$esults->currentPage()
  • $$esults->hasMorePages()
  • $$esults->lastPage() (simplePaginate không có)
  • $$esults->nextPageUrl()
  • $$esults->perPage()
  • $$esults->previousPageUrl()
  • $$esults->total() (simplePaginate không có)
  • results−>url(results->url(results>url(page)

4. Chuyển đổi kết quả phân trang

Chúng tùy vào mục đích sử dụng chúng ta có thể chuyển đổi kết quả phân trang thành array hay json. Nếu muốn convert sang array chúng ta dùng phương thức toArray(), còn nếu muốn convert sang json chúng ta dùng phương thức toJson().

Đôi khi chúng ta chỉ muốn lấy kết quả phân trang phục vụ cho các tác vụ liên quan tới javascript hoặc muốn lấy dữ liệu theo định dạng json, chúng ta có thể convert dữ liệu phân trang thành json. Vì paginator result trong laravel được implement từ IlluminateContractsSupportJsonableInterface vậy nên chúng ta có thể convert dữ liệu phân trang bằng phương thức toJson của JsonableInterface.

Route::get('users', function () {
    return AppUser::paginate()->toJson();
});

Ngoài cách trả dữ liệu từ Route chúng ta có thể trả về từ Controller, chúng ta cũng có thể kết hợp với các query khác như đã nói ở trên trước khi convert kết quả sang json.

Kết quả convert sẽ bao gồm hầu nhiều thông tin như total, current_page, last_page,... Dưới đây là ví dụ:

{
   "total": 50,
   "per_page": 15,
   "current_page": 1,
   "last_page": 4,
   "next_page_url": "http://laravel.app?page=2",
   "prev_page_url": null,
   "from": 1,
   "to": 15,
   "data":[
        {
            // Result Object
        },
        {
            // Result Object
        }
   ]
}
0