- Let's create migration file. Run command:
php artisan make:migration create_companies_table
- In migration file add code:
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateCompaniesTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('companies', function (Blueprint $table) {
$table->id();
$table->string('title')->unique();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('companies');
}
}
- Run command to add table in database:
php artisan migrate
- Run command to create Livewire CRUD files (it will create /app/Http/Livewire/Companies.php and /resources/views/livewire/companies.blade.php):
php artisan make:livewire companies
- In /app/Http/Livewire/Companies.php file add code:
<?php
namespace App\Http\Livewire;
use Livewire\Component;
use App\Models\Company;
class Companies extends Component
{
public $title;
public $company_id;
public $isOpen = 0;
/**
* The attributes that are mass assignable.
*
* @var array
*/
public function render()
{
return view('livewire.companies', [
'companies' => Company::orderBy('id', 'desc')]);
}
/**
* The attributes that are mass assignable.
*
* @var array
*/
public function create()
{
$this->resetInputFields();
$this->openModal();
}
/**
* The attributes that are mass assignable.
*
* @var array
*/
public function openModal()
{
$this->isOpen = true;
}
/**
* The attributes that are mass assignable.
*
* @var array
*/
public function closeModal()
{
$this->isOpen = false;
}
/**
* The attributes that are mass assignable.
*
* @var array
*/
private function resetInputFields(){
$this->title = '';
$this->company_id = '';
}
/**
* The attributes that are mass assignable.
*
* @var array
*/
public function store()
{
$this->validate([
'title' => 'required|unique:companies,title,'.$this->company_id,
]);
$data = array(
'title' => $this->title
);
$company = Company::updateOrCreate(['id' => $this->company_id],$data);
session()->flash('message', $this->company_id ? 'Company updated successfully.' : 'Company created successfully.');
$this->closeModal();
$this->resetInputFields();
}
/**
* The attributes that are mass assignable.
*
* @var array
*/
public function edit($id)
{
$company = Company::findOrFail($id);
$this->company_id = $id;
$this->title = $company->title;
$this->openModal();
}
/**
* The attributes that are mass assignable.
*
* @var array
*/
public function delete($id)
{
$this->company_id = $id;
Company::find($id)->delete();
session()->flash('message', 'Company deleted successfully.');
}
}
- In /resources/views/livewire/companies.blade.php file add code
<x-slot name="header">
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
{{ __('Companies') }}
</h2>
</x-slot>
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
@if (session()->has('message'))
<div id="alert" class="text-white px-6 py-4 border-0 rounded relative mb-4 bg-green-500">
<span class="inline-block align-middle mr-8">
{{ session('message') }}
</span>
<button class="absolute bg-transparent text-2xl font-semibold leading-none right-0 top-0 mt-4 mr-6 outline-none focus:outline-none" onclick="document.getElementById('alert').remove();">
<span>×</span>
</button>
</div>
@endif
<button wire:click="create()" class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded mt-10">Create New Company</button>
@if (count($companies)>0)
<div class="py-10">
<div class="inline-block min-w-full shadow rounded-lg overflow-hidden">
<table class="min-w-full leading-normal">
<thead>
<tr>
<th
class="px-5 py-3 border-b-2 border-black bg-black text-left text-xs font-semibold text-white uppercase tracking-wider">
{{ __('Title') }}
</th>
<th
class="px-5 py-3 border-b-2 border-black bg-black text-left text-xs font-semibold text-white uppercase tracking-wider">
</th>
</tr>
</thead>
<tbody>
@foreach($companies as $company)
<tr>
<td class="px-5 py-5 bg-white text-sm @if (!$loop->last) border-gray-200 border-b @endif">
{{ Str::limit($company->title, 25) }}
</td>
<td class="px-5 py-5 bg-white text-sm @if (!$loop->last) border-gray-200 border-b @endif text-right">
<div class="inline-block whitespace-no-wrap">
<button wire:click="edit({{ $company->id }})" class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">Edit</button>
<button wire:click="$emit('triggerDelete',{{ $company->id }})" class="bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded">Delete</button>
</div>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
@endif
@if($isOpen)
<div class="fixed z-100 w-full h-full bg-gray-500 opacity-75 top-0 left-0"></div>
<div class="fixed z-101 w-full h-full top-0 left-0 overflow-y-auto">
<div class="table w-full h-full py-6">
<div class="table-cell text-center align-middle">
<div class="w-full max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="bg-white rounded-lg text-left overflow-hidden shadow-xl">
<form>
<div class="px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
<div class="flex flex-wrap -mx-3 mb-6">
<div class="w-full md:w-1/2 px-3 mb-6 md:mb-0">
<label for="titleInput" class="block text-gray-700 text-sm font-bold mb-2">Title:</label>
<input type="text" class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" id="titleInput" placeholder="Enter Title" wire:model="title">
@error('title') <span class="text-red-500">{{ $message }}</span>@enderror
</div>
</div>
</div>
<div class="px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
<span class="flex w-full sm:ml-3 sm:w-auto">
<button wire:click.prevent="store()" type="button" class="inline-flex bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">Save</button>
</span>
<span class="mt-3 flex w-full sm:mt-0 sm:w-auto">
<button wire:click="closeModal()" type="button" class="inline-flex bg-white hover:bg-gray-200 border border-gray-300 text-gray-500 font-bold py-2 px-4 rounded">Cancel</button>
</span>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
@endif
</div>
@push('styles')
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/sweetalert2@10/dist/sweetalert2.min.css">
@endpush
@push('scripts')
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@10"></script>
<script src="https://cdn.jsdelivr.net/npm/promise-polyfill@8/dist/polyfill.js"></script>
<script type="text/javascript">
document.addEventListener('DOMContentLoaded', function () {
@this.on('triggerDelete', companyId => {
Swal.fire({
title: 'Are You Sure?',
text: 'Company record will be deleted!',
type: "warning",
showCancelButton: true,
confirmButtonColor: '#d33',
cancelButtonColor: '#3085d6',
confirmButtonText: 'Delete!'
}).then((result) => {
if (result.value) {
@this.call('delete',companyId)
} else {
console.log("Canceled");
}
});
});
})
</script>
@endpush
- Our javascript code has to be between tags:
@push('scripts') ... @endpush
and style
@push('styles') ... @endpush
It means in
/resources/views/layouts/app.blade.php
need to add @stack('styles') and @stack('scripts')
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>{{ config('app.name', 'Laravel') }}</title>
<!-- Fonts -->
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Nunito:wght@400;600;700&display=swap">
<!-- Styles -->
<link rel="stylesheet" href="{{ asset('css/app.css') }}">
@livewireStyles
@stack('styles')
<!-- Scripts -->
<script src="https://cdn.jsdelivr.net/gh/alpinejs/alpine@v2.7.0/dist/alpine.js" defer></script>
</head>
<body class="font-sans antialiased">
<div class="min-h-screen bg-gray-100">
@livewire('navigation-dropdown')
<!-- Page Heading -->
<header class="bg-white shadow">
<div class="max-w-7xl mx-auto py-6 px-4 sm:px-6 lg:px-8">
{{ $header }}
</div>
</header>
<!-- Page Content -->
<main>
{{ $slot }}
</main>
</div>
@stack('modals')
@livewireScripts
@stack('scripts')
</body>
</html>
- Run command to create Company Model:
php artisan make:model Company
- In /app/Models/Company.php file add code:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Company extends Model
{
use HasFactory;
protected $fillable = [
'title'
];
}
- In /routes/web.php need to add route:
Route::middleware(['auth:sanctum', 'verified'])->get('/companies', App\Http\Livewire\Companies::class)->name('companies');
- And run command to clean route cache:
php artisan route:cache
- And in the last step need to add companies pages link to main menu. In /resources/views/navigation-dropdown.blade.php add menu item:
<div class="hidden space-x-8 sm:-my-px sm:ml-10 sm:flex">
<x-jet-nav-link href="{{ route('companies') }}" :active="request()->routeIs('companies')">
{{ __('Companies') }}
</x-jet-nav-link>
</div>
- And in the same file, where is comment <!-- Responsive Navigation Menu --> add:
<x-jet-responsive-nav-link href="{{ route('companies') }}" :active="request()->routeIs('companies')">
{{ __('Companies') }}
</x-jet-responsive-nav-link>
- These are basic steps to create CRUD by using Laravel, Livewire and Tailwind, but if we want to customise our CRUD please visit my second tutorial part: Laravel 8 - advanced CRUD (Livewire and Tailwind)
Top comments (5)
i dont have /resources/views/layouts/app.blade.php and /resources/views/navigation-dropdown.blade.php files.
did i miss something? are there any previous guide before this?
This tutorial skips a lot of steps or assumes you know how to do some things maybe? Check this out: laravel-livewire.com/docs/2.x/rend...
Sorry for that, but my tutorial was only about CRUD not about part of website design elements. "app.blade.php" is my main layout and "navigation-dropdown.blade.php" has menu items.
If you get "count(): Parameter must be an array or an object that implements Countable",
try replacing the content of the Companies render-method with:
struck on "$_instance is not define"