| Install | |
|---|---|
composer require milenmk/laravel-livewire-crud |
|
| Latest Version: | 1.4.1 |
| PHP: | ^8.3 |
This package provides a comprehensive solution for CRUD operations within your Laravel applications, offering a streamlined and reusable approach for managing data. Whether you're working with Laravel controllers or Livewire components, this package allows you to:
This package is ideal for developers who want to focus on business logic rather than repetitive CRUD operations, saving time and minimizing errors.
Since version 1.3.0, the package also support Livewire Forms. Just move the corresponding logic (validation rules and CRUD methods from the component to the form)
Run composer require milenmk/laravel-livewire-crud to install the package
In your controller or Livewire component, include the GetSetData trait:
use Milenmk\LaravelCrud\GetSetData;
Reference the CRUD methods from the CrudClass trait, passing the model name as a parameter. Here's an example with a Client model and a Livewire component:
<?php
declare(strict_types=1);
namespace App\Livewire\Client;
use App\Livewire\CommonComponent;
use App\Models\Client;
use Milenmk\LaravelCrud\GetSetData;
final class Clients extends CommonComponent
{
use GetSetData;
// Store new client
public function storeClient(): void
{
$this->rules = [
'company' => 'required|min:3',
'country' => 'nullable',
'city' => 'nullable',
'zip' => 'nullable',
'address' => 'nullable',
'phone' => 'nullable',
'fax' => 'nullable',
'mobile' => 'nullable',
'email' => 'nullable',
];
$this->commonStoreData('Client');
}
// Edit existing client
public function editClient(int $clientId): void
{
$this->commonEditData('Client', $clientId);
}
// Update client information
public function updateClient(): void
{
$this->rules = [
'company' => 'required|min:3',
'country' => 'nullable',
'city' => 'nullable',
'zip' => 'nullable',
'address' => 'nullable',
'phone' => 'nullable',
'fax' => 'nullable',
'mobile' => 'nullable',
'email' => 'nullable',
'status' => 'required',
];
$this->commonUpdateData('Client');
}
// Delete client
public function deleteClient(int $clientId): void
{
$this->commonDeleteData('Client', $clientId);
}
// Destroy client (permanent delete)
public function destroyClient(): void
{
$this->commonDestroyData('Client');
}
// Bulk delete clients
public function bulkDestroyClients(): void
{
$this->commonBulkDestroyData('Client', $this->selectedItems);
}
/**
* Array of selected items for bulk action
*/
public array $selectedItems = [];
}
To use the Bulk Delete option, define a selectedItems property in your Livewire component, which should be an array
of selected record IDs:
/**
* Array of selected items for bulk action
*
*/
public array $selectedItems = [];
Sample Livewire component
namespace App\Livewire;
use App\Models\Test;
use Illuminate\Database\Eloquent\Collection;
use Livewire\Attributes\Computed;
use Livewire\Attributes\Locked;
use Livewire\Component;
use Throwable;
class Tests extends Component
{
public Test $model;
public TestForm $form;
public bool $addModal = false;
public bool $editModal = false;
public bool $deleteModal = false;
public string $modelName = 'Test';
#[Locked]
protected $id;
public function render()
{
return view('livewire.tests');
}
#[Computed]
public function data(): array
{
return [
['key' => 'id', 'label' => 'ID'],
['key' => 'name', 'label' => 'Name'],
['key' => 'code', 'label' => 'Code'],
['key' => 'region', 'label' => 'Region'],
];
}
#[Computed]
public function records(): array|Collection
{
return Test::all();
}
/**
* @throws Throwable
*/
public function save(): void
{
$this->form->create($this->modelName);
$this->addModal = false;
}
public function edit(Test $record): void
{
$this->form->fillEditForm(record: $record);
$this->editModal = true;
}
/**
* @throws Throwable
*/
public function update(): void
{
$this->form->update($this->modelName, $this->form->record->id);
$this->editModal = false;
}
public function delete(Test $record): void
{
$this->form->fillDeleteForm(record: $record);
$this->deleteModal = true;
}
/**
* @throws Throwable
*/
public function destroy(): void
{
$this->form->destroy($this->modelName, $this->form->record->id);
$this->deleteModal = false;
}
}
Sample blade file (for Livewire with Flux)
<div>
<flux:modal.trigger @click="$wire.addModal = true">
<flux:button variant="filled">{{ __('Add Record') }}</flux:button>
</flux:modal.trigger>
<div class="mt-6">
<table>
<thead>
<tr>
@foreach ($this->data as $column)
<th class="text-nowrap">{{ $column['label'] }}</th>
@endforeach
<th></th>
</tr>
</thead>
<tbody>
@foreach ($this->records as $record)
<tr>
@foreach ($this->data as $row)
@php
$prop = $row['key'];
@endphp
<td class="text-nowrap">{{ $record->$prop }}</td>
@endforeach
<td>
<flux:button variant="primary" wire:click="edit({{ $record->id }})">
{{ __('Edit Record') }}
</flux:button>
<flux:button variant="danger" wire:click="delete({{ $record->id }})">
{{ __('Delete Record') }}
</flux:button>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
<!-- Add Modal -->
<flux:modal wire:model.self="addModal" class="md:w-96">
<div>
<h5 class="mb-5 font-bold">Add record</h5>
</div>
<flux:spacer />
<form wire:submit="save">
<div class="space-y-6">
@foreach ($this->data as $input)
@if ($input['key'] !== 'id')
<flux:input label="{{ __($input['label']) }}" wire:model="form.{{ $input['key'] }}" />
@endif
@endforeach
<div class="flex">
<flux:spacer />
<flux:button type="submit" variant="primary">{{ __('Create') }}</flux:button>
</div>
</div>
</form>
</flux:modal>
<!-- Edit Modal -->
<flux:modal wire:model.self="editModal" class="md:w-96">
<div>
<h5 class="mb-5 font-bold">Edit record</h5>
</div>
<flux:spacer />
<form wire:submit="update()">
<div class="space-y-6">
@foreach ($this->data as $input)
@if ($input['key'] !== 'id')
<flux:input label="{{ __($input['label']) }}" wire:model="form.{{ $input['key'] }}" />
@endif
@endforeach
<div class="flex">
<flux:spacer />
<flux:button type="submit" variant="primary">{{ __('Update') }}</flux:button>
</div>
</div>
</form>
</flux:modal>
<!-- Delete Modal -->
<flux:modal wire:model.self="deleteModal" class="md:w-96">
<div>
<h5 class="text-danger mb-5 font-bold">Delete record</h5>
</div>
<flux:spacer />
@if ($this->deleteModal === true)
<p class="mb-5">
{{ __('Are you sure you want to delete this record :name?', ['name' => $this->form->record->name]) }}
</p>
@endif
<form wire:submit="destroy()">
<div class="space-y-6">
<div class="flex">
<flux:spacer />
<flux:button type="submit" variant="primary">{{ __('Delete') }}</flux:button>
</div>
</div>
</form>
</flux:modal>
</div>
Sample Form component
namespace App\Livewire;
use App\Models\Test;
use Livewire\Attributes\Validate;
use Livewire\Form;
use Milenmk\LaravelCrud\GetSetData;
use Throwable;
class TestForm extends Form
{
use GetSetData;
public ?Test $record;
#[Validate(['required', 'max:128'])]
public $name = '';
#[Validate(['required', 'max:128'])]
public $code = '';
#[Validate(['required', 'max:128'])]
public $region = '';
/**
* @throws Throwable
*/
public function create($model): void
{
$this->commonStoreData($model);
}
public function fillEditForm(Test $record): void
{
$this->record = $record;
$this->commonEditData('Test', $record->id);
}
/**
* @throws Throwable
*/
public function update($model, $recordId): void
{
$this->commonUpdateData($model, $recordId);
}
public function fillDeleteForm(Test $record): void
{
$this->record = $record;
$this->commonDeleteData('Test', $record->id);
}
/**
* @throws Throwable
*/
public function destroy($model, $recordId): void
{
$this->commonDestroyData($model, $recordId);
}
}
commonStoreData('Model') stores a new record in the database.commonEditData('Model', $recordId) retrieves data for editing a specific record by ID.commonUpdateData('Model') updates a specific record in the database.commonDeleteData('Model', $recordId) marks a specific record for deletion (soft delete).commonDestroyData('Model') permanently deletes a specific record from the database.commonBulkDestroyData('Model', $selectedItems) bulk deletes records based on an array of IDs.For regular Laravel controllers, the event() function is used for an event dispatching, as Livewire-specific methods like dispatch() are not available.
<?php
declare(strict_types=1);
namespace App\Http\Controllers;
use App\Models\Client;
use Milenmk\LaravelCrud\GetSetData;
class ClientController extends Controller
{
use GetSetData;
public function storeClient(): void
{
$this->rules = [
'company' => 'required|min:3',
'country' => 'nullable',
'city' => 'nullable',
'zip' => 'nullable',
'address' => 'nullable',
'phone' => 'nullable',
'fax' => 'nullable',
'mobile' => 'nullable',
'email' => 'nullable',
];
$this->commonStoreData('Client');
}
public function editClient(int $clientId): void
{
$this->commonEditData('Client', $clientId);
}
public function updateClient(): void
{
$this->rules = [
'company' => 'required|min:3',
'country' => 'nullable',
'city' => 'nullable',
'zip' => 'nullable',
'address' => 'nullable',
'phone' => 'nullable',
'fax' => 'nullable',
'mobile' => 'nullable',
'email' => 'nullable',
'status' => 'required',
];
$this->commonUpdateData('Client');
}
public function deleteClient(int $clientId): void
{
$this->commonDeleteData('Client', $clientId);
}
public function destroyClient(): void
{
$this->commonDestroyData('Client');
}
public function bulkDestroyClients(array $clientIds): void
{
$this->commonBulkDestroyData('Client', $clientIds);
}
}
Please see CHANGELOG.md for recent changes.
If this package saves you time, you can support ongoing development:
👉 Become a Patron
Check out my other Laravel packages:
This package is licensed under the MIT License. See the LICENSE file for more details.
This package is provided ”as is”, without warranty of any kind, either express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose, or noninfringement.
The author(s) make no representations or warranties regarding the accuracy, reliability or completeness of the code or its suitability for any specific use case. It is recommended that you thoroughly test this package in your environment before deploying it to production.
By using this package, you acknowledge and agree that the author(s) shall not be held liable for any damages, losses or other issues arising from the use of this software.