Skip to content

Instantly share code, notes, and snippets.

@jacksleight
Last active December 2, 2024 11:55
Show Gist options
  • Save jacksleight/6ebb1b4a5c5d67b342fde75b67a898dd to your computer and use it in GitHub Desktop.
Save jacksleight/6ebb1b4a5c5d67b342fde75b67a898dd to your computer and use it in GitHub Desktop.
Volt + Flux single file CRUD
<?php
use App\Models\Tag;
use Livewire\Attributes\Locked;
use Livewire\Attributes\Url;
use Livewire\Attributes\Validate;
use Livewire\Volt\Component;
use Livewire\WithPagination;
new class extends Component
{
use WithPagination;
#[Url(except: '')]
public $search;
#[Url(except: 'name')]
public $sortBy = 'name';
#[Url(except: 'asc')]
public $sortDir = 'asc';
#[Locked]
public ?Tag $model;
#[Validate('required')]
public $name;
public function mount()
{
$this->authorize('viewAny', Tag::class);
}
public function sort($column)
{
if ($this->sortBy === $column) {
$this->sortDir = $this->sortDir === 'asc' ? 'desc' : 'asc';
} else {
$this->sortBy = $column;
$this->sortDir = 'asc';
}
}
public function load(Tag $model)
{
$this->authorize('view', $model);
$this->model = $model;
$this->fill($this->model);
$this->modal('form')->show();
}
public function save()
{
$values = $this->validate();
if ($this->model->exists) {
$this->authorize('update', $this->model);
$this->model->update($values);
} else {
$this->authorize('create', Tag::class);
$this->model = Tag::create($values);
}
$this->modal('form')->close();
Flux::toast('Tag saved', variant: 'success');
}
public function confirm(Tag $model)
{
$this->model = $model;
$this->modal('confirm')->show();
}
public function delete()
{
$this->authorize('delete', $this->model);
$this->model->delete();
$this->modal('confirm')->close();
Flux::toast('Tag deleted', variant: 'success');
}
public function clear()
{
$this->reset();
$this->resetValidation();
}
public function with()
{
return [
'tags' => Tag::query()
->when($this->search, function ($query) {
$query->where('name', 'like', "%{$this->search}%");
})
->orderBy($this->sortBy, $this->sortDir)
->paginate(10),
];
}
}; ?>
<div>
<div class="flex gap-2 justify-between">
<flux:heading size="xl" level="1">
Tags
</flux:heading>
<div class="flex gap-4">
<flux:input wire:model.live.debounce.250ms="search" placeholder="Search…" />
<flux:button wire:click="load" icon="plus">Add</flux:button>
</div>
</div>
<flux:table :paginate="$tags">
<flux:columns>
<flux:column
sortable :sorted="$sortBy === 'name'" :direction="$sortDir"
wire:click="sort('name')">Name</flux:column>
<flux:column></flux:column>
</flux:columns>
<flux:rows>
@foreach ($tags as $tag)
<flux:row>
<flux:cell>{{ $tag->name }}</flux:cell>
<flux:cell class="text-right">
<flux:button
variant="ghost" size="sm"
wire:click="load({{ $tag->id }})">Edit</flux:button>
<flux:button
variant="ghost" size="sm"
wire:click="confirm({{ $tag->id }})">Delete</flux:button>
</flux:cell>
</flux:row>
@endforeach
</flux:rows>
</flux:table>
<flux:modal name="form" wire:close="clear" :dismissible="false" class="md:w-96">
<form wire:submit="save" class="space-y-6" autocomplete="off">
<flux:heading size="lg" level="2">
{{ $model?->exists ? 'Edit' : 'Add' }} Tag
</flux:heading>
<flux:input wire:model="name" label="Name" />
<div class="flex gap-2 justify-end">
<flux:button type="submit" variant="primary">Save</flux:button>
</div>
</form>
</flux:modal>
<flux:modal name="confirm" wire:close="clear" class="md:w-96">
<form wire:submit="delete" class="space-y-6" autocomplete="off">
<flux:heading size="lg" level="2">Are you sure?</flux:heading>
<div class="flex gap-2 justify-end">
<flux:modal.close>
<flux:button variant="ghost">Cancel</flux:button>
</flux:modal.close>
<flux:button type="submit" variant="danger">Delete</flux:button>
</div>
</form>
</flux:modal>
</div>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment