What Files are Inside Livewire+Tailwind CRUD?
When you create a CRUD in Livewire+Tailwind QuickAdminPanel, minimum of 12 new files are generated automatically, and 4 more files are updated. Potentially more, if you use some advanced features/modules.
For example, if you create CRUD called Transactions with a few simple columns like "amount" and "transaction_date", here's the minimum list of generated files.
[New Model]
  • app/Models/Transaction.php
1
<?php
2
​
3
namespace App\Models;
4
​
5
use \DateTimeInterface;
6
use App\Support\HasAdvancedFilter;
7
use Carbon\Carbon;
8
use Illuminate\Database\Eloquent\Factories\HasFactory;
9
use Illuminate\Database\Eloquent\Model;
10
use Illuminate\Database\Eloquent\SoftDeletes;
11
​
12
class Transaction extends Model
13
{
14
use HasFactory;
15
use HasAdvancedFilter;
16
use SoftDeletes;
17
​
18
public $table = 'transactions';
19
​
20
public $orderable = [
21
'id',
22
'amount',
23
'transaction_date',
24
];
25
​
26
public $filterable = [
27
'id',
28
'amount',
29
'transaction_date',
30
];
31
​
32
protected $fillable = [
33
'amount',
34
'transaction_date',
35
];
36
​
37
protected $dates = [
38
'transaction_date',
39
'created_at',
40
'updated_at',
41
'deleted_at',
42
];
43
​
44
public function getTransactionDateAttribute($value)
45
{
46
return $value ? Carbon::parse($value)->format(config('project.date_format')) : null;
47
}
48
​
49
public function setTransactionDateAttribute($value)
50
{
51
$this->attributes['transaction_date'] = $value ? Carbon::createFromFormat(config('project.date_format'), $value)->format('Y-m-d') : null;
52
}
53
​
54
protected function serializeDate(DateTimeInterface $date)
55
{
56
return $date->format('Y-m-d H:i:s');
57
}
58
}
Copied!
[New Controller]
  • app/Http/Controllers/Admin/TransactionController.php
1
<?php
2
​
3
namespace App\Http\Controllers\Admin;
4
​
5
use App\Http\Controllers\Controller;
6
use App\Models\Transaction;
7
use Gate;
8
use Illuminate\Http\Request;
9
use Illuminate\Http\Response;
10
​
11
class TransactionController extends Controller
12
{
13
public function index()
14
{
15
abort_if(Gate::denies('transaction_access'), Response::HTTP_FORBIDDEN, '403 Forbidden');
16
​
17
return view('admin.transaction.index');
18
}
19
​
20
public function create()
21
{
22
abort_if(Gate::denies('transaction_create'), Response::HTTP_FORBIDDEN, '403 Forbidden');
23
​
24
return view('admin.transaction.create');
25
}
26
​
27
public function edit(Transaction $transaction)
28
{
29
abort_if(Gate::denies('transaction_edit'), Response::HTTP_FORBIDDEN, '403 Forbidden');
30
​
31
return view('admin.transaction.edit', compact('transaction'));
32
}
33
​
34
public function show(Transaction $transaction)
35
{
36
abort_if(Gate::denies('transaction_show'), Response::HTTP_FORBIDDEN, '403 Forbidden');
37
​
38
return view('admin.transaction.show', compact('transaction'));
39
}
40
}
41
​
Copied!
[New database migration]
  • database/migrations/2021_04_18_000006_create_transactions_table.php
1
<?php
2
​
3
use Illuminate\Database\Migrations\Migration;
4
use Illuminate\Database\Schema\Blueprint;
5
use Illuminate\Support\Facades\Schema;
6
​
7
class CreateTransactionsTable extends Migration
8
{
9
public function up()
10
{
11
Schema::create('transactions', function (Blueprint $table) {
12
$table->bigIncrements('id');
13
$table->decimal('amount', 15, 2)->nullable();
14
$table->date('transaction_date')->nullable();
15
$table->timestamps();
16
$table->softDeletes();
17
});
18
}
19
}
Copied!
Notice about migrations: after every new or changed CRUD, we regenerate all migration files to make sure they are in the right order, to avoid creating foreign keys on non-existing tables. Therefore, keep in mind that you need to double-check the migration files manually, so they still work after you merge changes.
[New Blade views]
  • resources/views/admin/transaction/create.blade.php
1
@extends('layouts.admin')
2
@section('content')
3
​
4
<div class="card bg-blueGray-100">
5
<div class="card-header">
6
<div class="card-header-container">
7
<h6 class="card-title">
8
{{ trans('global.create') }}
9
{{ trans('cruds.transaction.title_singular') }}
10
</h6>
11
</div>
12
</div>
13
​
14
<div class="card-body">
15
@livewire('transaction.create')
16
</div>
17
</div>
18
@endsection
Copied!
  • resources/views/admin/transaction/edit.blade.php
1
@extends('layouts.admin')
2
@section('content')
3
​
4
<div class="card bg-blueGray-100">
5
<div class="card-header">
6
<div class="card-header-container">
7
<h6 class="card-title">
8
{{ trans('global.edit') }}
9
{{ trans('cruds.transaction.title_singular') }}:
10
{{ trans('cruds.transaction.fields.id') }}
11
{{ $transaction->id }}
12
</h6>
13
</div>
14
</div>
15
​
16
<div class="card-body">
17
@livewire('transaction.edit', [$transaction])
18
</div>
19
</div>
20
@endsection
Copied!
  • resources/views/admin/transaction/index.blade.php
1
@extends('layouts.admin')
2
@section('content')
3
<div class="card bg-white">
4
<div class="card-header border-b border-blueGray-200">
5
<div class="card-header-container">
6
<h6 class="card-title">
7
{{ trans('cruds.transaction.title_singular') }}
8
{{ trans('global.list') }}
9
</h6>
10
​
11
@can('transaction_create')
12
<a class="btn btn-indigo" href="{{ route('admin.transactions.create') }}">
13
{{ trans('global.add') }} {{ trans('cruds.transaction.title_singular') }}
14
</a>
15
@endcan
16
</div>
17
</div>
18
@livewire('transaction.index')
19
​
20
</div>
21
@endsection
Copied!
  • resources/views/admin/transaction/show.blade.php
1
@extends('layouts.admin')
2
@section('content')
3
​
4
<div class="card bg-blueGray-100">
5
<div class="card-header">
6
<div class="card-header-container">
7
<h6 class="card-title">
8
{{ trans('global.view') }}
9
{{ trans('cruds.transaction.title_singular') }}:
10
{{ trans('cruds.transaction.fields.id') }}
11
{{ $transaction->id }}
12
</h6>
13
</div>
14
</div>
15
​
16
<div class="card-body">
17
<div class="pt-3">
18
<table class="table table-view">
19
<tbody class="bg-white">
20
<tr>
21
<th>
22
{{ trans('cruds.transaction.fields.id') }}
23
</th>
24
<td>
25
{{ $transaction->id }}
26
</td>
27
</tr>
28
<tr>
29
<th>
30
{{ trans('cruds.transaction.fields.amount') }}
31
</th>
32
<td>
33
{{ $transaction->amount }}
34
</td>
35
</tr>
36
<tr>
37
<th>
38
{{ trans('cruds.transaction.fields.transaction_date') }}
39
</th>
40
<td>
41
{{ $transaction->transaction_date }}
42
</td>
43
</tr>
44
</tbody>
45
</table>
46
</div>
47
<div class="form-group">
48
<a href="{{ route('admin.transactions.index') }}" class="btn btn-secondary">
49
{{ trans('global.back') }}
50
</a>
51
</div>
52
</div>
53
</div>
54
@endsection
Copied!
[New Livewire Components]
  • app/Http/Livewire/Transaction/Create.php
1
<?php
2
​
3
namespace App\Http\Livewire\Transaction;
4
​
5
use App\Models\Transaction;
6
use Livewire\Component;
7
​
8
class Create extends Component
9
{
10
public Transaction $transaction;
11
​
12
public function mount(Transaction $transaction)
13
{
14
$this->transaction = $transaction;
15
}
16
​
17
public function render()
18
{
19
return view('livewire.transaction.create');
20
}
21
​
22
public function submit()
23
{
24
$this->validate();
25
​
26
$this->transaction->save();
27
​
28
return redirect()->route('admin.transactions.index');
29
}
30
​
31
protected function rules(): array
32
{
33
return [
34
'transaction.amount' => [
35
'numeric',
36
'nullable',
37
],
38
'transaction.transaction_date' => [
39
'nullable',
40
'date_format:' . config('project.date_format'),
41
],
42
];
43
}
44
}
Copied!
  • app/Http/Livewire/Transaction/Edit.php
1
class Edit extends Component
2
{
3
public Transaction $transaction;
4
​
5
public function mount(Transaction $transaction)
6
{
7
$this->transaction = $transaction;
8
}
9
​
10
public function render()
11
{
12
return view('livewire.transaction.edit');
13
}
14
​
15
public function submit()
16
{
17
$this->validate();
18
​
19
$this->transaction->save();
20
​
21
return redirect()->route('admin.transactions.index');
22
}
23
​
24
protected function rules(): array
25
{
26
return [
27
'transaction.amount' => [
28
'numeric',
29
'nullable',
30
],
31
'transaction.transaction_date' => [
32
'nullable',
33
'date_format:' . config('project.date_format'),
34
],
35
];
36
}
37
}
Copied!
  • app/Http/Livewire/Transaction/Index.php
1
<?php
2
​
3
namespace App\Http\Livewire\Transaction;
4
​
5
use App\Http\Livewire\WithConfirmation;
6
use App\Http\Livewire\WithSorting;
7
use App\Models\Transaction;
8
use Illuminate\Http\Response;
9
use Illuminate\Support\Facades\Gate;
10
use Livewire\Component;
11
use Livewire\WithPagination;
12
​
13
class Index extends Component
14
{
15
use WithPagination;
16
use WithSorting;
17
use WithConfirmation;
18
​
19
public int $perPage;
20
​
21
public array $orderable;
22
​
23
public string $search = '';
24
​
25
public array $selected = [];
26
​
27
public array $paginationOptions;
28
​
29
protected $queryString = [
30
'search' => [
31
'except' => '',
32
],
33
'sortBy' => [
34
'except' => 'id',
35
],
36
'sortDirection' => [
37
'except' => 'desc',
38
],
39
];
40
​
41
public function getSelectedCountProperty()
42
{
43
return count($this->selected);
44
}
45
​
46
public function updatingSearch()
47
{
48
$this->resetPage();
49
}
50
​
51
public function updatingPerPage()
52
{
53
$this->resetPage();
54
}
55
​
56
public function resetSelected()
57
{
58
$this->selected = [];
59
}
60
​
61
public function mount()
62
{
63
$this->sortBy = 'id';
64
$this->sortDirection = 'desc';
65
$this->perPage = 100;
66
$this->paginationOptions = config('project.pagination.options');
67
$this->orderable = (new Transaction())->orderable;
68
}
69
​
70
public function render()
71
{
72
$query = Transaction::advancedFilter([
73
's' => $this->search ?: null,
74
'order_column' => $this->sortBy,
75
'order_direction' => $this->sortDirection,
76
]);
77
​
78
$transactions = $query->paginate($this->perPage);
79
​
80
return view('livewire.transaction.index', compact('query', 'transactions', 'transactions'));
81
}
82
​
83
public function deleteSelected()
84
{
85
abort_if(Gate::denies('transaction_delete'), Response::HTTP_FORBIDDEN, '403 Forbidden');
86
​
87
Transaction::whereIn('id', $this->selected)->delete();
88
​
89
$this->resetSelected();
90
}
91
​
92
public function delete(Transaction $transaction)
93
{
94
abort_if(Gate::denies('transaction_delete'), Response::HTTP_FORBIDDEN, '403 Forbidden');
95
​
96
$transaction->delete();
97
}
98
}
Copied!
[New Livewire Blade views]
  • resources/views/livewire/transaction/create.blade.php
1
<form wire:submit.prevent="submit" class="pt-3">
2
​
3
<div class="form-group {{ $errors->has('transaction.amount') ? 'invalid' : '' }}">
4
<label class="form-label" for="amount">{{ trans('cruds.transaction.fields.amount') }}</label>
5
<input class="form-control" type="number" name="amount" id="amount" wire:model.defer="transaction.amount" step="0.01">
6
<div class="validation-message">
7
{{ $errors->first('transaction.amount') }}
8
</div>
9
<div class="help-block">
10
{{ trans('cruds.transaction.fields.amount_helper') }}
11
</div>
12
</div>
13
<div class="form-group {{ $errors->has('transaction.transaction_date') ? 'invalid' : '' }}">
14
<label class="form-label" for="transaction_date">{{ trans('cruds.transaction.fields.transaction_date') }}</label>
15
<x-date-picker class="form-control" wire:model="transaction.transaction_date" id="transaction_date" name="transaction_date" picker="date" />
16
<div class="validation-message">
17
{{ $errors->first('transaction.transaction_date') }}
18
</div>
19
<div class="help-block">
20
{{ trans('cruds.transaction.fields.transaction_date_helper') }}
21
</div>
22
</div>
23
​
24
<div class="form-group">
25
<button class="btn btn-indigo mr-2" type="submit">
26
{{ trans('global.save') }}
27
</button>
28
<a href="{{ route('admin.transactions.index') }}" class="btn btn-secondary">
29
{{ trans('global.cancel') }}
30
</a>
31
</div>
32
</form>
Copied!
  • resources/views/livewire/transaction/edit.blade.php
1
<form wire:submit.prevent="submit" class="pt-3">
2
​
3
<div class="form-group {{ $errors->has('transaction.amount') ? 'invalid' : '' }}">
4
<label class="form-label" for="amount">{{ trans('cruds.transaction.fields.amount') }}</label>
5
<input class="form-control" type="number" name="amount" id="amount" wire:model.defer="transaction.amount" step="0.01">
6
<div class="validation-message">
7
{{ $errors->first('transaction.amount') }}
8
</div>
9
<div class="help-block">
10
{{ trans('cruds.transaction.fields.amount_helper') }}
11
</div>
12
</div>
13
<div class="form-group {{ $errors->has('transaction.transaction_date') ? 'invalid' : '' }}">
14
<label class="form-label" for="transaction_date">{{ trans('cruds.transaction.fields.transaction_date') }}</label>
15
<x-date-picker class="form-control" wire:model="transaction.transaction_date" id="transaction_date" name="transaction_date" picker="date" />
16
<div class="validation-message">
17
{{ $errors->first('transaction.transaction_date') }}
18
</div>
19
<div class="help-block">
20
{{ trans('cruds.transaction.fields.transaction_date_helper') }}
21
</div>
22
</div>
23
​
24
<div class="form-group">
25
<button class="btn btn-indigo mr-2" type="submit">
26
{{ trans('global.save') }}
27
</button>
28
<a href="{{ route('admin.transactions.index') }}" class="btn btn-secondary">
29
{{ trans('global.cancel') }}
30
</a>
31
</div>
32
</form>
Copied!
  • resources/views/livewire/transaction/index.blade.php
1
<div>
2
<div class="card-controls sm:flex">
3
<div class="w-full sm:w-1/2">
4
Per page:
5
<select wire:model="perPage" class="form-select w-full sm:w-1/6">
6
@foreach($paginationOptions as $value)
7
<option value="{{ $value }}">{{ $value }}</option>
8
@endforeach
9
</select>
10
​
11
<button class="btn btn-rose ml-3 disabled:opacity-50 disabled:cursor-not-allowed" type="button" wire:click="confirm('deleteSelected')" wire:loading.attr="disabled" {{ $this->selectedCount ? '' : 'disabled' }}>
12
{{ __('Delete Selected') }}
13
</button>
14
​
15
</div>
16
<div class="w-full sm:w-1/2 sm:text-right">
17
Search:
18
<input type="text" wire:model.debounce.300ms="search" class="w-full sm:w-1/3 inline-block" />
19
</div>
20
</div>
21
<div wire:loading.delay class="col-12 alert alert-info">
22
Loading...
23
</div>
24
<table class="table table-index w-full">
25
<thead>
26
<tr>
27
<th class="w-9">
28
</th>
29
<th class="w-28">
30
{{ trans('cruds.transaction.fields.id') }}
31
@include('components.table.sort', ['field' => 'id'])
32
</th>
33
<th>
34
{{ trans('cruds.transaction.fields.amount') }}
35
@include('components.table.sort', ['field' => 'amount'])
36
</th>
37
<th>
38
{{ trans('cruds.transaction.fields.transaction_date') }}
39
@include('components.table.sort', ['field' => 'transaction_date'])
40
</th>
41
<th>
42
</th>
43
</tr>
44
</thead>
45
<tbody>
46
@forelse($transactions as $transaction)
47
<tr>
48
<td>
49
<input type="checkbox" value="{{ $transaction->id }}" wire:model="selected">
50
</td>
51
<td>
52
{{ $transaction->id }}
53
</td>
54
<td>
55
{{ $transaction->amount }}
56
</td>
57
<td>
58
{{ $transaction->transaction_date }}
59
</td>
60
<td>
61
<div class="flex justify-end">
62
@can('transaction_show')
63
<a class="btn btn-sm btn-info mr-2" href="{{ route('admin.transactions.show', $transaction) }}">
64
{{ trans('global.view') }}
65
</a>
66
@endcan
67
@can('transaction_edit')
68
<a class="btn btn-sm btn-success mr-2" href="{{ route('admin.transactions.edit', $transaction) }}">
69
{{ trans('global.edit') }}
70
</a>
71
@endcan
72
@can('transaction_delete')
73
<button class="btn btn-sm btn-rose mr-2" type="button" wire:click="confirm('delete', {{ $transaction->id }})" wire:loading.attr="disabled">
74
{{ trans('global.delete') }}
75
</button>
76
@endcan
77
</div>
78
</td>
79
</tr>
80
@empty
81
<tr>
82
<td colspan="10">No entries found.</td>
83
</tr>
84
@endforelse
85
</tbody>
86
</table>
87
​
88
<div class="card-body">
89
<div class="pt-3">
90
@if($this->selectedCount)
91
<p class="text-sm leading-5">
92
<span class="font-medium">
93
{{ $this->selectedCount }}
94
</span>
95
{{ __('Entries selected') }}
96
</p>
97
@endif
98
{{ $transactions->links() }}
99
</div>
100
</div>
101
</div>
102
​
103
@push('scripts')
104
<script>
105
Livewire.on('confirm', e => {
106
if (!confirm("{{ trans('global.areYouSure') }}")) {
107
return
108
}
109
@this[e.callback](...e.argv)
110
})
111
</script>
112
@endpush
Copied!
[Changed main menu Blade Component]
  • resources/views/components/sidebar.blade.php
1
<nav class="md:left-0 md:block md:fixed md:top-0 md:bottom-0 md:overflow-y-auto md:flex-row md:flex-nowrap md:overflow-hidden shadow-xl bg-white flex flex-wrap items-center justify-between relative md:w-64 z-10 py-4 px-6">
2
<div class="md:flex-col md:items-stretch md:min-h-full md:flex-nowrap px-0 flex flex-wrap items-center justify-between w-full mx-auto">
3
<button class="cursor-pointer text-black opacity-50 md:hidden px-3 py-1 text-xl leading-none bg-transparent rounded border border-solid border-transparent" type="button" onclick="toggleNavbar('example-collapse-sidebar')">
4
<i class="fas fa-bars"></i>
5
</button>
6
<a class="md:block text-left md:pb-2 text-blueGray-700 mr-0 inline-block whitespace-nowrap text-sm uppercase font-bold p-4 px-0" href="{{ route('admin.home') }}">
7
{{ trans('panel.site_title') }}
8
</a>
9
<div class="md:flex md:flex-col md:items-stretch md:opacity-100 md:relative md:mt-4 md:shadow-none shadow absolute top-0 left-0 right-0 z-40 overflow-y-auto overflow-x-hidden h-auto items-center flex-1 rounded hidden" id="example-collapse-sidebar">
10
<div class="md:min-w-full md:hidden block pb-4 mb-4 border-b border-solid border-blueGray-300">
11
<div class="flex flex-wrap">
12
<div class="w-6/12">
13
<a class="md:block text-left md:pb-2 text-blueGray-700 mr-0 inline-block whitespace-nowrap text-sm uppercase font-bold p-4 px-0" href="{{ route('admin.home') }}">
14
{{ trans('panel.site_title') }}
15
</a>
16
</div>
17
<div class="w-6/12 flex justify-end">
18
<button type="button" class="cursor-pointer text-black opacity-50 md:hidden px-3 py-1 text-xl leading-none bg-transparent rounded border border-solid border-transparent" onclick="toggleNavbar('example-collapse-sidebar')">
19
<i class="fas fa-times"></i>
20
</button>
21
</div>
22
</div>
23
</div>
24
<!-- Divider -->
25
<hr class="mb-6 md:min-w-full" />
26
<!-- Heading -->
27
​
28
<ul class="md:flex-col md:min-w-full flex flex-col list-none">
29
<li class="items-center">
30
<a href="{{ route("admin.home") }}" class="{{ request()->is("admin") ? "sidebar-nav-active" : "sidebar-nav" }}">
31
<i class="fas fa-tv"></i>
32
{{ trans('global.dashboard') }}
33
</a>
34
</li>
35
36
@can('transaction_access')
37
<li class="items-center">
38
<a class="{{ request()->is("admin/transactions*") ? "sidebar-nav-active" : "sidebar-nav" }}" href="{{ route("admin.transactions.index") }}">
39
<i class="fa-fw c-sidebar-nav-icon fas fa-cogs">
40
</i>
41
{{ trans('cruds.transaction.title') }}
42
</a>
43
</li>
44
@endcan
45
​
46
@if(file_exists(app_path('Http/Controllers/Auth/ChangePasswordController.php')))
47
@can('profile_password_edit')
48
<li class="items-center">
49
<a href="{{ route("profile.password.edit") }}" class="{{ request()->is("profile/password") || request()->is("profile/password/*") ? "sidebar-nav-active" : "sidebar-nav" }}">
50
<i class="fas fa-cogs"></i>
51
{{ trans('global.change_password') }}
52
</a>
53
</li>
54
@endcan
55
@endif
56
​
57
<li class="items-center">
58
<a href="#" onclick="event.preventDefault(); document.getElementById('logoutform').submit();" class="sidebar-nav">
59
<i class="fa-fw fas fa-sign-out-alt"></i>
60
{{ trans('global.logout') }}
61
</a>
62
</li>
63
</ul>
64
</div>
65
</div>
66
</nav>
Copied!
[Changed main routes]
  • routes/web.php
1
<?php
2
​
3
// ...
4
​
5
Route::group(['prefix' => 'admin', 'as' => 'admin.', 'middleware' => ['auth']], function () {
6
// ... other routes
7
​
8
// Transactions
9
Route::resource('transactions', TransactionController::class, ['except' => ['store', 'update', 'destroy']]);
10
});
Copied!
[Changed Seeds for Permissions]
  • database/seeds/PermissionsTableSeeder.php
1
<?php
2
​
3
namespace Database\Seeders;
4
​
5
use App\Models\Permission;
6
use Illuminate\Database\Seeder;
7
​
8
class PermissionsTableSeeder extends Seeder
9
{
10
public function run()
11
{
12
$permissions = [
13
// ... other permissions
14
15
[
16
'id' => 28,
17
'title' => 'transaction_create',
18
],
19
[
20
'id' => 29,
21
'title' => 'transaction_edit',
22
],
23
[
24
'id' => 30,
25
'title' => 'transaction_show',
26
],
27
[
28
'id' => 31,
29
'title' => 'transaction_delete',
30
],
31
[
32
'id' => 32,
33
'title' => 'transaction_access',
34
],
35
];
36
​
37
Permission::insert($permissions);
38
}
39
}
Copied!
[Changed Translation Files for new CRUD]
  • resources/lang/en/cruds.php
1
<?php
2
​
3
return [
4
// ... other translations
5
​
6
'transaction' => [
7
'title' => 'Transactions',
8
'title_singular' => 'Transaction',
9
'fields' => [
10
'id' => 'ID',
11
'id_helper' => ' ',
12
'amount' => 'Amount',
13
'amount_helper' => ' ',
14
'transaction_date' => 'Transaction Date',
15
'transaction_date_helper' => ' ',
16
'created_at' => 'Created at',
17
'created_at_helper' => ' ',
18
'updated_at' => 'Updated at',
19
'updated_at_helper' => ' ',
20
'deleted_at' => 'Deleted at',
21
'deleted_at_helper' => ' ',
22
],
23
],
24
];
Copied!
​
Last modified 7mo ago
Copy link