Pengurutan
Sebuah kolom menjadi sortable ketika kolom tersebut mendeklarasikannya, lalu header dapat digunakan untuk mengubah arah pengurutan. Sorting dilakukan di server dan dikendalikan melalui URL: query string membawa nama kolom dan arah, sementara schema menentukan kolom mana yang boleh digunakan. Gunakan bagian di bawah ketika pengurutan bukan sekadar kolom biasa — misalnya CASE berdasarkan status, nilai pada related record, atau default ordering ketika tabel pertama kali dibuka.
Contoh minimal
<?php
declare(strict_types=1);
namespace App\Panels\Admin\Resources\Posts\Tables;
use PandaPanel\Tables\Columns\DateTimeColumn;
use PandaPanel\Tables\Columns\TextColumn;
use PandaPanel\Tables\Enums\SortDirection;
use PandaPanel\Tables\TableSchema;
final class PostsTable
{
public static function configure(TableSchema $table): TableSchema
{
return $table
->columns([
TextColumn::make('title')->sortable(),
DateTimeColumn::make('created_at')->label('Published')->sortable(),
])
->defaultSort('created_at', SortDirection::Descending);
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Kedua header sekarang dapat diklik dan tabel terbuka dengan record terbaru terlebih dahulu.
Mendeklarasikan kolom sortable
use PandaPanel\Tables\Columns\Column;
Column::sortable(bool $sortable = true, ?string $column = null): static
Column::isSortable(): bool
Column::getSortColumn(): string // $column ?? $name2
3
4
5
| Argumen | Tipe | Default | Arti |
|---|---|---|---|
$sortable | bool | true | apakah header menawarkan pengurutan |
$column | string|null | null | kolom database yang digunakan untuk order by jika berbeda dari nama atribut |
use PandaPanel\Tables\Columns\TextColumn;
TextColumn::make('author')->sortable(column: 'author_last_name'),
// Explicitly not sortable, for a column a shared configuration made sortable.
TextColumn::make('summary')->sortable(false),2
3
4
5
6
sortable() adalah deklarasi, bukan implementasi ordering. TableQuery membacanya sebagai whitelist. ?sort=password terhadap kolom yang tidak sortable — atau terhadap nama yang sama sekali bukan kolom — diabaikan dan state.sort yang dikembalikan bernilai null, sehingga header tidak pernah terlihat seolah-olah diurutkan berdasarkan sesuatu yang sebenarnya tidak diterapkan query.
Arah pengurutan
use PandaPanel\Tables\Enums\SortDirection;
SortDirection::Ascending; // 'asc'
SortDirection::Descending; // 'desc'
SortDirection::fromRequest(mixed $value): self; // anything unrecognised → Ascending
SortDirection::opposite(): self;2
3
4
5
6
fromRequest() mengubah string menjadi lowercase dan fallback ke Ascending untuk nilai lain. Karena itu ?direction=DESC tetap bekerja, sedangkan ?direction=nonsense tidak pernah diteruskan ke query builder.
Urutan default
use PandaPanel\Tables\Enums\SortDirection;
use PandaPanel\Tables\TableSchema;
TableSchema::defaultSort(string $column, SortDirection $direction = SortDirection::Descending): self
TableSchema::defaultSortOptionLabel(string $label): self
TableSchema::getDefaultSortColumn(): ?string
TableSchema::getDefaultSortDirection(): SortDirection2
3
4
5
6
7
$table
->defaultSort('created_at', SortDirection::Descending)
->defaultSortOptionLabel('Newest first');2
3
defaultSortOptionLabel() memberi nama pada ordering tersebut untuk UI yang menampilkan daftar opsi sort. Nilainya dikirim sebagai defaultSort.label dan bernilai null jika tidak diset.
Ketika request tidak menyebut kolom sortable, tabel menggunakan default sort. Jika default juga tidak dideklarasikan, tabel mengurutkan berdasarkan primary key model menggunakan default direction schema, yaitu SortDirection::Descending kecuali defaultSort() mengubahnya. Default eksplisit lebih dapat diandalkan daripada insertion order implisit, yang tidak stabil di berbagai database.
$schema->toArray()['defaultSort'];
// ['column' => 'created_at', 'direction' => 'desc', 'label' => 'Newest first']
// null when no default sort was declared2
3
defaultSort() diperiksa terhadap kolom yang tersedia pada saat serialization, bukan ketika setter dipanggil. Ini memungkinkan defaultSort() dipanggil sebelum columns(), ketika belum ada kolom untuk divalidasi. Default yang menunjuk kolom yang tidak dideklarasikan schema melempar PanelSchemaException::unknownDefaultSort() beserta daftar kolom yang tersedia. Ini lebih aman daripada tabel yang diam-diam fallback ke natural order sementara deklarasi terlihat seolah-olah diterapkan.
Custom ordering
use PandaPanel\Tables\Columns\Column;
Column::sortUsing(Closure(Builder, SortDirection): void $callback): static
Column::hasCustomSort(): bool
Column::applyCustomSort(Builder $query, SortDirection $direction): void2
3
4
5
Gunakan untuk ordering yang tidak dapat dinyatakan hanya dengan nama kolom — misalnya CASE berdasarkan status, JSON path, atau computed distance:
use Illuminate\Database\Eloquent\Builder;
use PandaPanel\Tables\Columns\TextColumn;
use PandaPanel\Tables\Enums\SortDirection;
TextColumn::make('status')->sortUsing(
static fn (Builder $query, SortDirection $direction) => $query
->orderByRaw("field(status, 'urgent', 'open', 'closed') {$direction->value}"),
),
TextColumn::make('attention')
->label('Needs attention')
->sortUsing(static function (Builder $query, SortDirection $direction): void {
$query
->orderByRaw('email_verified_at is null '.$direction->value)
->orderBy('created_at');
}),2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
sortUsing() otomatis mengaktifkan sortable, sehingga tidak perlu memanggil sortable() lagi. Closure mengambil alih seluruh strategi ordering untuk kolom tersebut: jika custom sort tersedia, getSortColumn() maupun relation sort tidak digunakan. Direction yang diterima closure sudah tervalidasi terhadap enum, sehingga interpolasi $direction->value aman; keamanan bagian string lainnya tetap menjadi tanggung jawab Anda.
Mengurutkan berdasarkan related column
use PandaPanel\Tables\Columns\Column;
Column::sortableByRelation(string $relation, string $column): static
Column::getSortRelation(): ?string2
3
4
use PandaPanel\Tables\Columns\TextColumn;
TextColumn::make('author_name')->label('Author')->sortableByRelation('author', 'name'),2
3
Ini membuat kolom sortable dan menggunakan correlated subquery alih-alih join. Join terhadap to-many relation dapat menggandakan row dan diam-diam merusak page size serta total. Aggregate column tidak memerlukan strategi ini — posts_count merupakan kolom nyata pada result set, sehingga cukup menggunakan ORDER BY biasa. Lihat Kolom relasi.
Cara urutan query disusun
TableQuery::applySort() berjalan dalam urutan tetap, dan hanya satu strategi sort digunakan untuk kolom yang dipilih:
- Group aktif, jika ada, diurutkan terlebih dahulu agar row dari grup yang sama tetap berdekatan.
- Jika kolom yang diminta memiliki custom sort,
applyCustomSort()dijalankan dan strategi lain tidak digunakan. - Jika tidak, tetapi kolom memiliki sort relation,
applyRelationshipSort()dijalankan dan strategi lain tidak digunakan. - Jika keduanya tidak ada, digunakan
orderBy(getSortColumn(), $direction). - Jika tidak ada sortable column yang diminta, digunakan
orderBy($defaultSortColumn ?? $model->getKeyName(), $defaultSortDirection).
State di URL
?sort=created_at&direction=descState pada relation table diberi namespace agar beberapa tabel pada record page yang sama dapat diurutkan secara independen:
?relations[posts][sort]=title&relations[posts][direction]=ascNilai yang benar-benar diterapkan dikembalikan melalui state:
use PandaPanel\Tables\TableQuery;
$state = (new TableQuery($schema, $request))->state();
$state['sort']; // 'created_at', or null when the request named nothing sortable
$state['direction']; // 'asc' | 'desc'2
3
4
5
6
Mengingat urutan
use PandaPanel\Tables\TableSchema;
TableSchema::persistSortInSession(bool $persist = true): self // off by default
TableSchema::persistsSortInSession(): bool2
3
4
Fitur ini mengingat sort, direction, dan group aktif — ketiganya diperlakukan sebagai satu keputusan tentang bagaimana tabel disusun. Request menang ketika menyatakan nilai apa pun, termasuk nilai kosong, dan kolom yang dipulihkan dari session tetap melewati whitelist yang sama. Dengan begitu session lama yang menyimpan kolom yang sudah dihapus dari schema akan diabaikan sama seperti input URL manual. Lihat State yang dipertahankan.
Sorting pada array table
PandaPanel\Tables\ArrayTableData melakukan sorting di PHP menggunakan Collection::sortBy() atas Column::getSortColumn() dan menerapkan whitelist yang sama: kolom yang tidak dikenal atau tidak sortable diabaikan. sortUsing() dan sortableByRelation() merupakan strategi query dan tidak berlaku di sini.
use PandaPanel\Tables\ArrayTableData;
$data = ArrayTableData::make($schema, $records, $request);
$data->sortableColumns(); // list<Column> this data source can honour2
3
4
5
Lihat Tabel data array.
Catatan
- Reordering menetapkan default sort.
TableSchema::reorderable('position')juga memanggildefaultSort('position', SortDirection::Ascending): urutan yang diatur pengguna hanya bermakna ketika tabel menampilkan urutan tersebut. Lihat Reordering. - Group selalu diurutkan sebelum strategi lain. Dengan group aktif, tabel diurutkan berdasarkan group column terlebih dahulu lalu kolom pilihan pengguna. Ini menjaga row dalam satu band tetap contiguous. Lihat Grouping.
sortable()setelah aggregate me-reset sort column.sortable(bool, ?string $column = null)mengisi$columntanpa kondisi, sehingga->counts('posts')->sortable()kembali menggunakan nama kolom itu sendiri. Namai kolomposts_count, atau gunakansortable(column: 'posts_count').- Nama kolom bertitik dibiarkan apa adanya.
TextColumn::make('author.name')->sortable()akan mencoba mengurutkan kolom lokal yang tidak ada; gunakansortableByRelation(). - Summary tidak meninggalkan
order by. Summary meng-clone query lalu memanggilreorder()sebelum agregasi, sehingga perhitungan summary tidak dapat mengubah ordering yang baru saja digunakan paginator. Lihat Summary. - Mengurutkan kolom tanpa index adalah penyebab umum list lambat. Framework akan menjalankan ordering apa pun yang dideklarasikan schema; apakah database mampu melakukannya dengan cepat adalah tanggung jawab desain database.