State Tabel yang Dipertahankan
State tabel tersimpan di URL: halaman, ukuran halaman, pencarian, pengurutan, filter, susunan kolom, dan grup aktif. Inilah yang membuat tombol kembali, maju, refresh, bookmark, dan "kirim link ini ke saya" tetap bekerja sebagaimana mestinya. Selain itu, tabel dapat memilih untuk mengingat sebagian state tersebut di session, sehingga saat pengguna kembali ke daftar, kondisinya tetap seperti terakhir ditinggalkan.
Gunakan persistence pada tabel yang benar-benar dipakai untuk bekerja — misalnya antrean, daftar moderasi, atau ledger. Biarkan nonaktif pada tabel yang hanya dilewati pengguna: kembali ke tabel dan mendapati filter dari sesuatu yang diketik kemarin biasanya justru membingungkan.
Contoh minimal dengan persistence
use PandaPanel\Tables\Columns\TextColumn;
use PandaPanel\Tables\Enums\SortDirection;
use PandaPanel\Tables\TableSchema;
return $table
->columns([
TextColumn::make('reference')->searchable()->sortable(),
TextColumn::make('status')->sortable(),
])
->defaultSort('created_at', SortDirection::Descending)
->persistSearchInSession()
->persistSortInSession()
->persistFiltersInSession()
->persistColumnsInSession();2
3
4
5
6
7
8
9
10
11
12
13
14
Keempat opsi tersebut nonaktif secara default dan bekerja secara independen.
Cakupan setiap method
| Method | Yang diingat | Method pembaca |
|---|---|---|
persistSearchInSession(bool $persist = true) | search | persistsSearchInSession(): bool |
persistSortInSession(bool $persist = true) | sort, direction, group | persistsSortInSession(): bool |
persistFiltersInSession(bool $persist = true) | seluruh map filters | persistsFiltersInSession(): bool |
persistColumnsInSession(bool $persist = true) | columns.visible dan columns.order | persistsColumnsInSession(): bool |
group ikut disimpan bersama pengurutan karena keduanya merupakan satu keputusan tentang bagaimana baris disusun.
Tidak ada state lain yang diingat. page, perPage, dan kotak pencarian per kolom selalu dibaca dari request. Mengingat nomor halaman dapat membuat pengguna membuka halaman tujuh dari hasil yang sudah berubah, sedangkan ukuran halaman adalah preferensi yang sudah dinyatakan oleh kontrol ukuran halaman itu sendiri.
Seluruh peta state
| Query parameter | Dipertahankan oleh | Divalidasi terhadap |
|---|---|---|
page | — | max(1, …) |
perPage | — | perPageOptions() |
search | persistSearchInSession() | di-trim, dipotong maksimal 255 karakter |
columnSearch[{column}] | — | kolom yang mendeklarasikan searchable(individually: true) |
sort | persistSortInSession() | kolom yang mendeklarasikan sortable() |
direction | persistSortInSession() | SortDirection; nilai tidak dikenal fallback ke asc |
group | persistSortInSession() | grup yang dideklarasikan |
filters[{name}] | persistFiltersInSession() | sanitize() milik masing-masing filter |
columns[visible][], columns[order][] | persistColumnsInSession() | nama kolom yang dideklarasikan dan dapat di-toggle |
Dua aturan yang membuat pemulihan state tetap aman
Request selalu menang ketika menyatakan sesuatu — termasuk ketika nilainya sekarang kosong. Hanya ketidakhadiran parameter yang membuat sistem fallback ke nilai yang tersimpan. Tanpa aturan ini, pengguna yang menghapus pencarian justru akan mendapatkan kembali pencarian lama dari session.
?search=orders simpan "orders", terapkan "orders"
?search= simpan "", jangan terapkan pencarian
(tanpa key search) terapkan nilai yang tersimpan2
3
Query string tidak dapat merepresentasikan array kosong secara langsung. Karena itu, ?filters= dan ?columns= digunakan sebagai cara URL mengatakan "filter ada, tetapi kosong" dan "gunakan susunan yang dideklarasikan". Frontend menulis sentinel tersebut setelah terjadi perubahan pada map terkait.
Nilai yang dipulihkan dari session tetap melewati validasi yang sama seperti nilai baru. Jika session menyimpan nama kolom pengurutan yang sudah tidak ada di tabel, nilai tersebut diabaikan sama seperti jika pengguna mengetiknya langsung di URL. Dengan demikian, mempersempit schema tidak pernah menghidupkan kembali kolom lama melalui session pengguna.
Session key
Session key dibangun oleh page, bukan dari data apa pun pada request:
| Tabel | Key |
|---|---|
| index resource | panel.{panel id}.table.{resource slug} |
| relation manager | panel.{panel id}.table.{resource slug}.{manager key} |
Setiap nilai disimpan satu level di bawah key tersebut — misalnya panel.admin.table.users.search, panel.admin.table.users.filters, dan seterusnya.
Jika key dapat dipengaruhi caller, satu tabel dapat membaca state tersimpan milik tabel lain. Karena itulah request tidak menjadi bagian dari key. Dua tabel tidak akan berbagi state yang diingat, dan tabel yang sama di dua panel memiliki set state yang berbeda.
use PandaPanel\Tables\TableQuery;
$tableQuery = new TableQuery(
$schema,
$request,
namespace: null,
sessionKey: sprintf('panel.%s.table.%s', $panel->getId(), UserResource::slug()),
);2
3
4
5
6
7
8
Memberikan sessionKey: null menonaktifkan persistence untuk instance tersebut, terlepas dari apa yang dideklarasikan schema. TableWidget melakukan hal ini: tabel dashboard memiliki namespace, tetapi tidak memiliki session key.
Tidak ada session, tidak masalah
Tabel dapat dirender di luar web stack — misalnya dari console command, job export yang membangun ulang query daftar, atau test yang tidak pernah menyentuh session. TableQuery memeriksa Request::hasSession() sebelum membaca atau menulis, sehingga tabel seperti itu hanya tidak mengingat state tanpa mengalami error.
Namespace versus persistence
Keduanya menjawab pertanyaan yang berbeda dan dapat digunakan bersamaan.
- Namespace — di bagian mana dalam query string state tabel ini disimpan, sehingga beberapa tabel dalam satu halaman tidak berebut parameter
page. Contoh:relations.tasks,widgets.recent-orders. - Session key — di mana state yang diingat disimpan, per pengguna.
new TableQuery($schema, $request, 'relations.tasks', 'panel.admin.table.projects.tasks');
// reads ?relations[tasks][sort]=title, remembers under panel.admin.table.projects.tasks.sort2
Pengujian
Karena session adalah objek yang diuji, gunakan satu session store yang sama di beberapa request dan buat Request baru setiap kali. request() adalah singleton; memodifikasi query bag-nya dapat membocorkan parameter dari satu pemanggilan ke pemanggilan berikutnya.
use Illuminate\Http\Request;
use PandaPanel\Tables\TableQuery;
function state(TableSchema $schema, array $query, string $key): array
{
$request = Request::create('/', 'GET', $query);
$request->setLaravelSession(app('session.store'));
return (new TableQuery($schema, $request, null, $key))->state();
}
$schema = $schema->persistSearchInSession();
state($schema, ['search' => 'Apollo'], 'table.projects');
// A second visit carrying nothing gets what the first one asked for.
expect(state($schema, [], 'table.projects')['search'])->toBe('Apollo');
// And an explicit empty value clears it.
expect(state($schema, ['search' => ''], 'table.projects')['search'])->toBeNull();2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Untuk kasus sentinel, buat request dari query string yang nyata: ['filters' => []] adalah sesuatu yang bisa ditulis oleh test, tetapi tidak bisa direpresentasikan URL secara langsung.
$request = Request::create('/?filters=', 'GET');Hal yang perlu diperhatikan
- Persistence berlaku per pengguna, bukan berdasarkan nilai session cookie yang Anda kendalikan. Ini adalah session storage biasa; menghapus session juga menghapus state yang diingat.
- Default filter dan filter yang diingat dapat saling berinteraksi. Default hanya mengisi kondisi benar-benar kosong. Setelah session mencatat bahwa pengguna pernah datang dan membuat pilihan — termasuk memilih untuk menghapus semua filter — default tidak lagi diterapkan. Lihat Filter.
persistSortInSession()juga menyimpan grup aktif. Tidak ada switch terpisah karena membatalkan grouping dan mengubah sort adalah jenis keputusan yang sama.- Kotak pencarian per kolom tidak pernah diingat. Fitur tersebut mempersempit view yang sudah dipersempit; memulihkannya diam-diam dapat membuat tabel kosong sulit dijelaskan.
- Session lama diabaikan, bukan diperbaiki. Mempersempit schema tidak memerlukan migration; nilai yang tidak lagi valid cukup berhenti diterapkan.
- URL tetap memiliki prioritas tertinggi. Apa pun yang dinyatakan request akan diterapkan dan disimpan; persistence hanya mengisi keadaan ketika request tidak menyatakan apa pun.