Liste sayfası
Bu sayfa, arama, filtre, sıralama ve sayfalaması olan bir kayıt listesini WithListing trait'i ve Acun bileşenleriyle elle kurmayı öğretir.
Trait arama, sıralama, sayfalama ve toplu seçimi hazır getirir; tabloyu ve filtreleri siz yazarsınız. Sütunları ve filtreleri PHP'de tanımlayan hazır bir tablo istiyorsanız DataTable kılavuzuna bakın.
Not
WithListingve arama PRO paketindedir; Acun temalarında kurulu gelir. Bu sayfadaki bileşenlerin hepsi (acun:table,acun:sortable-th,acun:search,acun:selection-bar…) ücretsizdir. Onları kendi sorgunuzla da kullanabilirsiniz.
En kısa örnek
app/Livewire/Customers/Index.php:
namespace App\Livewire\Customers;
use Acun\Ui\Livewire\Concerns\WithListing;
use Acun\Ui\Search\SearchField;
use Acun\Ui\Search\SearchMode;
use App\Models\Customer;
use Illuminate\Database\Eloquent\Builder;
use Livewire\Attributes\Layout;
use Livewire\Attributes\Title;
use Livewire\Component;
#[Layout('layouts.admin.vertical')]
#[Title('Müşteriler')]
final class Index extends Component
{
use WithListing;
// Liste ilk açıldığında bu sütuna göre sıralanır.
protected function defaultSortField(): string
{
return 'name';
}
// Kullanıcının sıralayabileceği sütunlar.
protected function sortableFields(): array
{
return ['name', 'email', 'created_at'];
}
// Arama: adın içinde geçen sözcük, e-postanın başı.
protected function searchFields(): array
{
return [
SearchField::make('name', SearchMode::Contains),
SearchField::make('email'),
];
}
// Açık sayfadaki satırların kimlikleri (toplu seçim bunu kullanır).
protected function currentPageKeys(): array
{
return $this->query()->forPage($this->getPage(), $this->perPage)->pluck('id')->all();
}
private function query(): Builder
{
return $this->applySearch(Customer::query())
->orderBy($this->sortField, $this->sortDirection);
}
public function render()
{
$this->fillPageSelection();
return view('admin.customers.index', ['customers' => $this->query()->paginate($this->perPage)]);
}
}
resources/views/admin/customers/index.blade.php:
<section class="mx-auto w-full max-w-7xl space-y-6">
<acun:page-header :title="__('Müşteriler')" />
<acun:list-toolbar>
<acun:slot:start><acun:per-page-select :options="$this->pageSizeOptions()" /></acun:slot:start>
<acun:search wire:model.live.debounce.300ms="search" placeholder="Ad veya e-posta ara" />
</acun:list-toolbar>
<acun:table>
<table class="ui-table">
<thead>
<tr>
<acun:sortable-th field="name" :sort-field="$sortField" :sort-direction="$sortDirection">Ad</acun:sortable-th>
<acun:sortable-th field="email" :sort-field="$sortField" :sort-direction="$sortDirection">E-posta</acun:sortable-th>
</tr>
</thead>
<tbody>
@foreach ($customers as $customer)
<tr wire:key="customer-{{ $customer->id }}">
<td>{{ $customer->name }}</td>
<td>{{ $customer->email }}</td>
</tr>
@endforeach
</tbody>
</table>
</acun:table>
{{ $customers->links() }}
</section>
Arama, veritabanının ürettiği name_search ve email_search sütunlarında çalışır. Bu sütunları bir migration'da ekleyin:
Schema::table('customers', function (Blueprint $table) {
$table->searchColumn('name', index: false); // içinde aranır; indeks işe yaramaz
$table->searchColumn('email'); // baştan aranır; indeksli
});
Artık arama kutusuna yazdıkça liste daralır, başlığa tıklayınca sıralanır. Türkçe harf ve büyük/küçük harf farkı yoktur: "ayse" yazınca "Ayşe" bulunur. Arama metni ve sıralama adres çubuğunda ?q=, ?sort= ve ?direction= olarak saklanır. Rotayı ve menü kaydını İlk sayfanız kılavuzundaki gibi ekleyin.
Sık kullanılan seçenekler
Bileşeninize yazdığınız metotlar:
| Metot | Ne yapar | Örnek |
|---|---|---|
defaultSortField() |
Varsayılan sıralama sütunu (zorunlu) | 'name' |
currentPageKeys() |
Açık sayfadaki satırların kimlikleri (zorunlu) | yukarıdaki örnek |
sortableFields() |
Sıralanabilecek sütunlar; varsayılan yalnızca defaultSortField() |
['name', 'email'] |
perPageOptions() |
Seçilebilecek sayfa boyutları; varsayılan [10, 25, 50, 100] |
[10, 25, 50] |
searchFields() |
Aranacak alanlar; bkz. Arama | [SearchField::make('email')] |
Filtre eklemek
Filtre, bileşeninizin kendi özelliğidir. #[Url] ile adres çubuğunda saklanır. Değişince sayfayı başa alın ve seçimi temizleyin:
use Livewire\Attributes\Url;
#[Url(history: true)]
public string $status = '';
public function updated(string $property): void
{
if ($property === 'status') {
$this->resetPage();
$this->clearSelection();
}
}
public function activeFilterCount(): int
{
return $this->status !== '' ? 1 : 0;
}
Filtreyi sorguda uygulayın: ->when($this->status !== '', fn (Builder $q) => $q->where('status', $this->status)). Filtre alanlarını bir çekmeceye (drawer) koyun; acun:filter-button çekmeceyi açar ve etkin filtre sayısını gösterir:
<acun:list-toolbar>
<acun:search wire:model.live.debounce.300ms="search" placeholder="Ad veya e-posta ara" />
<acun:slot:actions>
<acun:filter-button drawer="customer-filters" :count="$this->activeFilterCount()" />
</acun:slot:actions>
</acun:list-toolbar>
<acun:drawer name="customer-filters" docked title="Filtreler" compact>
<acun:select wire:model.live="status" label="Durum" :options="['' => 'Tümü', 'active' => 'Aktif', 'passive' => 'Pasif']" />
</acun:drawer>
Toplu işlem eklemek
Başlığa ve her satıra birer onay kutusu ekleyin. Seçim çubuğunu tablo ve sayfalamadan sonra yerleştirin:
{{-- <thead> satırının ilk hücresi --}}
<th class="w-10"><acun:checkbox wire:model.live="pageSelected" :checked="$pageSelected" aria-label="Tümünü seç" /></th>
{{-- @foreach içindeki her satırın ilk hücresi --}}
<td><acun:checkbox wire:model.live="selected" :value="$customer->id" :id="'customer-'.$customer->id" aria-label="Seç" /></td>
{{-- {{ $customers->links() }} satırından sonra --}}
<acun:selection-bar :count="$this->selectionCount($customers->total())" :all-matching="$allMatchingSelected" :total="$this->selectionCount($customers->total())">
<acun:button variant="soft-danger" size="sm" wire:click="bulkArchive">Arşivle</acun:button>
</acun:selection-bar>
Toplu işlemde seçimi her zaman sayfanın kendi sorgusuna applySelection() ile uygulayın:
public function bulkArchive(): void
{
$this->query()
->tap(fn (Builder $q) => $this->applySelection($q))
->update(['archived' => true]);
$this->clearSelection();
}
Başlıktaki onay kutusu aramaya ve filtrelere uyan tüm kayıtları, bütün sayfalarda seçer. Bu durumda işareti kaldırılan satır $excluded listesine girer; toplu işlem o satıra dokunmaz. Sayfa değiştirmek ya da sayfa boyutunu değiştirmek seçimi silmez. Arama ya da filtre değişince seçim temizlenir. Çubuktaki "Seçimi temizle" düğmesi clearSelection() çağırır.
Güvenlik
orderBy($this->sortField, $this->sortDirection)güvenle kullanılabilir. Trait sıralama ve sayfa boyutu değerlerini her istekte izin verilen listelerle denetler.- Toplu işlemde seçili kimlikleri
whereKey($this->selected)ile doğrudan kullanmayın.applySelection()ile sayfanın sorgusuna uygulayın. Böylece kullanıcının göremediği kayıtlara dokunulmaz; diğer sayfalar ve işareti kaldırılan satırlar da hesaba katılır.
İleri düzey
Trait'in özellikleri ve metotları
| Özellik / metot | Ne yapar |
|---|---|
$search |
Arama metni; adres çubuğunda ?q= |
$sortField, $sortDirection |
Sıralama; adres çubuğunda ?sort= ve ?direction= |
$perPage |
Sayfa boyutu; varsayılan 25 |
$selected, $pageSelected |
Seçili kayıtlar ve başlıktaki onay kutusu |
$allMatchingSelected, $excluded |
Filtreye uyan tümü seçili mi; o sırada işareti kaldırılanlar |
applySelection($query) |
Toplu işlem sorgusunu seçime daraltır |
selectionCount($total) |
Seçili kayıt sayısı |
sortBy($field) |
Sıralamayı değiştirir; acun:sortable-th bunu çağırır |
clearSelection() |
Seçimi temizler; arama değişince kendiliğinden çağrılır |
applySearch($query, $term = null, $fields = null) |
$search metnini searchFields() alanlarında arar |
searchDriver() |
Arama sürücüsü; varsayılan acun-ui.search.driver |
pageSizeOptions() |
Görünüm için sayfa boyutları; perPageOptions() değerinden gelir |
fillPageSelection() |
"Tümü seçili"yken yeni sayfanın satırlarını seçime ekler |
fillPageSelection() metodunu render() başında çağırın. Tek dosyalı bileşenlerde with() başında çağırın.
Değerler nasıl denetlenir
$sortField, $sortDirection ve $perPage tarayıcıdan değiştirilebilir. Trait bunları her istekte düzeltir:
sortableFields()dışındaki bir sütun varsayılan sütuna döner.ascvedescdışındaki bir yönascolur.perPageOptions()dışındaki bir boyut listedeki ilk değere döner.
mount() içinde $perPage değerine verdiğiniz sayı da perPageOptions() listesinde olmalıdır. Aşağıdaki örnek sayfa boyutunu 10 yapar. Adres çubuğunda ?direction= yoksa listeyi azalan sırayla açar:
public function mount(): void
{
$this->perPage = 10;
if (! request()->query->has('direction')) {
$this->sortDirection = 'desc';
}
}
İkinci bir arama kutusu
applySearch() başka bir terim ve alan listesi de alır. Ayrıntılar ve arama sütunları için Arama kılavuzuna bakın.
Daha fazlası
- Tam, çalışan örnek: demo uygulamasında Tablolar › DataTable › Sunucu taraflı (Ajax) sayfasının "WithListing ile elle" sekmesi.
- DataTable: sütunları PHP'de tanımlanan hazır tablo.
- DataTable örnekleri
- Arama: arama sütunları, modlar ve büyük tablolar.
- Form penceresi: listeden kayıt eklemek ve düzenlemek.