
Laravel 12 AJAX CRUD Example : A Step-by-Step Guide
Table of Contents
Laravel 12
Laravel 12 has arrived, bringing a host of exciting updates that continue to refine and elevate the Laravel ecosystem. This release introduces new starter kits for React, Vue, and Livewire, making it easier than ever to kickstart modern web applications. Alongside these kits, Laravel 12 integrates WorkOS AuthKit for enhanced authentication, offering social login, passkeys, and SSO support. These features, combined with improved concurrency handling, validation security, and request merging, make Laravel 12 a powerhouse for developers.
Are you looking to create a dynamic Laravel 12 CRUD application that performs operations seamlessly without reloading the page? This step-by-step tutorial is perfect for beginners who want to integrate AJAX with Laravel for efficient data handling.
In this guide, you'll learn how to use Laravel 12 and jQuery AJAX to implement CRUD functionalities—allowing users to insert, fetch, edit, update, and delete records without page refresh. By the end of this tutorial, you will have a fully functional AJAX-powered Laravel CRUD application that ensures a better user experience and improved efficiency. Let’s get started!
Read Also: Laravel 12: What’s New and Why It Matters for Developers
Step 1: Setting Up Laravel 12 Project
Start by creating a new Laravel 12 project: create one with Composer:
composer create-project --prefer-dist laravel/laravel laravel12-ajax-crud
Navigate to the app directory by running the following command in your terminal
cd laravel12-ajax-crud
Step 2: Database Configuration
Open the .env file and update your database details:
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=<DATABASE NAME>
DB_USERNAME=<DATABASE USERNAME>
DB_PASSWORD=<DATABASE PASSWORD>
Ensure your database is created before proceeding.
Step 3: Generate a Model and Migration File
-
Generate a Model and Migration
Run the following Artisan command to create the Employee model and its corresponding migration file:
php artisan make:model Employee -m
-
Update the Employee Model
Define relationships and fillable fields in app/Models/Employee.php:
namespace App\Models; use Illuminate\Database\Eloquent\Model; class Employee extends Model { protected $table = 'employees'; protected $fillable = [ 'name', 'email', 'phone', ]; public function getCreatedAtAttribute($value) { return \Carbon\Carbon::parse($value)->format('d-m-Y'); // Customize as needed } }
-
Define the Migration Schema
Update the database/migrations/xxxx_xx_xx_create_employees_table.php file:
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('employees', function (Blueprint $table) { $table->id(); $table->string('name'); $table->string('email'); $table->string('phone'); $table->timestamps(); }); } /** * Reverse the migrations. */ public function down(): void { Schema::dropIfExists('employees'); } };
-
Run the Migration
Execute the migration to create the Employees table:
php artisan migrate
Step 4: Create a Controller for CRUD Operations
Generate a resource controller using the following Artisan command. This controller will handle the logic for Create, Read, Update, and Delete (CRUD) operations.
php artisan make:controller EmployeeController --resource
Define CRUD Methods in EmployeeController.php
Write methods in the controller to handle CRUD operations. Use AJAX to send and receive data asynchronously, ensuring a seamless user experience without page reloads.-
Index Method (Display Employee Page).
public function index() { return view('employee.index'); }
-
List Method (Fetch and filter employee data).
public function list(Request $request) { $employees = Employee::query(); if($request->search_by_key != null && $request->search_by_key_value!= null){ $employees->where($request->search_by_key, $request->search_by_key_value); }else if($request->dateFrom != null && $request->dateTo!= null){ $employees->whereBetween('created_at', [$request->dateFrom, $request->dateTo]); } $employees = $employees->orderBy("created_at", "desc")->get(); return response()->json(['message' => "Employee Fetched Successfully.", 'data' => $employees, 'status' => true],201); }
-
Store Method (Validate and create new employees).
public function store(Request $request) { $validator = Validator::make($request->all(), [ 'name'=> 'required|max:191', 'email'=>'required|email|max:191', 'phone'=>'required|max:10|min:10', ]); if($validator->fails()) { return response()->json([ 'status'=>400, 'errors'=>$validator->messages() ]); } else { $employee = new Employee; $employee->name = $request->input('name'); $employee->email = $request->input('email'); $employee->phone = $request->input('phone'); $employee->save(); return response()->json(['message' => "Employee Added Successfully.", 'data' => $employee, 'status' => true],201); } }
-
Edit Method (Fetch a single employee record for editing).
public function edit($id) { $employee = Employee::find($id); if($employee) { return response()->json(['message' => "Data Fetched", 'data' => $employee, 'status' => true],200); } else { return response()->json(['message' => "No Employee Found.", 'status' => false], 404); } }
-
Update Method (Validate and update employee details).
public function update(Request $request, $id) { $validator = Validator::make($request->all(), [ 'name'=> 'required|max:191', 'email'=>'required|email|max:191', 'phone'=>'required|max:10|min:10', ]); if($validator->fails()) { return response()->json([ 'status'=>400, 'errors'=>$validator->messages() ]); } else { $employee = Employee::find($id); if($employee) { $employee->name = $request->input('name'); $employee->email = $request->input('email'); $employee->phone = $request->input('phone'); $employee->update(); return response()->json(['message' => "Employee Updated Successfully.", 'data' => $employee, 'status' => true],200); } else { return response()->json(['message' => "No Employee Found.", 'status' => false], 404); } } }
-
Destroy Method (Delete an employee record).
public function destroy($id) { $employee = Employee::find($id); if($employee) { $employee->delete(); return response()->json(['message' => "Employee Deleted Successfully.", 'data' => $employee, 'status' => true],200); } else { return response()->json(['message' => "No Employee Found.", 'status' => false], 404); } }
Open the generated file EmployeeController.php and update it as follows:
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Employee;
use Illuminate\Support\Facades\Validator;
class EmployeeController extends Controller
{
public function index()
{
return view('employee.index');
}
public function list(Request $request)
{
$employees = Employee::query();
if($request->search_by_key != null && $request->search_by_key_value!= null){
$employees->where($request->search_by_key, $request->search_by_key_value);
}else if($request->dateFrom != null && $request->dateTo!= null){
$employees->whereBetween('created_at', [$request->dateFrom, $request->dateTo]);
}
$employees = $employees->orderBy("created_at", "desc")->get();
return response()->json(['message' => "Employee Fetched Successfully.", 'data' => $employees, 'status' => true],201);
}
public function store(Request $request)
{
$validator = Validator::make($request->all(), [
'name'=> 'required|max:191',
'email'=>'required|email|max:191',
'phone'=>'required|max:10|min:10',
]);
if($validator->fails())
{
return response()->json([
'status'=>400,
'errors'=>$validator->messages()
]);
}
else
{
$employee = new Employee;
$employee->name = $request->input('name');
$employee->email = $request->input('email');
$employee->phone = $request->input('phone');
$employee->save();
return response()->json(['message' => "Employee Added Successfully.", 'data' => $employee, 'status' => true],201);
}
}
public function edit($id)
{
$employee = Employee::find($id);
if($employee)
{
return response()->json(['message' => "Data Fetched", 'data' => $employee, 'status' => true],200);
}
else
{
return response()->json(['message' => "No Employee Found.", 'status' => false], 404);
}
}
public function update(Request $request, $id)
{
$validator = Validator::make($request->all(), [
'name'=> 'required|max:191',
'email'=>'required|email|max:191',
'phone'=>'required|max:10|min:10',
]);
if($validator->fails())
{
return response()->json([
'status'=>400,
'errors'=>$validator->messages()
]);
}
else
{
$employee = Employee::find($id);
if($employee)
{
$employee->name = $request->input('name');
$employee->email = $request->input('email');
$employee->phone = $request->input('phone');
$employee->update();
return response()->json(['message' => "Employee Updated Successfully.", 'data' => $employee, 'status' => true],200);
}
else
{
return response()->json(['message' => "No Employee Found.", 'status' => false], 404);
}
}
}
public function destroy($id)
{
$employee = Employee::find($id);
if($employee)
{
$employee->delete();
return response()->json(['message' => "Employee Deleted Successfully.", 'data' => $employee, 'status' => true],200);
}
else
{
return response()->json(['message' => "No Employee Found.", 'status' => false], 404);
}
}
}
Step 5: Design a Reusable Template Layout
Create a layout file:
php artisan make:view layouts.app
Customize resources/views/layouts/app.blade.php with Bootstrap and JavaScript assets.
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>Laravel 12 AJAX CRUD Example : A Step-by-Step Guide</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/daterangepicker/daterangepicker.css">
</head>
<body>
<div id="app">
<main class="py-4">
@yield('content')
</main>
</div>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script type="text/javascript" src="https://cdn.jsdelivr.net/momentjs/latest/moment.min.js"></script>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/daterangepicker/daterangepicker.min.js"></script>
@yield('script')
</body>
</html>
Step 6: Build a Blade View for the User Interface
Generate a Blade template for the CRUD interface:
Edit the index.blade.php file to design the UI for displaying the Employee Details.
@extends('layouts.app')
@section('content')
Laravel 12 CRUD Example with AJAX
Employee Data
ID
Name
Email
Phone
Date
Actions
@endsection
@section('scripts')
<script src="{{URL::asset('custom/js/employee.js?version=1')}}"></script>
@endsection
Step 7: Create a External JavaScript File for AJAX Handling
Create a JavaScript file at public/custom/js/employee.js to handle AJAX requests. This file will manage form submissions, data fetching, and dynamic UI updates without page reloads.
Open employee.js and add the following code for seamless AJAX functionality.
$(document).ready(function () {
console.log("employee js call");
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
}
});
let employeeFilter = {
search_by_key: null,
search_by_key_value: null,
dateFrom: null,
dateTo: null,
};
$('input[name="dateRangeInput"]').daterangepicker({
"autoApply": true,
"autoUpdateInput": false,
"locale": {
"format": "DD/MM/YYYY",
"separator": " - ",
"applyLabel": "Apply",
"cancelLabel": "Cancel",
"fromLabel": "From",
"toLabel": "To",
"customRangeLabel": "Custom",
},
"linkedCalendars": false,
"showCustomRangeLabel": false,
"startDate": moment(),
"endDate": moment(),
"maxDate": moment(),
"maxSpan": {
"days": 30
},
}, function (start, end, label, item) {
if (start && end) {
$('input[name="dateRangeInput"]').val(start.format('DD/MM/YYYY') + ' - ' + end.format('DD/MM/YYYY'));
} else {
$('input[name="dateRangeInput"]').val("");
}
});
loadEmployees();
function loadEmployees() {
$.ajax({
type: "POST",
url: "/employees/list",
data: employeeFilter,
dataType: "json",
success: function (response) {
console.log(response);
setHtmlData(response.data);
},
error: function (data) {
console.log('Error:', data);
setHtmlData([]);
}
});
}
function setHtmlData(data) {
let htmlData = "";
if (data.length > 0) {
data.forEach((item, index) => {
htmlData += `
${item.id}
${item.name}
${item.email}
${item.phone}
Create: ${item.created_at}
`;
});
}
if (htmlData.length > 0) {
$("#EmployeeTable").html(htmlData);
} else {
$("#EmployeeTable").html(" ");
}
}
$("#filterForm").submit((e) => {
console.log("filterForm");
employeeFilter.search_by_key = $('#search_key').val();
employeeFilter.search_by_key_value = $('#search_value').val();
let dateRangeInput = $('#dateRangeInput').val();
if (dateRangeInput) {
let splitDate = dateRangeInput.split(/-/);
employeeFilter.dateFrom = moment(splitDate[0], 'DD-MM-YYYY').format('DD-MM-YYYY');
employeeFilter.dateTo = moment(splitDate[1], 'DD-MM-YYYY').format('DD-MM-YYYY');
} else {
employeeFilter.dateFrom = null;
employeeFilter.dateTo = null;
}
console.log("employeeFilter", employeeFilter);
loadEmployees();
});
$(document).on('click', '#clearFilterForm', function (e) {
console.log("resetFilterForm");
employeeFilter.search_by_key = null;
employeeFilter.search_by_key_value = null;
employeeFilter.dateFrom = null;
employeeFilter.dateTo = null;
$("#filterForm")[0].reset();
loadEmployees();
});
$('#AddEmployeeForm').on('submit', function (e) {
console.log("AddEmployeeForm");
e.preventDefault();
var data = {
'name': $('.name').val(),
'email': $('.email').val(),
'phone': $('.phone').val(),
};
$.ajax({
type: "POST",
url: "/employees",
data: data,
dataType: "json",
success: function (response) {
if (response.status == 400) {
$('#createFromErrors').html("");
$('#createFromErrors').addClass('alert alert-danger');
$.each(response.errors, function (key, err_value) {
$('#createFromErrors').append('' + err_value + ' ');
});
} else {
$('#createFromErrors').html("");
$('#successMessage').addClass('alert alert-success');
$('#successMessage').text(response.message);
$('#AddEmployeeForm').trigger("reset");
$('#AddEmployeeModal').modal('hide');
loadEmployees();
}
},
error: function (error) {
console.log(error);
}
});
});
$(document).on('click', '.EditEmployee', function (e) {
e.preventDefault();
var EmployeeID = $(this).val();
$('#editModal').modal('show');
$.ajax({
type: "GET",
url: "/employees/edit/" + EmployeeID,
success: function (response) {
if (response.status == 404) {
$('#successMessage').addClass('alert alert-success');
$('#successMessage').text(response.message);
$('#editModal').modal('hide');
} else {
$('#edit_name').val(response.data.name);
$('#edit_email').val(response.data.email);
$('#edit_phone').val(response.data.phone);
$('#EmployeeID').val(EmployeeID);
}
},
error: function (error) {
console.log(error);
}
});
$('.btn-close').find('input').val('');
});
$('#EditEmployeeForm').on('submit', function (e) {
e.preventDefault();
var id = $('#EmployeeID').val();
var data = {
'name': $('#edit_name').val(),
'email': $('#edit_email').val(),
'phone': $('#edit_phone').val(),
}
$.ajax({
type: "PUT",
url: "/employees/update/" + id,
data: data,
dataType: "json",
success: function (response) {
// console.log(response);
if (response.status == 400) {
$('#updateFromErrors').html("");
$('#updateFromErrors').addClass('alert alert-danger');
$.each(response.errors, function (key, err_value) {
$('#updateFromErrors').append('' + err_value +
' ');
});
} else {
$('#updateFromErrors').html("");
$('#successMessage').addClass('alert alert-success');
$('#successMessage').text(response.message);
$('#editModal').find('input').val('');
$('#editModal').modal('hide');
loadEmployees();
}
}
});
});
$(document).on('click', '.DeleteEmployee', function () {
var EmployeeID = $(this).val();
$('#DeleteModal').modal('show');
$('#deleteingRecordID').val(EmployeeID);
});
$('#DeleteEmployeeForm').on('submit', function (e) {
e.preventDefault();
var id = $('#deleteingRecordID').val();
$.ajax({
type: "DELETE",
url: "/employees/delete/" + id,
dataType: "json",
success: function (response) {
if (response.status == 404) {
$('#successMessage').addClass('alert alert-success');
$('#successMessage').text(response.message);
} else {
$('#successMessage').html("");
$('#successMessage').addClass('alert alert-success');
$('#successMessage').text(response.message);
$('#DeleteModal').modal('hide');
loadEmployees();
}
}
});
});
});
Step 8: Define Routes and Run the Application
-
Add Route
Define the route in routes/web.php
use App\Http\Controllers\EmployeeController; Route::get('employees', [EmployeeController::class, 'index']); Route::post('employees', [EmployeeController::class, 'store']); Route::post('employees/list', [EmployeeController::class, 'list']); Route::get('employees/edit/{id}', [EmployeeController::class, 'edit']); Route::put('employees/update/{id}', [EmployeeController::class, 'update']); Route::delete('employees/delete/{id}', [EmployeeController::class, 'destroy']);
-
Run the Project
Start the development server:
php artisan serve
-
Preview the Application
Open your web browser and navigate to the following URL to view the code preview:
http://localhost:8000/employeesPreview:
Display Employee List with filters
Create new employee Modal
Edit/Update employee Modal
Delete employee Modal
Step 9: Test and Debug the Laravel CRUD Application
-
Test all CRUD operations:
- Add, edit, update, and delete employees.
- Ensure all actions work without page reloads.
- Debug any issues and optimize the application.
Conclusion
Congratulations! You have successfully built a Laravel 12 AJAX CRUD application. By integrating jQuery AJAX, you’ve enhanced the user experience by eliminating unnecessary page reloads. This approach makes your web application faster, more efficient, and user-friendly.
Keep experimenting and optimizing to take your Laravel skills to the next level!
Get the complete source code on GitHub: Click here to download Code
0 Comments