BAB 34: Pagination
Menambahkan paginasi pada daftar data menggunakan fitur paginasi bawaan Laravel.
Dengan 28 catatan yang baru saja diisi seeder, masalah yang tadinya tidak terlihat kini jadi nyata: halaman listing menampilkan semua data sekaligus dalam satu gulungan panjang. Tidak ada pembatas, tidak ada navigasi halaman. Semakin banyak data yang ditambahkan, semakin tidak nyaman halaman itu digunakan.
Laravel menyediakan solusi bawaan untuk ini. Satu perubahan kecil di controller — mengganti get() dengan paginate() — sudah cukup untuk membagi data menjadi beberapa halaman, lengkap dengan metadata seperti total record, halaman aktif, dan link navigasi yang bisa di-render langsung di view.
Mengubah Query di Controller
Buka app/Http/Controllers/CatatanController.php dan ubah method index():
// app/Http/Controllers/CatatanController.php
<?php
namespace App\Http\Controllers;
use App\Models\Catatan;
use Illuminate\Http\Request;
class CatatanController extends Controller
{
public function index()
{
$catatans = Catatan::latest()->paginate(10);
return view('catatan.index', compact('catatans'));
}
// ... method lainnya tidak berubah
}
Perubahan hanya satu baris: ->get() diganti ->paginate(10). Angka 10 adalah jumlah record per halaman.
Perbedaan yang terjadi di balik layar cukup signifikan. get() mengeksekusi SELECT * FROM catatan ORDER BY created_at DESC dan mengembalikan semua record sebagai Collection. paginate(10) mengeksekusi dua query: satu SELECT COUNT(*) untuk menghitung total, satu lagi SELECT ... LIMIT 10 OFFSET 0 untuk mengambil data halaman aktif. Hasilnya bukan Collection biasa, melainkan objek LengthAwarePaginator yang menyimpan data sekaligus metadata paginasi.
Angka per halaman bisa disesuaikan dengan konteks. Halaman listing sederhana biasanya 10–15, sedangkan halaman admin yang padat bisa sampai 25–50. Pilih yang membuat halaman tidak terasa terlalu panjang atau terlalu sering berganti.
Menambahkan Navigasi di View
Objek LengthAwarePaginator punya method links() yang menghasilkan HTML navigasi halaman secara otomatis. Tambahkan ke resources/views/catatan/index.blade.php:
{{-- resources/views/catatan/index.blade.php --}}
@extends('layouts.app')
@section('title', 'Daftar Catatan')
@section('content')
<div class="flex items-center justify-between mb-6">
<h1 class="text-2xl font-bold text-gray-900">Catatan Saya</h1>
<a href="{{ route('catatan.create') }}" class="bg-blue-600 text-white px-4 py-2 rounded text-sm font-medium hover:bg-blue-700">
+ Catatan Baru
</a>
</div>
@if ($catatans->isEmpty())
<p class="text-gray-500">Belum ada catatan. Mulai buat catatan pertamamu.</p>
@else
<div class="space-y-3">
@foreach ($catatans as $catatan)
<div class="bg-white p-4 rounded border border-gray-200">
<a href="{{ route('catatan.show', $catatan) }}" class="font-medium text-gray-900 hover:text-blue-600">
{{ $catatan->judul }}
</a>
<div class="mt-1">
<span class="text-xs text-gray-500 capitalize">Prioritas: {{ $catatan->prioritas }}</span>
</div>
</div>
@endforeach
</div>
<div class="mt-6">
{{ $catatans->links() }}
</div>
@endif
@endsection
{{ $catatans->links() }} adalah satu-satunya tambahan. Method ini menghasilkan blok HTML lengkap berisi tombol “Previous”, nomor-nomor halaman, dan tombol “Next” — semuanya dengan URL yang sudah mengandung parameter ?page=N yang benar.
Hasil di Browser
Buka /catatan. Dari 28 catatan yang ada, halaman pertama menampilkan 10 data terbaru. Di bagian bawah daftar muncul navigasi halaman.
Klik halaman 2 — URL berubah menjadi /catatan?page=2, dan 10 data berikutnya ditampilkan. Laravel membaca parameter page dari query string secara otomatis tanpa perlu kode tambahan di controller.
Mempertahankan Parameter Lain di URL
Jika nantinya halaman listing memiliki filter atau pencarian lewat query string, link paginasi perlu mempertahankan parameter tersebut saat berpindah halaman. Gunakan withQueryString():
// app/Http/Controllers/CatatanController.php
public function index()
{
$catatans = Catatan::latest()->paginate(10)->withQueryString();
return view('catatan.index', compact('catatans'));
}
Dengan withQueryString(), jika URL saat ini adalah /catatan?prioritas=tinggi&page=1, maka link ke halaman 2 akan menghasilkan /catatan?prioritas=tinggi&page=2 — parameter prioritas ikut terbawa. Tanpanya, link paginasi hanya mengandung ?page=N dan parameter lain hilang.
Untuk aplikasi ini yang belum punya filter, withQueryString() belum diperlukan. Tapi perlu diketahui keberadaannya untuk pengembangan berikutnya.
Menampilkan Info Halaman
LengthAwarePaginator juga menyediakan informasi yang berguna untuk ditampilkan ke pengguna:
{{-- di dalam @else, sebelum div space-y-3 --}}
<p class="text-sm text-gray-500 mb-4">
Menampilkan {{ $catatans->firstItem() }}–{{ $catatans->lastItem() }}
dari {{ $catatans->total() }} catatan
</p>
Hasilnya: teks seperti “Menampilkan 1–10 dari 28 catatan” yang membantu pengguna tahu posisinya dalam daftar. Ini opsional tapi meningkatkan kejelasan antarmuka, terutama ketika data sudah banyak.
Beberapa method LengthAwarePaginator yang sering berguna:
| Method | Keterangan |
|---|---|
$catatans->total() | Total seluruh record |
$catatans->perPage() | Jumlah record per halaman |
$catatans->currentPage() | Nomor halaman aktif |
$catatans->lastPage() | Nomor halaman terakhir |
$catatans->firstItem() | Nomor urut record pertama di halaman ini |
$catatans->lastItem() | Nomor urut record terakhir di halaman ini |
$catatans->hasPages() | true jika ada lebih dari satu halaman |
Simple Pagination
Jika tidak perlu menampilkan nomor-nomor halaman dan hanya butuh tombol “Previous” dan “Next”, gunakan simplePaginate():
$catatans = Catatan::latest()->simplePaginate(10);
simplePaginate() lebih efisien karena tidak menjalankan query COUNT(*). Ini penting untuk tabel dengan jutaan record di mana menghitung total bisa lambat. Kelemahannya: tidak ada informasi total record dan nomor halaman — hanya “ada halaman sebelumnya” dan “ada halaman berikutnya”.
Untuk aplikasi catatan ini, paginate() biasa sudah cukup karena datanya tidak akan mencapai skala yang membuat COUNT(*) jadi masalah.
Pagination Sudah Berjalan
Dua perubahan — satu baris di controller, satu baris di view — sudah membuat halaman listing jauh lebih fungsional. Data 28 catatan terbagi rapi menjadi 3 halaman, dan navigasi antar halaman berjalan tanpa kode tambahan yang rumit.
Ini adalah contoh baik dari filosofi Laravel: fitur yang kompleks di balik layar dihadirkan dengan antarmuka yang sesederhana mungkin bagi developer. Kita tidak perlu menghitung offset, membangun URL secara manual, atau menghasilkan HTML navigasi dari nol.
Aplikasi manajemen catatan sudah punya semua fitur inti yang direncanakan — CRUD lengkap ditambah paginasi. Yang tersisa adalah satu lapisan keamanan: memastikan semua route catatan hanya bisa diakses oleh pengguna yang sudah login. Itulah yang akan diselesaikan di bab terakhir studi kasus ini.