TR
Başlangıç

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

WithListing ve 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.
  • asc ve desc dışındaki bir yön asc olur.
  • 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.
Acun UIİnsanlar için tasarlandı.