Crud Package
CRUD Package Packagist
Prerequisites
Zet de “minimum-stability” in je composer.json op “dev” om de package te kunnen installeren.
{ "minimum-stability": "dev"}Installation
composer require justin0122/crudConfiguration
<?php
return [ App\Providers\AppServiceProvider::class, App\Providers\FortifyServiceProvider::class, App\Providers\JetstreamServiceProvider::class, \Justin0122\Crud\CrudServiceProvider::class,];Livewire directory
Maak een Livewire directory aan in de app directory.
# voor linux:mkdir app/Livewire# voor windows:md app/LivewireUsage
php artisan crud:make PostGenereert de volgende bestanden:
-
app/Livewire/Post.php
-
app/Livewire/Posts.php
-
app/Models/Post.php
-
database/migrations/2021_01_01_000000_create_posts_table.php
-
resources/views/livewire/posts/index.blade.php
-
resources/views/livewire/crud/create.blade.php
-
resources/views/livewire/crud/edit.blade.php
Na het genereren van de bestanden
Model Fillables
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;use Illuminate\Database\Eloquent\Model;use Illuminate\Database\Eloquent\SoftDeletes;
class Post extends Model{ use HasFactory; use SoftDeletes;
protected $fillable = [ 'title', 'content' ];}Migration
Schema::create('posts', function (Blueprint $table) { $table->id(); $table->string('title'); $table->text('content'); $table->timestamps(); $table->softDeletes();});Table
@props(['results', 'type', 'create' => false, 'fillables' => null, 'crud' => true, 'warning' => null])<div class="mx-4"> @if($warning) <div class="rounded p-1.5 bg-red-200"> <span class="font-bold">{{$warning}}</span> </div> @endif <table class="table-auto w-full divide-y divide-gray-200 dark:divide-gray-700 dark:bg-gray-800 dark:text-gray-200"> <thead> <tr> @foreach($fillables ? $fillables : $results[0]->getFillable() as $fillable) <th class="px-4 py-2 text-left">{{ ucwords(join(' ', preg_split('/(?=[A-Z])/', $fillable))) }}</th> @endforeach <th class="px-4 py-2 text-left">Created At</th> <th class="px-4 py-2 text-left">Updated At</th> <th class="px-4 py-2 text-left">Deleted At</th> <th class="px-4 py-2 text-left">Actions</th> </tr> </thead> <tbody> @if ($create) @can('crud')
<tr class="hover:bg-gray-100 dark:hover:bg-gray-700"> <form wire:submit.prevent="create"> @foreach($fillables ? $fillables : $results[0]->getFillable() as $fillable) <td class="py-2 px-4"> <label for="{{ $results[0]->$fillable ?? $fillable }}"></label> @if (str_contains($fillable, 'date') !== false) <x-input type="date" class="w-full rounded border-gray-300 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-200" wire:model="form.{{ $fillable }}" /> @error('form.' . $fillable) <span class="text-red-500">{{ str_replace('form.', '', $message) }}</span> @enderror @elseif (str_contains($fillable, 'image') !== false || str_contains($fillable, 'logo') !== false) <x-input type="file" class="w-full rounded border-gray-300 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-200" wire:model="form.{{ $fillable }}" /> @error('form.' . $fillable) <span class="text-red-500">{{ str_replace('form.', '', $message) }}</span> @enderror @elseif (str_contains($fillable, 'time') !== false) <x-input type="datetime-local" min="'{{ date('Y-m-d\TH:i:s', strtotime(now())) }}'" class="w-full rounded border-gray-300 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-200" wire:model="form.{{ $fillable }}" /> @else <x-input type="text" class="w-full rounded border-gray-300 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-200" wire:model="form.{{ $fillable }}" /> @endif </td> @endforeach <td class="py-2 px-4 grid grid-cols-2 gap-2"></td> <td class="py-2 px-4"></td> <td></td> <td class="py-2 px-4"> <x-button>Add</x-button> </td> </form> </tr> @endcan @endif @foreach ($results as $result) <tr class="{{ $loop->odd ? 'bg-gray-50 dark:bg-gray-900' : '' }}"> @foreach ($result->getFillable() as $field) <td class="py-2 px-4"> @if (str_contains($field, 'image') !== false || str_contains($field, 'logo') !== false) <div> <img src="{{ $result->$field }}" alt="{{ $result->$field }}" class="w-20 h-20 rounded-full object-cover"> </div> @else {{ $result->$field }} @endif </td> @endforeach <td class="py-2 px-4"> {{ $result->created_at }} </td> <td class="py-2 px-4"> {{ $result->updated_at }} </td> <td class="py-2 px-4"> {{ $result->deleted_at }} </td> <td class="py-2 px-4"> <div class="flex"> {{ $buttons ?? '' }} @if ($crud) <a href="{{'?type=' . $type . '&id=' . $result->id}}" class="mr-2"> <x-secondary-button> Edit </x-secondary-button> </a> @if ($result->deleted_at) <x-button wire:click="restore({{ $result->id }})" class="mr-2"> Restore </x-button> <x-danger-button wire:click="forceDelete({{ $result->id }})" wire:confirm="Are you sure you want to force delete {{ ucwords(join(' ', preg_split('/(?=[A-Z])/', $type))) }} {{ $result->key ?? $result->name }}?"> Force Delete </x-danger-button> @else <x-danger-button wire:click="delete({{ $result->id }})"> Delete </x-danger-button> @endif @endif </div> </td> </tr> @endforeach
@if ($results->count() == 0) <tr> <td colspan="4" class="py-2 text-center">No results found.</td> </tr> @endif </tbody> </table>
<!-- Pagination links --> <div class="p-4"> @if(isset($results->links)) {{ $results->links }} @endif </div></div>Livewire Component
<!-- resources/views/livewire/Post/index.blade.php --><div class="container mx-auto px-4"> @if($this->id) {{ __('Edit') }} @include('livewire.crud.edit') @else {{ __('Create') }} @include('livewire.crud.create')
{{ $results->links() }}
<div class="mt-4"> <x-table :results="$results" :type="'post'" :create="true" :fillables="$fillables"/> </div>
@endif</div>Zoekfunctie en per page
<!-- resources/views/livewire/Post/index.blade.php -->
<div class="container mx-auto px-4"> @if($this->id) {{ __('Edit') }} @include('livewire.crud.edit') @else {{ __('Create') }} @include('livewire.crud.create')
<div class="flex row-auto gap-2"> <label class="w-full block text-sm font-medium text-gray-900 dark:text-gray-400" for="search"> <input wire:model.live="search" type="text" class="w-full rounded border-gray-300 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-200" placeholder="Search..."> </label> <x-select-per-page/> <x-button wire:click="clearFilters"> Clear Filters </x-button> </div>
{{ $results->links() }}
<div class="mt-4"> <x-table :results="$results" :type="'post'" :create="true" :fillables="$fillables"/> </div>
@endif</div><?php
namespace App\Livewire;
use Livewire\Attributes\Url;use Livewire\Component;use Livewire\WithPagination;use App\Models\Post as PostModel;
class Post extends Component{ use WithPagination;
#[Url (as: 'id')] public $id; public $form = [];
public $url = ''; #[Url(as: 'q')] public $search = ""; public $perPage = 10; public $showDeleted = false;
public function render() { if ($this->id && !PostModel::find($this->id)) { $this->id = ''; }
$query = PostModel::where('title', 'like', '%' . $this->search . '%');
if ($this->showDeleted) { $query = $query->onlyTrashed(); } else { $query = $query->withoutTrashed(); }
return view('livewire.Post.index', [ 'results' => $this->id ? PostModel::find($this->id) : $query->paginate($this->perPage), 'fillables' => (new PostModel())->getFillable(), 'url' => current(explode('?', url()->current())), ]); }
public function create() { $Post = new PostModel(); foreach ($this->form as $key => $value) { $Post->$key = $value; } $Post->save(); }
public function update() { $Post = PostModel::find($this->id); foreach ($this->form as $key => $value) { $Post->$key = $value; } $Post->save(); }
public function delete($id) { $Post = PostModel::find($id); $Post->delete();
return redirect()->route(strtolower('Post')); }
public function clearFilters() { $this->reset(['search', 'showDeleted']); }
public function restore($id) { $Post = PostModel::withTrashed()->find($id); $Post->restore();
return redirect()->route(strtolower('Post')); }
public function forceDelete($id) { $Post = PostModel::withTrashed()->find($id); $Post->forceDelete();
return redirect()->route(strtolower('Post')); }}Routes
Route::get('/posts', function () { return view('posts'); })->name('posts');