Laravel CRUD with jQuery Validation, DataTables & SweetAlert for Beginners

Building a Laravel CRUD (Create, Read, Update, Delete) application is a great way to get hands-on experience with backend and frontend integration. In this tutorial, we will enhance the user experience by adding jQuery Validation for client-side form checks, DataTables for dynamic and interactive data display, and SweetAlert for beautiful alert messages.
This guide is perfect for beginners who want to learn how to create a clean, responsive, and user-friendly Laravel app with modern UI components.
Step 1: Create a New Laravel Project
Open your terminal and run:
composer create-project --prefer-dist laravel/laravel laravel-crud-app
cd laravel-crud-app
Set up your database credentials in the .env file:
DB_DATABASE=your_database_name
DB_USERNAME=your_username
DB_PASSWORD=your_password
Follow these steps to create a Contact for with Laravel:
Step 2: Create a Model, Migration, and Controller
For this example, let’s create a simple Contact model:
php artisan make:model Contact -mcr
This creates:
Contact Model
Migration file
Resource Controller
ContactController
Migration Fields Example
migration file in database/migrations/xxxx_xx_xx_create_contacts_table.php
:
<?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('contacts', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->text('message');
$table->string('gender');
$table->string('interests'); // Store comma-separated interests
$table->string('country');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('contacts');
}
};
Then run:
php artisan migrate
Model :-
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Contact extends Model
{
use HasFactory;
protected $fillable = ['name', 'message', 'gender', 'interests', 'country'];
}
Step 3: Define Routes
Open routes/web.php and add:
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\ContactController;
/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider and all of them will
| be assigned to the "web" middleware group. Make something great!
|
*/
Route::get('contacts/fetch', [ContactController::class, 'fetch'])->name('contacts.fetch');
Route::resource('contacts', ContactController::class);
Step 4: Build the Controller Logic
In app/Http/Controllers/ContactController.php, implement CRUD methods. Here’s a simplified version:
<?php
namespace App\Http\Controllers;
use App\Models\Contact;
use Illuminate\Http\Request;
class ContactController extends Controller
{
public function welcome()
{
return view('layouts.app');
}
public function index()
{
return view('contacts.index');
}
public function fetch()
{
$contacts = Contact::all();
return response()->json(['data' => $contacts]);
}
public function create()
{
return view('contacts.create');
}
public function store(Request $request)
{
$validated = $request->validate([
'name' => 'required|string|max:255',
'message' => 'required|string',
'gender' => 'required|string',
'interests' => 'required|array',
'country' => 'required|string',
]);
$validated['interests'] = implode(',', $validated['interests']);
Contact::create($validated);
return redirect()->route('contacts.index')->with('success', 'Contact created successfully');
}
public function edit(Contact $contact)
{
$contact->interests = explode(',', $contact->interests);
return view('contacts.edit', compact('contact'));
}
public function update(Request $request, Contact $contact)
{
$validated = $request->validate([
'name' => 'required|string|max:255',
'message' => 'required|string',
'gender' => 'required|string',
'interests' => 'required|array',
'country' => 'required|string',
]);
$validated['interests'] = implode(',', $validated['interests']);
$contact->update($validated);
return redirect()->route('contacts.index')->with('success', 'Contact updated successfully');
}
public function destroy(Contact $contact)
{
$contact->delete();
return response()->json(['success' => true]);
}
}
Step 5: Create Blade Views with DataTables and jQuery Validation
Layout resources/views/layouts/app.blade.php
<!DOCTYPE html>
<html>
<head>
<title>@yield('title', 'Contacts App')</title>
<meta name="csrf-token" content="{{ csrf_token() }}">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" />
<link href="https://cdn.datatables.net/1.13.5/css/jquery.dataTables.min.css" rel="stylesheet" />
<link href="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/toastr.min.css" rel="stylesheet" />
@stack('styles')
</head>
<body>
<div class="container mt-5">
@yield('content')
</div>
<script src="https://code.jquery.com/jquery-3.7.0.min.js"></script>
<script src="http://ajax.aspnetcdn.com/ajax/jquery.validate/1.11.1/jquery.validate.min.js"></script>
<script src="https://cdn.datatables.net/1.13.5/js/jquery.dataTables.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/toastr.min.js"></script>
<script>
@if (session('success'))
toastr.success("{{ session('success') }}");
@endif
@if (session('error'))
toastr.error("{{ session('error') }}");
@endif
</script>
@yield('scripts')
</body>
</html>
Contacts List View resources/views/contacts/index.blade.php
@extends('layouts.app')
@section('title', 'Contacts List')
@section('content')
<h2>Contacts</h2>
<a href="{{ route('contacts.create') }}" class="btn btn-primary mb-3">Create Contact</a>
<table id="contactsTable" class="table table-bordered">
<thead>
<tr>
<th>Name</th>
<th>Message</th>
<th>Gender</th>
<th>Interests</th>
<th>Country</th>
<th>Actions</th>
</tr>
</thead>
<tbody></tbody>
</table>
@endsection
@section('scripts')
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<script>
$(document).ready(function() {
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
}
});
var table = $('#contactsTable').DataTable({
ajax: "{{ route('contacts.fetch') }}",
columns: [{
data: 'name'
},
{
data: 'message'
},
{
data: 'gender'
},
{
data: 'interests',
render: data => data.split(',').join(', ')
},
{
data: 'country'
},
{
data: 'id',
orderable: false,
searchable: false,
render: function(id) {
return `
<a href="/contacts/${id}/edit" class="btn btn-sm btn-info">Edit</a>
<button class="btn btn-sm btn-danger delete-btn" data-id="${id}">Delete</button>
`;
}
}
]
});
$('#contactsTable tbody').on('click', '.delete-btn', function() {
let id = $(this).data('id');
Swal.fire({
title: 'Are you sure?',
text: "You won't be able to revert this!",
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#3085d6',
cancelButtonColor: '#d33',
confirmButtonText: 'Yes, delete it!',
cancelButtonText: 'Cancel'
}).then((result) => {
if (result.isConfirmed) {
$.ajax({
url: `/contacts/${id}`,
method: 'DELETE',
success: function() {
toastr.success('Deleted successfully');
table.ajax.reload(null, false);
},
error: function() {
toastr.error('Delete failed');
}
});
}
});
});
});
</script>
@endsection
Create Contact View resources/views/contacts/create.blade.php
@extends('layouts.app')
@section('title', 'Create Contact')
@section('content')
<h2>Create Contact</h2>
<form method="POST" id="contactForm" action="{{ route('contacts.store') }}">
@csrf
<div class="mb-3">
<label>Name *</label>
<input type="text" name="name" class="form-control" value="{{ old('name') }}">
@error('name') <div class="text-danger">{{ $message }}</div> @enderror
</div>
<div class="mb-3">
<label>Message *</label>
<textarea name="message" class="form-control">{{ old('message') }}</textarea>
@error('message') <div class="text-danger">{{ $message }}</div> @enderror
</div>
<div class="mb-3">
<label>Gender *</label><br>
<div id="genderGroup">
<input type="radio" name="gender" value="male" {{ old('gender') == 'male' ? 'checked' : '' }}> Male
<input type="radio" name="gender" value="female" {{ old('gender') == 'female' ? 'checked' : '' }} class="ms-3"> Female
</div>
</div>
<div class="mb-3">
<label>Interests *</label><br>
<div id="interestsGroup">
<input type="checkbox" name="interests[]" value="coding" {{ is_array(old('interests')) && in_array('coding', old('interests')) ? 'checked' : '' }}> Coding
<input type="checkbox" name="interests[]" value="music" {{ is_array(old('interests')) && in_array('music', old('interests')) ? 'checked' : '' }} class="ms-3"> Music
<input type="checkbox" name="interests[]" value="sports" {{ is_array(old('interests')) && in_array('sports', old('interests')) ? 'checked' : '' }} class="ms-3"> Sports
</div>
</div>
<div class="mb-3">
<label>Country *</label>
<select name="country" class="form-select">
<option value="">Select Country</option>
<option value="India" {{ old('country') == 'India' ? 'selected' : '' }}>India</option>
<option value="USA" {{ old('country') == 'USA' ? 'selected' : '' }}>USA</option>
<option value="UK" {{ old('country') == 'UK' ? 'selected' : '' }}>UK</option>
</select>
@error('country') <div class="text-danger">{{ $message }}</div> @enderror
</div>
<button type="submit" class="btn btn-success">Save</button>
<a href="{{ route('contacts.index') }}" class="btn btn-secondary">Back</a>
</form>
@endsection
@section('scripts')
<script>
$(document).ready(function () {
$("#contactForm").validate({
rules: {
name: {
required: true,
maxlength: 255
},
message: {
required: true
},
gender: {
required: true
},
'interests[]': {
required: true
},
country: {
required: true
}
},
messages: {
name: {
required: "Please enter name",
maxlength: "Name cannot be longer than 255 characters"
},
message: "Please enter message",
gender: "Please select gender",
'interests[]': "Please select at least one interest",
country: "Please select country"
},
errorElement: 'div',
errorClass: 'text-danger',
highlight: function (element) {
$(element).addClass('is-invalid');
},
unhighlight: function (element) {
$(element).removeClass('is-invalid');
},
errorPlacement: function (error, element) {
if (element.attr("name") === "gender") {
error.insertAfter("#genderGroup");
} else if (element.attr("name") === "interests[]") {
error.insertAfter("#interestsGroup");
} else {
error.insertAfter(element);
}
}
});
});
</script>
@endsection
Edit Contact View resources/views/contacts/edit.blade.php
@extends('layouts.app')
@section('title', 'Edit Contact')
@section('content')
<h2>Edit Contact</h2>
<form method="POST" id="contactForm" action="{{ route('contacts.update', $contact->id) }}">
@csrf
@method('PUT')
<div class="mb-3">
<label>Name *</label>
<input type="text" name="name" class="form-control" value="{{ old('name', $contact->name) }}">
@error('name')
<div class="text-danger">{{ $message }}</div>
@enderror
</div>
<div class="mb-3">
<label>Message *</label>
<textarea name="message" class="form-control">{{ old('message', $contact->message) }}</textarea>
@error('message')
<div class="text-danger">{{ $message }}</div>
@enderror
</div>
<div class="mb-3">
<label>Gender *</label><br>
<div id="genderGroup">
<input type="radio" name="gender" value="male"
{{ old('gender', $contact->gender) == 'male' ? 'checked' : '' }}> Male
<input type="radio" name="gender" value="female"
{{ old('gender', $contact->gender) == 'female' ? 'checked' : '' }} class="ms-3"> Female
</div>
</div>
<div class="mb-3">
<label>Interests *</label><br>
@php
$selectedInterests = old('interests');
if (!is_array($selectedInterests)) {
// Check if $contact->interests is already array or string
if (is_string($contact->interests)) {
$selectedInterests = explode(',', $contact->interests);
} else {
// If already array or null, use it directly or empty array
$selectedInterests = is_array($contact->interests) ? $contact->interests : [];
}
}
@endphp
<div id="interestsGroup">
<input type="checkbox" name="interests[]" value="coding"
{{ in_array('coding', $selectedInterests) ? 'checked' : '' }}> Coding
<input type="checkbox" name="interests[]" value="music"
{{ in_array('music', $selectedInterests) ? 'checked' : '' }} class="ms-3"> Music
<input type="checkbox" name="interests[]" value="sports"
{{ in_array('sports', $selectedInterests) ? 'checked' : '' }} class="ms-3"> Sports
</div>
</div>
<div class="mb-3">
<label>Country *</label>
<select name="country" class="form-select">
<option value="">Select Country</option>
<option value="India" {{ old('country', $contact->country) == 'India' ? 'selected' : '' }}>India</option>
<option value="USA" {{ old('country', $contact->country) == 'USA' ? 'selected' : '' }}>USA</option>
<option value="UK" {{ old('country', $contact->country) == 'UK' ? 'selected' : '' }}>UK</option>
</select>
@error('country')
<div class="text-danger">{{ $message }}</div>
@enderror
</div>
<button type="submit" class="btn btn-success">Update</button>
<a href="{{ route('contacts.index') }}" class="btn btn-secondary">Back</a>
</form>
@endsection
@section('scripts')
{{-- jQuery Validation --}}
<script src="https://cdn.jsdelivr.net/npm/jquery-validation@1.19.5/dist/jquery.validate.min.js"></script>
<script>
$(document).ready(function() {
$("#contactForm").validate({
rules: {
name: {
required: true,
maxlength: 255
},
message: {
required: true
},
gender: {
required: true
},
'interests[]': {
required: true
},
country: {
required: true
}
},
messages: {
name: {
required: "Please enter name",
maxlength: "Name cannot be longer than 255 characters"
},
message: "Please enter message",
gender: "Please select gender",
'interests[]': "Please select at least one interest",
country: "Please select country"
},
errorElement: 'div',
errorClass: 'text-danger',
highlight: function(element) {
$(element).addClass('is-invalid');
},
unhighlight: function(element) {
$(element).removeClass('is-invalid');
},
errorPlacement: function(error, element) {
if (element.attr("name") === "gender") {
error.insertAfter("#genderGroup");
} else if (element.attr("name") === "interests[]") {
error.insertAfter("#interestsGroup");
} else {
error.insertAfter(element);
}
}
});
});
</script>
@endsection
In this tutorial, we built a simple Laravel CRUD application to manage contacts, including features like validation, listing with DataTables, and deleting with SweetAlert confirmation. You now have a solid foundation to create more complex applications using Laravel.
Feel free to customize the code to suit your needs and explore additional Laravel features like authentication, relationships, and API integration.
If you found this tutorial helpful, please share it with others and leave a comment below if you have any questions or suggestions. Happy coding! 🚀
Subscribe to my newsletter
Read articles from CodeWithSdCode directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

CodeWithSdCode
CodeWithSdCode
I’m SdCode, a passionate Laravel developer sharing simple tutorials and practical coding tips to help beginners and intermediate devs grow their skills and build great projects.