Laravel has been a go-to choice for web developers due to its elegant syntax and powerful features. With the introduction of Laravel 10, the framework has become even more robust and versatile. Laravel has continually pushed the envelope in this regard. With the advent of Laravel 10, developers are granted even more tools to build dynamic web applications efficiently. One of the standout features of Laravel 10 is its seamless integration with Livewire. The Livewire is a full-stack framework that simplifies the development of interactive and real-time web applications. In this comprehensive step-by-step guide, we will explore the creation of a dynamic Livewire CRUD in Laravel 10. So, by the end of this tutorial, you will have a deep understanding of how to harness the power of Laravel and Livewire to craft robust and interactive web applications.
Before moving to the example, let’s quickly take a look at what we are going to cover up through the example.
Firstly, we will have one tabular view to list out all the records with option to Add New Records.
We will have the action buttons as View, Edit, and Delete.
Therefore, on clicking on Add new, it will open a modal having the form with some basic inputs. This single modal will be used to View, Edit, and Update the record.
Also, we have real-time form validation using Livewire.
We have the Livewire flash message. So, it will be captured in the blade file for success and error as well.
Last but not least, we have to Delete confirmation modal. So, on confirmation, the record will be deleted.
This has not ended up. We have covered many more things as well in this post. So, let’s dive deep into this tutorial.
Prerequisites
To proceed with this Livewire CRUD in Laravel, you will have to follow the below prerequisites.
- PHP >=8.1 – If you are implementing in Laravel 10 then you will require this.
- Composer – For dependency manager.
- Apache/Nginx Server
- VS Code Editor (Optional)
- MySQL (version > 5)
- Basic Understanding – You require some basic understanding of Laravel.
Once you are done with these, let’s go ahead for create a Laravel application setup.
Recommended: Livewire Form Validation: Building Better User Experiences in Laravel
Step 1 – Create a Project For Livewire CRUD in Laravel 10
At the very first step, you will have to create a Laravel project for implementing a Livewire CRUD. Hence, hit the below command in the terminal.
composer create-project --prefer-dist laravel/laravel laravel-livewire
Once, it is done, you will have to configure a database connection for this. Therefore, come inside the project directory using the cd command.
cd laravel-livewire
Thereafter, look at the .env file available in the root of the project folder. Now, under the DB section, just add the database credentials as you create it.
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel_livewire
DB_USERNAME=root
DB_PASSWORD=root
Now, the project is connected to the Database. Hence, let’s move to the Livewire installation.
Recommended: Getting Started with Laravel Livewire : A Comprehensive Guide for Beginners
Step 2 – Install Livewire in Laravel
Livewire is a game-changer for Laravel developers. It makes it easier to build dynamic interfaces. In order to install Livewire, you will have to go through the composer. Hence, hit the below command in the terminal.
composer install livewire/livewire
Once the installation has been completed, you can move to the next step. Next, you will have to create a model and migration for Livewire CRUD in Laravel 10.
Hence, let’s do the model and migration setup.
Step 3 – Create a Model and Migration in Laravel 10
Let’s begin by creating a simple “Book” model for our CRUD application using Livewire. Hence, generate both the model and migration files using the below artisan command.
php artisan make:model Book -mc
Here, in the command, I have passed the controller flag as well. So, now, we will have a model, a migration, and a controller too.
After that, you will have to add the schema in the migration. Therefore, add the below snippet in the book migration file.
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('books', function (Blueprint $table) {
$table->id();
$table->string('title')->nullable();
$table->string('author')->nullable();
$table->double('price')->nullable();
$table->string('publisher')->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('books');
}
};
Next, thereafter, you will have to migrate it using the artisan command.
php artisan migrate
Also, you will have to add the fillable property in the Book model.
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Book extends Model
{
use HasFactory;
protected $fillable = [
'title',
'author',
'price',
'publisher'
];
}
Step 4 – Create a Livewire Component
For creating a Livewire CRUD application in Laravel, you will need at least one Livewire component.
php artisan make:livewire BookManager
The above command will create two files. One is the class file and another one is the view file.
The livewire class file will be located in the app/Http/Livewire directory. But the Livewire component blade file (book-manager.blade.php) will be located inside the resources/views/livewire folder.
After having the component, let’s move to the interface design part.
Recommended: Scaling Laravel Apps with Efficient Many-to-Many Relationships
Step 5 – Design the User Interface For Livewire CRUD in Laravel
Regarding the design interface as I shown at the beginning, you will require a tabular view for displaying the list of Books. Also, there will be one form for adding and updating the book record.
We will be using the modal. Hence, you will have to create two extra blade files inside the views/livewire directory.
- create.blade.php
- delete-book.blade.php
So, after creating these two blade files the views/livewire directory will have these files as shown below.
- book-manager.blade.php – This is the main component that we have created using the command.
- create.blade.php – This is a modal used for managing to create, view, and update operations.
- delete-book.blade.php – This is also a modal used for delete confirmation.
So, let’s design the user interfaces by adding the below snippets.
<div>
@include('livewire.create')
@include('livewire.delete-book')
<div class="row">
{{-- Alert --}}
<div class="col-xl-6 col-md-6 col-6">
@if (session()->has('success'))
<div class="alert alert-success alert-dismissible fade show">
<svg class="bi flex-shrink-0 me-2" width="24" height="24" role="img" aria-label="Success:">
<use xlink:href="#check-circle-fill" />
</svg>
<strong>Success!</strong> {{ session('success') }}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
@elseif (session()->has('error'))
<div class="alert alert-danger alert-dismissible fade show">
<svg class="bi flex-shrink-0 me-2" width="24" height="24" role="img"
aria-label="Danger:">
<use xlink:href="#exclamation-triangle-fill" />
</svg>
<strong>Alert!</strong> {{ session('error') }}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
@endif
</div>
{{-- Add New Book --}}
<div class="col-xl-6 col-md-6 col-6 text-end">
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#bookModal">
Add New Book
</button>
</div>
</div>
<div class="card shadow mt-3">
<div class="card-body">
<div class="table-responsive mt-3">
<table class="table table-striped table-bordered">
<thead>
<tr>
<th>ID</th>
<th>Title</th>
<th>Author</th>
<th>Price</th>
<th>Publisher</th>
<th>Action</th>
</tr>
</thead>
<tbody>
@forelse ($books as $book)
<tr>
<td>{{ $book->id }}</td>
<td>
{{ $book->title }}
</td>
<td>
{{ $book->author }}
</td>
<td>
{{ $book->price }}
</td>
<td>
{{ $book->publisher }}
</td>
<td>
<button wire:click="showBook({{ $book->id }})" data-bs-toggle="modal"
data-bs-target="#bookModal" class="btn btn-info btn-sm">View</button>
<button wire:click="editBook({{ $book->id }})" data-bs-toggle="modal"
data-bs-target="#bookModal" class="btn btn-success btn-sm">Edit</button>
<button wire:click="deleteBook({{ $book->id }})" data-bs-toggle="modal"
data-bs-target="#deleteBookModal" class="btn btn-danger btn-sm">Delete</button>
</td>
</tr>
@empty
<tr>
<td colspan="6" align="center">
<span class="text-danger"> No Records Found </span>
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
</div>
</div>
{{-- Handle Browser Dispatched Events --}}
<script>
window.addEventListener('close-modal', event => {
$('#bookModal').modal('hide');
$('#deleteBookModal').modal('hide');
});
</script>
</div>
Next, you will have to add the below snippet for the form (create.blade.php).
{{-- Book Modal --}}
<div wire:ignore.self class="modal fade" id="bookModal" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1"
aria-labelledby="bookModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title fw-bold" id="bookModalLabel">{{ $isView ? 'Show' : ($isEdit ? 'Update' : 'Create') }} Book</h5>
<button type="button" wire:click="closeModal" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
{{-- Form starts --}}
<form wire:submit.prevent="saveBook">
{{-- Book title --}}
<div class="form-group mb-3">
<label for="title">Book Title <span class="text-danger">*</span></label>
<input type="text" {{$isView ? 'disabled' : '' }} class="form-control" placeholder="Book Title" wire:model="title" />
@error('title') <span class="text-danger">{{ $message }}</span> @enderror
</div>
{{-- Book author --}}
<div class="form-group mb-3">
<label for="title">Author <span class="text-danger">*</span></label>
<input type="text" {{$isView ? 'disabled' : '' }} class="form-control" placeholder="Book Author" wire:model="author" />
@error('author') <span class="text-danger">{{ $message }}</span> @enderror
</div>
{{-- Book price --}}
<div class="form-group mb-3">
<label for="price">Price <span class="text-danger">*</span></label>
<input type="text" {{$isView ? 'disabled' : '' }} class="form-control" placeholder="Book Price" wire:model="price" />
@error('price') <span class="text-danger">{{ $message }}</span> @enderror
</div>
{{-- Book publisher --}}
<div class="form-group mb-3">
<label for="publisher">Publisher <span class="text-danger">*</span></label>
<input type="text" {{$isView ? 'disabled' : '' }} class="form-control" placeholder="Publisher Name" wire:model="publisher" />
@error('publisher') <span class="text-danger">{{ $message }}</span> @enderror
</div>
<div class="text-end">
<button type="button" wire:click="closeModal" data-bs-dismiss="modal" class="btn btn-secondary">Close</button>
{{-- If not view then only show the submit button --}}
@if (!$isView)
<button type="submit" class="btn btn-success">Save</button>
@endif
</div>
</form>
{{-- Form ends --}}
</div>
</div>
</div>
</div>
{{-- Modal ends --}}
Lastly, we have the delete-book modal for the confirmation. Hence, add the below snippet in that.
{{-- Delete Book Modal --}}
<div wire:ignore.self class="modal fade" id="deleteBookModal" tabindex="-1" aria-labelledby="deleteBookModalLabel"
aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title fw-bold" id="deleteBookModalLabel">Delete Book</h5>
<button type="button" wire:click="closeModal" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
{{-- Form starts --}}
<form wire:submit.prevent="destroyBook">
<div class="modal-body py-5">
<h5 class="ps-3">Are you sure you want to delete?</h5>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-success">Yes! Delete</button>
<button type="button" wire:click="closeModal" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
</div>
</form>
{{-- Form ends --}}
</div>
</div>
</div>
{{-- Delete Modal ends --}}
We are done with the user interface design part.
After that, let’s implement the Livewire CRUD functionality in Laravel.
Recommended: Advanced Eloquent Relation with hasManyThrough in Laravel 10
Step 6 – Implement Livewire CRUD Functionality in Laravel
The heart of our application lies in the Livewire component. In the component, you’ll have to implement the logic needed for the CRUD operation.
Hence, add the below functionality inside it for the Livewire CRUD operations.
<?php
namespace App\Http\Livewire;
use App\Models\Book;
use Livewire\Component;
class BookManager extends Component
{
public $books = [];
public $title, $author, $price, $publisher, $book = null, $isView = false, $isEdit = false;
public function render()
{
$this->books = Book::orderBy('id', 'DESC')->get();
return view('livewire.book-manager');
}
// Define Validation error rules
protected $rules = [
'title' => 'required|min:3',
'author' => 'required|string|max:50',
'price' => 'required|numeric',
'publisher' => 'required|string'
];
// Customize Validation error messages
protected $messages = [
'title.required' => 'Book title is required',
'title.min' => 'Book title must be atleast 3 chars',
'author.required' => 'Book author is required',
'author.max' => 'Book author must be max 50 chars',
'price.required' => 'Book price is required',
'price.numeric' => 'Book price allows number only',
'publisher.required' => 'Book publisher is required',
];
/**
* Function : Updated
* @description : For triggering Real Time Validation
*/
public function updated($inputs)
{
$this->validateOnly($inputs);
}
/**
* Function : Form Action Handling
* Description : used to handle two different action based on condtion
*/
public function saveBook()
{
if (!$this->isEdit) {
$this->storeBook();
} else {
$this->updateBook();
}
}
/**
* Function : Submit Book Details
* @param NA
* @return response
**/
public function storeBook()
{
$validatedData = $this->validate();
$book = Book::create($validatedData);
if ($book) {
session()->flash('success', 'New Book details added successfully!');
} else {
session()->flash('error', 'Unable to create Book details!');
}
$this->resetInputs();
// Dispatching browser event to close modal
$this->dispatchBrowserEvent('close-modal');
}
/**
* Function : Reset inputs
* @param NA
*/
private function resetInputs()
{
$this->title = null;
$this->author = null;
$this->price = null;
$this->publisher = null;
$this->isEdit = false;
$this->isView = false;
$this->book = null;
}
/**
* Function : Show Book
* @param Book $book // Route Model Binding
*/
public function showBook(Book $book)
{
if ($book) {
$this->title = $book->title;
$this->author = $book->author;
$this->price = $book->price;
$this->publisher = $book->publisher;
$this->isView = true;
} else {
return redirect()->route('books');
}
}
/**
* Function : Edit Book
* @param Book $book // Route Model Binding
*/
public function editBook(Book $book)
{
// If Book Exist then assign attributes to variables
if ($book) {
$this->book = $book;
$this->title = $book->title;
$this->author = $book->author;
$this->price = $book->price;
$this->publisher = $book->publisher;
$this->isEdit = true;
} else {
return redirect()->route('books');
}
}
/**
* Function : Update Book Details
* @param NA
* @return response
*/
public function updateBook()
{
// Validate book inputs data
$validatedData = $this->validate();
// If Book exist
if ($this->book) {
$this->book->title = $validatedData['title'];
$this->book->author = $validatedData['author'];
$this->book->price = $validatedData['price'];
$this->book->publisher = $validatedData['publisher'];
$this->book->save();
session()->flash('success', 'Book updated successfully!');
} else {
session()->flash('error', 'Unable to update. Book record not found!');
}
$this->isEdit = false;
// Dispatching browser event to close modal
$this->dispatchBrowserEvent('close-modal');
}
/**
* Function : Delete Book
* @description : This is for delete confirmation
* @param Book $book // Route model binding
*/
public function deleteBook(Book $book)
{
// Assign book model object to variable
$this->book = $book;
}
/**
* Function : Destroy Book Details
* @param NA
* @return response
*/
public function destroyBook()
{
// If book model
if ($this->book) {
$this->book->delete();
session()->flash('success', 'Book record deleted successfully!');
} else {
session()->flash('error', 'Unable to delete. Book record not found!');
}
// Dispatching browser event to close modal
$this->dispatchBrowserEvent('close-modal');
}
/**
* Function : Close Modal
*/
public function closeModal()
{
// calling reset inputs function
$this->resetInputs();
}
}
You are done with the Livewire CRUD functionality.
But now, you have to render the Livewire component in Laravel. Hence, you will require a Laravel blade file.
Recommended: Efficient Data Retrieval With hasOneThrough Relation in Laravel
Step 7 – Call Livewire Component in Laravel Blade
For rendering the Livewire component, you will have to create a blade file in the views folder. Hence, I have created it by giving the name “books.blade.php”.
After that, you will have to add the below snippet.
<!doctype html>
<html lang="en">
<head>
<title>CRUD App Using Livewire in Laravel 10</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
{{-- Bootstrap CSS v5.2.1 --}}
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.1/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-iYQeCzEYFbKjA/T2uDLTpkwGzCiq6soy8tYaI1GyVh/UjpbCx/TYkiZhlZB6+fzT" crossorigin="anonymous">
{{-- Livewire Styles --}}
@livewireStyles
</head>
<body>
<main class="pt-3 px-5">
<div class="container-fluid">
<h3 class="text-center fw-bold border-bottom pb-2">CRUD App Using Livewire in Laravel 10 </h3>
<div class="row justify-content-center mt-3">
{{-- Calling Livewire Component --}}
@livewire('book-manager')
</div>
</div>
</main>
{{-- jQuery CDN --}}
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
{{-- Bootstrap JavaScript Libraries --}}
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.6/dist/umd/popper.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.1/dist/js/bootstrap.min.js"></script>
{{-- Livewire Scripts --}}
@livewireScripts
</body>
</html>
Next, you will be required to render this view file. Either you can render it directly in the route or by creating a controller.
Here, we already created a controller with the model. Hence, we will go through the controller.
Recommended: Simplify Relation By Converting hasMany to hasOne in Laravel 10
Step 8 – Render Laravel View in Controller
So, look at the BookController.php file and add the below function.
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class BookController extends Controller
{
public function index() {
return view('books');
}
}
That’s it, now, you have to execute this function through the route. So, let’s create a route for this.
Step 9 – Wire Up Routes
Last, but not least, you will have to add a route for executing the Livewire CRUD operations.
<?php
use App\Http\Controllers\BookController;
use Illuminate\Support\Facades\Route;
Route::get('books', [BookController::class, 'index'])->name('books');
You are done with the routing. Hence, it’s time to run the application and check the result.
So, let’s serve our Livewire CRUD application.
php artisan serve
Your application has started so simply navigate to the URL and check the result.
Conclusion
Congratulations! As a result, you’ve successfully built a dynamic Livewire CRUD application in Laravel 10. This tutorial has provided you with the fundamental knowledge and practical steps to leverage the power of Laravel and Livewire for creating robust and interactive web applications.
Therefore, with Laravel and Livewire, you have the tools to develop dynamic web applications efficiently and elegantly. Continue exploring their documentation and consider enhancing your application with features like validation, authentication, and more. However, your journey in web development is just beginning, and the possibilities are endless. Happy coding!
Leave a Reply