- Creating Columns
- Relationships
- Available Methods
- Other Column Types
- Column Selection
- Secondary Header
- Footer
Getting Started
Usage
DataTable
Columns
Rows
Sorting
Pagination
Search
Bulk Actions
Filters
Reordering
Secondary Header
Footer
Examples
Misc.
Sponsored
Advanced Usage
Examples
🎉 Enjoying this package? Consider sponsoring me on GitHub or buying me a beer.
This is the documentation for v2 but the latest version is v3. You can switch versions in the menu on the left/at the top. Check your current version with the following command:
composer show rappasoft/laravel-livewire-tables
Advanced Example
1<?php 2 3namespace App\Http\Livewire; 4 5use App\Models\Tag; 6use App\Models\User; 7use Illuminate\Database\Eloquent\Builder; 8use Rappasoft\LaravelLivewireTables\DataTableComponent; 9use Rappasoft\LaravelLivewireTables\Views\Columns\BooleanColumn; 10use Rappasoft\LaravelLivewireTables\Views\Column; 11use Rappasoft\LaravelLivewireTables\Views\Columns\ImageColumn; 12use Rappasoft\LaravelLivewireTables\Views\Filters\SelectFilter; 13use Rappasoft\LaravelLivewireTables\Views\Filters\MultiSelectFilter; 14use Rappasoft\LaravelLivewireTables\Views\Filters\DateFilter; 15 16class UsersTable extends DataTableComponent 17{ 18 public string $tableName = 'users'; 19 public array $users = []; 20 21 public $columnSearch = [ 22 'name' => null, 23 'email' => null, 24 ]; 25 26 public function configure(): void 27 { 28 $this->setPrimaryKey('id') 29 ->setReorderEnabled() 30 ->setSingleSortingDisabled() 31 ->setHideReorderColumnUnlessReorderingEnabled() 32 ->setFilterLayoutSlideDown() 33 ->setRememberColumnSelectionDisabled() 34 ->setSecondaryHeaderTrAttributes(function($rows) { 35 return ['class' => 'bg-gray-100']; 36 }) 37 ->setSecondaryHeaderTdAttributes(function(Column $column, $rows) { 38 if ($column->isField('id')) { 39 return ['class' => 'text-red-500']; 40 } 41 42 return ['default' => true]; 43 }) 44 ->setFooterTrAttributes(function($rows) { 45 return ['class' => 'bg-gray-100']; 46 }) 47 ->setFooterTdAttributes(function(Column $column, $rows) { 48 if ($column->isField('name')) { 49 return ['class' => 'text-green-500']; 50 } 51 52 return ['default' => true]; 53 }) 54 ->setUseHeaderAsFooterEnabled() 55 ->setHideBulkActionsWhenEmptyEnabled(); 56 } 57 58 public function columns(): array 59 { 60 return [ 61 ImageColumn::make('Avatar') 62 ->location(function($row) { 63 return asset('img/logo-'.$row->id.'.png'); 64 }) 65 ->attributes(function($row) { 66 return [ 67 'class' => 'w-8 h-8 rounded-full', 68 ]; 69 }), 70 Column::make('Order', 'sort') 71 ->sortable() 72 ->collapseOnMobile() 73 ->excludeFromColumnSelect(), 74 Column::make('ID', 'id') 75 ->sortable() 76 ->setSortingPillTitle('Key') 77 ->setSortingPillDirections('0-9', '9-0') 78 ->secondaryHeader(function($rows) { 79 return $rows->sum('id'); 80 }) 81 ->html(), 82 Column::make('Name') 83 ->sortable() 84 ->searchable() 85 ->view('tables.cells.actions') 86 ->secondaryHeader(function() { 87 return view('tables.cells.input-search', ['field' => 'name', 'columnSearch' => $this->columnSearch]); 88 }) 89 ->html(), 90 Column::make('E-mail', 'email') 91 ->sortable() 92 ->searchable() 93 ->secondaryHeader(function() { 94 return view('tables.cells.input-search', ['field' => 'email', 'columnSearch' => $this->columnSearch]); 95 }), 96 Column::make('Address', 'address.address') 97 ->sortable() 98 ->searchable() 99 ->collapseOnTablet(),100 Column::make('Address Group', 'address.group.name')101 ->sortable()102 ->searchable()103 ->collapseOnTablet(),104 Column::make('Group City', 'address.group.city.name')105 ->sortable()106 ->searchable()107 ->collapseOnTablet(),108 BooleanColumn::make('Active')109 ->sortable()110 ->collapseOnMobile(),111 Column::make('Verified', 'email_verified_at')112 ->sortable()113 ->collapseOnTablet(),114 Column::make('Tags')115 ->label(fn($row) => $row->tags->pluck('name')->implode(', '))116 ];117 }118 119 public function filters(): array120 {121 return [122 MultiSelectFilter::make('Tags')123 ->options(124 Tag::query()125 ->orderBy('name')126 ->get()127 ->keyBy('id')128 ->map(fn($tag) => $tag->name)129 ->toArray()130 )->filter(function(Builder $builder, array $values) {131 $builder->whereHas('tags', fn($query) => $query->whereIn('tags.id', $values));132 })133 ->setFilterPillValues([134 '3' => 'Tag 1',135 ]),136 SelectFilter::make('E-mail Verified', 'email_verified_at')137 ->setFilterPillTitle('Verified')138 ->options([139 '' => 'Any',140 'yes' => 'Yes',141 'no' => 'No',142 ])143 ->filter(function(Builder $builder, string $value) {144 if ($value === 'yes') {145 $builder->whereNotNull('email_verified_at');146 } elseif ($value === 'no') {147 $builder->whereNull('email_verified_at');148 }149 }),150 SelectFilter::make('Active')151 ->setFilterPillTitle('User Status')152 ->setFilterPillValues([153 '1' => 'Active',154 '0' => 'Inactive',155 ])156 ->options([157 '' => 'All',158 '1' => 'Yes',159 '0' => 'No',160 ])161 ->filter(function(Builder $builder, string $value) {162 if ($value === '1') {163 $builder->where('active', true);164 } elseif ($value === '0') {165 $builder->where('active', false);166 }167 }),168 SelectFilter::make('Address Group')169 ->options([170 '' => 'All',171 AddressGroup::query()172 ->orderBy('type')173 ->get()174 ->groupBy('type')175 ->map(fn ($addressGroup) => $addressGroup->pluck('type', 'id')->filter())176 ->toArray(),177 ])178 ->filter(function(Builder $builder, string $value) {179 $builder->where('address_groups.type', $value);180 }),181 DateFilter::make('Verified From')182 ->config([183 'min' => '2020-01-01',184 'max' => '2021-12-31',185 ])186 ->filter(function(Builder $builder, string $value) {187 $builder->where('email_verified_at', '>=', $value);188 }),189 DateFilter::make('Verified To')190 ->filter(function(Builder $builder, string $value) {191 $builder->where('email_verified_at', '<=', $value);192 }),193 ];194 }195 196 public function builder(): Builder197 {198 return User::query()199 ->when($this->columnSearch['name'] ?? null, fn ($query, $name) => $query->where('users.name', 'like', '%' . $name . '%'))200 ->when($this->columnSearch['email'] ?? null, fn ($query, $email) => $query->where('users.email', 'like', '%' . $email . '%'));201 }202 203 public function bulkActions(): array204 {205 return [206 'activate' => 'Activate',207 'deactivate' => 'Deactivate',208 ];209 }210 211 public function activate()212 {213 User::whereIn('id', $this->getSelected())->update(['active' => true]);214 215 $this->clearSelected();216 }217 218 public function deactivate()219 {220 User::whereIn('id', $this->getSelected())->update(['active' => false]);221 222 $this->clearSelected();223 }224 225 public function reorder($items): void226 {227 foreach ($items as $item) {228 User::find((int)$item['value'])->update(['sort' => (int)$item['order']]);229 }230 }231}
input-search.blade.php
1@if (config('livewire-tables.theme') === 'tailwind') 2 <div class="flex rounded-md shadow-sm"> 3 <input 4 wire:model.debounce="columnSearch.{{ $field }}" 5 placeholder="Search {{ ucfirst($field) }}" 6 type="text" 7 class="block w-full border-gray-300 rounded-md shadow-sm transition duration-150 ease-in-out sm:text-sm sm:leading-5 dark:bg-gray-800 dark:text-white dark:border-gray-600 @if (isset($columnSearch[$field]) && strlen($columnSearch[$field])) rounded-none rounded-l-md focus:ring-0 focus:border-gray-300 @else focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50 rounded-md @endif" 8 /> 9 10 @if (isset($columnSearch[$field]) && strlen($columnSearch[$field]))11 <span wire:click="$set('columnSearch.{{ $field }}', null)" class="inline-flex items-center px-3 text-gray-500 border border-l-0 border-gray-300 cursor-pointer bg-gray-50 rounded-r-md sm:text-sm dark:bg-gray-700 dark:text-white dark:border-gray-600 dark:hover:bg-gray-600">12 <svg xmlns="http://www.w3.org/2000/svg" class="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">13 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />14 </svg>15 </span>16 @endif17 </div>18@endif19 20@if (config('livewire-tables.theme') === 'bootstrap-4')21 <div class="mb-3 mb-md-0 input-group">22 <input23 wire:model.debounce="columnSearch.{{ $field }}"24 placeholder="Search {{ ucfirst($field) }}"25 type="text"26 class="form-control"27 >28 29 @if (isset($columnSearch[$field]) && strlen($columnSearch[$field]))30 <div class="input-group-append">31 <button wire:click="$set('columnSearch.{{ $field }}', null)" class="btn btn-outline-secondary" type="button">32 <svg style="width:.75em;height:.75em" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">33 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />34 </svg>35 </button>36 </div>37 @endif38 </div>39@endif40 41@if (config('livewire-tables.theme') === 'bootstrap-5')42 <div class="mb-3 mb-md-0 input-group">43 <input44 wire:model.debounce="columnSearch.{{ $field }}"45 placeholder="Search {{ ucfirst($field) }}"46 type="text"47 class="form-control"48 >49 50 @if (isset($columnSearch[$field]) && strlen($columnSearch[$field]))51 <button wire:click="$set('columnSearch.{{ $field }}', null)" class="btn btn-outline-secondary" type="button">52 <svg style="width:.75em;height:.75em" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">53 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />54 </svg>55 </button>56 @endif57 </div>58@endif