| Current Path : /var/www/html/mysis/doc/ |
| Current File : /var/www/html/mysis/doc/pagination.md |
# Panduan Pagination dalam MySIS
Dokumentasi ini menerangkan cara pagination berfungsi dalam sistem MySIS, perbezaan antara pagination default Laravel dan custom partial, serta cara menggunakannya.
## 1. Dua Jenis Pagination
### A. Default Laravel Pagination
Cara guna di view:
```blade
{{ $paginator->links() }}
```
Ciri-ciri:
- Layout disediakan oleh Laravel (Tailwind view)
- Mempunyai `onEachSide(n)` untuk kawal bilangan nombor page
- Kekurangan: untuk page count yang kecil, nombor page akan bertindih (semua keluar tanpa ellipsis)
### B. Custom Partial Pagination (digunakan sekarang)
Cara guna di view:
```blade
@include('partials.pagination', ['paginator' => $paginator])
```
File: `resources/views/partials/pagination.blade.php`
Ciri-ciri:
- Layout ditulis sendiri (Prev, nombor page, Next)
- **Sentiasa konsisten** — format tetap walaupun klik prev/next
- **Current page sentiasa nampak** (highlight indigo)
- Guna `...` (ellipsis) untuk page yang jauh
- Tidak bergantung pada `onEachSide` — logik hardcode dalam partial
## 2. Perbezaan Utama
| Aspek | Default Laravel (`links()`) | Custom Partial |
|-------|----------------------------|----------------|
| Kod | `{{ $paginator->links() }}` | `@include('partials.pagination', ...)` |
| `onEachSide(n)` | ✅ Berfungsi | ❌ Diabaikan |
| Ellipsis pada page sedikit | ❌ Semua nombor keluar | ✅ Sentiasa compact |
| Info "Showing X to Y of Z" | ✅ Ada | ✅ Ada |
| Format konsisten | ❌ Nombor berubah ikut window | ✅ Tetap (1 kiri, last kanan, current tengah) |
| Kawalan penuh | Terhad | Penuh (boleh ubah style) |
## 3. onEachSide(n)
`onEachSide(n)` **hanya berfungsi dengan default Laravel pagination**.
Ia menentukan berapa nombor page yang ditunjukkan di sebelah kiri dan kanan current page.
Contoh (page 10 dari 20):
| onEachSide | Paparan |
|------------|---------|
| `1` | `1 2 ... 9 [10] 11 ... 19 20` |
| `2` | `1 2 3 ... 8 9 [10] 11 12 ... 18 19 20` |
| `3` | `1 2 3 4 ... 7 8 9 [10] 11 12 13 ... 17 18 19 20` |
**Nota:** Untuk page count yang kecil (contoh 7 pages), Laravel default akan paparkan semua nombor tanpa ellipsis kerana window first + slider + last bertindih. Ini sebab custom partial dicipta.
## 4. Format Custom Partial
Struktur asas (sentiasa sama):
```
Prev | Page 1 | ... | [Current] | ... | Last Page | Next
```
Contoh paparan untuk 7 pages:
| Page | Paparan |
|------|---------|
| Page 1 | `« Prev [1] ... 7 Next »` |
| Page 2 | `« Prev 1 [2] ... 7 Next »` |
| Page 4 | `« Prev 1 ... [4] ... 7 Next »` |
| Page 7 | `« Prev 1 ... [7] Next »` |
- Page 1 sentiasa di kiri
- Page terakhir sentiasa di kanan
- Current page (indigo) sentiasa nampak di tengah
- Ellipsis `...` untuk gap
## 5. Contoh Kod
### A. Controller (dengan paginate)
```php
// app/Http/Controllers/UserController.php
public function index(Request $request)
{
$user = User::where('email','<>','admin@sis.com')
->when($request->search, function ($query, $search) {
return $query->where(function ($q) use ($search) {
$q->where('name', 'like', "%{$search}%")
->orWhere('email', 'like', "%{$search}%");
});
})
->paginate(10) // 10 records per page
->appends(['search' => $request->search]); // kekal search semasa tukar page
// Set bouncerRole untuk setiap user
foreach($user as $u){
$roleName = $u->getRoles()->first();
if ($roleName == null) {
$u->bouncerRole = 'No roles assigned';
} else {
$role = Bouncer::role()->where('name', $roleName)->first();
$u->bouncerRole = $role->title ?? $roleName;
}
}
return view('user.index')->with('user', $user);
}
```
```php
// app/Http/Controllers/SafeguardController.php
public function index(Request $request)
{
$safeguard = Safeguard::when($request->search, function ($query, $search) {
return $query->where(function ($q) use ($search) {
$q->where('title', 'like', "%{$search}%")
->orWhere('principle', 'like', "%{$search}%");
});
})
->paginate(10)
->appends(['search' => $request->search]);
return view('safeguard.index')->with('safeguard', $safeguard);
}
```
### B. View — Cara Default Laravel
```blade
{{-- resources/views/user/index.blade.php --}}
{{ $user->links() }}
```
### C. View — Cara Custom Partial (sekarang)
```blade
{{-- resources/views/user/index.blade.php --}}
@include('partials.pagination', ['paginator' => $user])
```
### D. Kod Penuh Custom Partial
File: `resources/views/partials/pagination.blade.php`
```blade
@if($paginator->hasPages())
<div class="mt-4 flex items-center justify-between">
<div class="text-sm text-gray-500">
Showing <span class="font-medium">{{ $paginator->firstItem() }}</span> to <span class="font-medium">{{ $paginator->lastItem() }}</span> of <span class="font-medium">{{ $paginator->total() }}</span>
</div>
<nav class="flex items-center gap-1">
{{-- Prev --}}
@if($paginator->onFirstPage())
<span class="px-3 py-1.5 text-sm text-gray-400 bg-white border border-gray-200 rounded-md cursor-not-allowed">Prev</span>
@else
<a href="{{ $paginator->previousPageUrl() }}" class="px-3 py-1.5 text-sm text-gray-600 bg-white border border-gray-300 rounded-md hover:bg-gray-50">Prev</a>
@endif
{{-- Page 1 (sentiasa di kiri) --}}
@if($paginator->currentPage() == 1)
<span class="px-3 py-1.5 text-sm font-medium text-white bg-indigo-600 rounded-md">1</span>
@else
<a href="{{ $paginator->url(1) }}" class="px-3 py-1.5 text-sm text-gray-600 bg-white border border-gray-300 rounded-md hover:bg-gray-50">1</a>
@endif
{{-- Ellipsis sebelum current (jika gap) --}}
@if($paginator->currentPage() > 3)
<span class="px-1.5 text-sm text-gray-400">...</span>
@endif
{{-- Current page (di tengah) --}}
@if($paginator->currentPage() > 1 && $paginator->currentPage() < $paginator->lastPage())
<span class="px-3 py-1.5 text-sm font-medium text-white bg-indigo-600 rounded-md">{{ $paginator->currentPage() }}</span>
@endif
{{-- Ellipsis selepas current (jika gap) --}}
@if($paginator->currentPage() < $paginator->lastPage() - 2)
<span class="px-1.5 text-sm text-gray-400">...</span>
@endif
{{-- Last page (sentiasa di kanan) --}}
@if($paginator->lastPage() > 1)
@if($paginator->currentPage() == $paginator->lastPage())
<span class="px-3 py-1.5 text-sm font-medium text-white bg-indigo-600 rounded-md">{{ $paginator->lastPage() }}</span>
@else
<a href="{{ $paginator->url($paginator->lastPage()) }}" class="px-3 py-1.5 text-sm text-gray-600 bg-white border border-gray-300 rounded-md hover:bg-gray-50">{{ $paginator->lastPage() }}</a>
@endif
@endif
{{-- Next --}}
@if($paginator->hasMorePages())
<a href="{{ $paginator->nextPageUrl() }}" class="px-3 py-1.5 text-sm text-gray-600 bg-white border border-gray-300 rounded-md hover:bg-gray-50">Next</a>
@else
<span class="px-3 py-1.5 text-sm text-gray-400 bg-white border border-gray-200 rounded-md cursor-not-allowed">Next</span>
@endif
</nav>
</div>
@endif
```
## 6. Penjelasan Logik
### Kenapa format sentiasa konsisten?
Sebab struktur tetap: **1 sentiasa kiri, last sentiasa kanan, current sentiasa di tengah**. Yang berubah cuma angka current dan ellipsis ikut kedudukan page. Jadi tak kira page mana kau berada, layout sama je.
### Logik setiap bahagian
**1. Button Prev**
```blade
@if($paginator->onFirstPage())
<span>Prev</span> {{-- halaman pertama → tak boleh klik --}}
@else
<a href="...">Prev</a> {{-- bukan halaman pertama → boleh klik --}}
@endif
```
**2. Page 1 (sentiasa di kiri)**
```blade
@if($paginator->currentPage() == 1)
<span class="bg-indigo-600">1</span> {{-- current → highlight --}}
@else
<a href="{{ $paginator->url(1) }}">1</a> {{-- bukan current → link --}}
@endif
```
**3. Ellipsis sebelum current (kalau ada gap)**
```blade
@if($paginator->currentPage() > 3)
<span>...</span> {{-- jarak lebih dari 3 → tunjuk ... --}}
@endif
```
Contoh: current page 5 → ada gap dari page 1 → `1 ... 5`
**4. Current page (di tengah)**
```blade
@if($paginator->currentPage() > 1 && $paginator->currentPage() < $paginator->lastPage())
<span class="bg-indigo-600">{{ $paginator->currentPage() }}</span>
@endif
```
Tunjuk page sekarang, warna indigo. Tak dikira bila page 1 atau page terakhir (sebab dah di-highlight kat tepi).
**5. Ellipsis selepas current**
```blade
@if($paginator->currentPage() < $paginator->lastPage() - 2)
<span>...</span>
@endif
```
**6. Last page (sentiasa di kanan)**
```blade
@if($paginator->currentPage() == $paginator->lastPage())
<span class="bg-indigo-600">{{ $paginator->lastPage() }}</span>
@else
<a href="{{ $paginator->url($paginator->lastPage()) }}">{{ $paginator->lastPage() }}</a>
@endif
```
**7. Button Next** — sama macam Prev, terbalik je.
### Method Paginator yang Digunakan
| Method | Fungsi |
|--------|--------|
| `$paginator->hasPages()` | Ada lebih dari 1 page? |
| `$paginator->currentPage()` | Page sekarang |
| `$paginator->lastPage()` | Jumlah page |
| `$paginator->onFirstPage()` | Betul-betul page 1? |
| `$paginator->hasMorePages()` | Ada page seterusnya? |
| `$paginator->url($n)` | URL untuk page n |
| `$paginator->previousPageUrl()` | URL page sebelum |
| `$paginator->nextPageUrl()` | URL page selepas |
| `$paginator->firstItem()` / `lastItem()` / `total()` | Info "Showing X to Y of Z" |
## 7. Pages yang Menggunakan Custom Partial
Semua index pages berikut sudah menggunakan custom partial:
| Page | Controller Method | Variable |
|------|-------------------|----------|
| `/indicator/index` | `IndicatorController@index` | `$indicator` |
| `/safeguard/index` | `SafeguardController@index` | `$safeguard` |
| `/state/index` | `StateController@index` | `$state` |
| `/state/fmu_listing/{state}` | `StateController@fmu_listing` | `$fmus` |
| `/user/index` | `UserController@index` | `$user` |
| `/user/role_index` | `UserController@role_index` | `$role` |
## 8. Cleanup
`->onEachSide(1)` dalam controller telah **dibuang** kerana custom partial mengabaikannya (dead code).
Contoh kod bersih:
```php
->paginate(10)->appends(['search' => $request->search])
```