
Author
Sahil Kumar
Publihsed On
Aug 18, 2024
Updated On
Aug 18, 2024
Category
CodeIgniter
CRUD App Using CodeIgniter 4, Bootstrap 5, jQuery - Ajax
In this post, I'm showing you how to develop a complete CRUD (Create, Read, Update, Delete) Application with Image upload using CodeIgniter 4, Bootstrap 5, jQuery - Ajax, and SweetAlert 2 library.
I've used CodeIgniter 4 framework for the backend and I've used MySQL as Database.
Bootstrap 5 for designing the application.
jQuery for sending ajax requests to the server.
Sweet Alert library for displaying popup messages.
I'll show you how to develop this application step by step:
Step 1: (Installing CodeIgniter 4 Framework)
In the first step, we'll install CodeIgniter's latest version framework in our system. For this, we'll use composer to install. Just use the below command in your terminal to download and install the latest version of the CodeIgniter framework.
composer create-project codeigniter4/appstarter ci_crud_ajax
after the installation just opens the project directory ci_crud_ajax in your code editor and then first you have to rename the env file to .env and then follow the below codes:
CI_ENVIRONMENT = development
app.baseURL = 'http://localhost:8080/'
database.default.hostname = localhost
database.default.database = ci_crud_ajax
database.default.username = root
database.default.password =
database.default.DBDriver = MySQLi
just uncomment these lines and fill the values according to your database credentials.
After setting all the configurations just open the terminal and use the below command to run the project on the development server.
php spark serve
Step 2: (Creating Model, Controller & Migration)
Now in this step, we'll create a Model, Controller, and Migration for this project. So first I'll create a Migration for this application. Just use the below command to create a migration:
php spark make:migration create_posts_table
this command will create a migration in the location app/database/migrations/2021-12-21-134111_CreatePostsTable.php just open this file and use the below codes:
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class CreatePostsTable extends Migration {
public function up() {
$this->forge->addField([
'id' => [
'type' => 'INT',
'constraint' => 11,
'unsigned' => true,
'auto_increment' => true,
],
'title' => [
'type' => 'VARCHAR',
'constraint' => 255,
],
'category' => [
'type' => 'VARCHAR',
'constraint' => 255,
],
'body' => [
'type' => 'TEXT',
],
'image' => [
'type' => 'VARCHAR',
'constraint' => 255,
],
'created_at' => [
'type' => 'DATETIME',
'null' => true,
],
'updated_at' => [
'type' => 'DATETIME',
'null' => true,
],
]);
$this->forge->addKey('id', true);
$this->forge->createTable('posts');
}
public function down() {
$this->forge->dropTable('posts');
}
}
after creating migration we've to run another command in the terminal to migrate this migration in the database. So just use the below command to migrate:
php spark migrate
the above command will migrate the migration and create table posts in the database.
Now next we'll create a model for the application. To create a model just use the below command:
php spark make:model PostModel
the above command will create a model in the location app/models/PostModel.php just open this file and use the below codes:
<?php
namespace App\Models;
use CodeIgniter\Model;
class PostModel extends Model {
protected $DBGroup = 'default';
protected $table = 'posts';
protected $primaryKey = 'id';
protected $useAutoIncrement = true;
protected $insertID = 0;
protected $returnType = 'array';
protected $useSoftDeletes = false;
protected $protectFields = true;
protected $allowedFields = ['title', 'category', 'body', 'image', 'created_at'];
// Dates
protected $useTimestamps = false;
protected $dateFormat = 'datetime';
protected $createdField = 'created_at';
protected $updatedField = 'updated_at';
protected $deletedField = 'deleted_at';
// Validation
protected $validationRules = [];
protected $validationMessages = [];
protected $skipValidation = false;
protected $cleanValidationRules = true;
// Callbacks
protected $allowCallbacks = true;
protected $beforeInsert = [];
protected $afterInsert = [];
protected $beforeUpdate = [];
protected $afterUpdate = [];
protected $beforeFind = [];
protected $afterFind = [];
protected $beforeDelete = [];
protected $afterDelete = [];
}
Now next we'll create the controller for the application. Just use the below command to create a controller:
php spark make:controller PostController
the above command will create a controller file in the location app/controllers/PostController.php just open this file and use the below codes:
<?php
namespace App\Controllers;
use App\Controllers\BaseController;
class PostController extends BaseController {
public function index() {
return view('index');
}
// handle add new post ajax request
public function add() {
$file = $this->request->getFile('file');
$fileName = $file->getRandomName();
$data = [
'title' => $this->request->getPost('title'),
'category' => $this->request->getPost('category'),
'body' => $this->request->getPost('body'),
'image' => $fileName,
'created_at' => date('Y-m-d H:i:s')
];
$validation = ConfigServices::validation();
$validation->setRules([
'image' => 'uploaded[file]|max_size[file,1024]|is_image[file]|mime_in[file,image/jpg,image/jpeg,image/png]',
]);
if (!$validation->withRequest($this->request)->run()) {
return $this->response->setJSON([
'error' => true,
'message' => $validation->getErrors()
]);
} else {
$file->move('uploads/avatar', $fileName);
$postModel = new AppModelsPostModel();
$postModel->save($data);
return $this->response->setJSON([
'error' => false,
'message' => 'Successfully added new post!'
]);
}
}
// handle fetch all posts ajax request
public function fetch() {
$postModel = new AppModelsPostModel();
$posts = $postModel->findAll();
$data = '';
if ($posts) {
foreach ($posts as $post) {
$data .= '<div class="col-md-4">
<div class="card shadow-sm">
<a href="#" id="' . $post['id'] . '" data-bs-toggle="modal" data-bs-target="#detail_post_modal" class="post_detail_btn"><img src="uploads/avatar/' . $post['image'] . '" class="img-fluid card-img-top"></a>
<div class="card-body">
<div class="d-flex justify-content-between align-items-center">
<div class="card-title fs-5 fw-bold">' . $post['title'] . '</div>
<div class="badge bg-dark">' . $post['category'] . '</div>
</div>
<p>
' . substr($post['body'], 0, 80) . '...
</p>
</div>
<div class="card-footer d-flex justify-content-between align-items-center">
<div class="fst-italic">' . date('d F Y', strtotime($post['created_at'])) . '</div>
<div>
<a href="#" id="' . $post['id'] . '" data-bs-toggle="modal" data-bs-target="#edit_post_modal" class="btn btn-success btn-sm post_edit_btn">Edit</a>
<a href="#" id="' . $post['id'] . '" class="btn btn-danger btn-sm post_delete_btn">Delete</a>
</div>
</div>
</div>
</div>';
}
return $this->response->setJSON([
'error' => false,
'message' => $data
]);
} else {
return $this->response->setJSON([
'error' => false,
'message' => '<div class="text-secondary text-center fw-bold my-5">No posts found in the database!</div>'
]);
}
}
// handle edit post ajax request
public function edit($id = null) {
$postModel = new AppModelsPostModel();
$post = $postModel->find($id);
return $this->response->setJSON([
'error' => false,
'message' => $post
]);
}
// handle update post ajax request
public function update() {
$id = $this->request->getPost('id');
$file = $this->request->getFile('file');
$fileName = $file->getFilename();
if ($fileName != '') {
$fileName = $file->getRandomName();
$file->move('uploads/avatar', $fileName);
if ($this->request->getPost('old_image') != '') {
unlink('uploads/avatar/' . $this->request->getPost('old_image'));
}
} else {
$fileName = $this->request->getPost('old_image');
}
$data = [
'title' => $this->request->getPost('title'),
'category' => $this->request->getPost('category'),
'body' => $this->request->getPost('body'),
'image' => $fileName,
'updated_at' => date('Y-m-d H:i:s')
];
$postModel = new AppModelsPostModel();
$postModel->update($id, $data);
return $this->response->setJSON([
'error' => false,
'message' => 'Successfully updated post!'
]);
}
// handle delete post ajax request
public function delete($id = null) {
$postModel = new AppModelsPostModel();
$post = $postModel->find($id);
$postModel->delete($id);
unlink('uploads/avatar/' . $post['image']);
return $this->response->setJSON([
'error' => false,
'message' => 'Successfully deleted post!'
]);
}
// handle fetch post detail ajax request
public function detail($id = null) {
$postModel = new AppModelsPostModel();
$post = $postModel->find($id);
return $this->response->setJSON([
'error' => false,
'message' => $post
]);
}
}
Step 3: (Creating View)
In this step, we'll create a view for our application. So just inside app/views create a new file index.php and just use the below codes:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CRUD App Using CI 4 and Ajax</title>
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
</head>
<body>
<!-- add new post modal start -->
<div class="modal fade" id="add_post_modal" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" aria-labelledby="staticBackdropLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="staticBackdropLabel">Add New Post</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<form action="#" method="POST" enctype="multipart/form-data" id="add_post_form" novalidate>
<div class="modal-body p-5">
<div class="mb-3">
<label>Post Title</label>
<input type="text" name="title" class="form-control" placeholder="Title" required>
<div class="invalid-feedback">Post title is required!</div>
</div>
<div class="mb-3">
<label>Post Category</label>
<input type="text" name="category" class="form-control" placeholder="Category" required>
<div class="invalid-feedback">Post category is required!</div>
</div>
<div class="mb-3">
<label>Post Body</label>
<textarea name="body" class="form-control" rows="4" placeholder="Body" required></textarea>
<div class="invalid-feedback">Post body is required!</div>
</div>
<div class="mb-3">
<label>Post Image</label>
<input type="file" name="file" id="image" class="form-control" required>
<div class="invalid-feedback">Post image is required!</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
<button type="submit" class="btn btn-primary" id="add_post_btn">Add Post</button>
</div>
</form>
</div>
</div>
</div>
<!-- add new post modal end -->
<!-- edit post modal start -->
<div class="modal fade" id="edit_post_modal" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" aria-labelledby="staticBackdropLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="staticBackdropLabel">Edit Post</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<form action="#" method="POST" enctype="multipart/form-data" id="edit_post_form" novalidate>
<input type="hidden" name="id" id="pid">
<input type="hidden" name="old_image" id="old_image">
<div class="modal-body p-5">
<div class="mb-3">
<label>Post Title</label>
<input type="text" name="title" id="title" class="form-control" placeholder="Title" required>
<div class="invalid-feedback">Post title is required!</div>
</div>
<div class="mb-3">
<label>Post Category</label>
<input type="text" name="category" id="category" class="form-control" placeholder="Category" required>
<div class="invalid-feedback">Post category is required!</div>
</div>
<div class="mb-3">
<label>Post Body</label>
<textarea name="body" class="form-control" rows="4" id="body" placeholder="Body" required></textarea>
<div class="invalid-feedback">Post body is required!</div>
</div>
<div class="mb-3">
<label>Post Image</label>
<input type="file" name="file" class="form-control">
<div class="invalid-feedback">Post image is required!</div>
<div id="post_image"></div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
<button type="submit" class="btn btn-primary" id="edit_post_btn">Update Post</button>
</div>
</form>
</div>
</div>
</div>
<!-- edit post modal end -->
<!-- detail post modal start -->
<div class="modal fade" id="detail_post_modal" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" aria-labelledby="staticBackdropLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="staticBackdropLabel">Details of Post</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<img src="" id="detail_post_image" class="img-fluid">
<h3 id="detail_post_title" class="mt-3"></h3>
<h5 id="detail_post_category"></h5>
<p id="detail_post_body"></p>
<p id="detail_post_created" class="fst-italic"></p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
<!-- detail post modal end -->
<div class="container">
<div class="row my-4">
<div class="col-lg-12">
<div class="card shadow">
<div class="card-header d-flex justify-content-between align-items-center">
<div class="text-secondary fw-bold fs-3">All Posts</div>
<button class="btn btn-dark" data-bs-toggle="modal" data-bs-target="#add_post_modal">Add New Post</button>
</div>
<div class="card-body">
<div class="row" id="show_posts">
<h1 class="text-center text-secondary my-5">Posts Loading..</h1>
</div>
</div>
</div>
</div>
</div>
</div>
<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
<script src="//cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<script>
$(function() {
// add new post ajax request
$("#add_post_form").submit(function(e) {
e.preventDefault();
const formData = new FormData(this);
if (!this.checkValidity()) {
e.preventDefault();
$(this).addClass('was-validated');
} else {
$("#add_post_btn").text("Adding...");
$.ajax({
url: '<?= base_url('post/add') ?>',
method: 'post',
data: formData,
contentType: false,
cache: false,
processData: false,
dataType: 'json',
success: function(response) {
if (response.error) {
$("#image").addClass('is-invalid');
$("#image").next().text(response.message.image);
} else {
$("#add_post_modal").modal('hide');
$("#add_post_form")[0].reset();
$("#image").removeClass('is-invalid');
$("#image").next().text('');
$("#add_post_form").removeClass('was-validated');
Swal.fire(
'Added',
response.message,
'success'
);
fetchAllPosts();
}
$("#add_post_btn").text("Add Post");
}
});
}
});
// edit post ajax request
$(document).delegate('.post_edit_btn', 'click', function(e) {
e.preventDefault();
const id = $(this).attr('id');
$.ajax({
url: '<?= base_url('post/edit/') ?>/' + id,
method: 'get',
success: function(response) {
$("#pid").val(response.message.id);
$("#old_image").val(response.message.image);
$("#title").val(response.message.title);
$("#category").val(response.message.category);
$("#body").val(response.message.body);
$("#post_image").html('<img src="<?= base_url('uploads/avatar/') ?>/' + response.message.image + '" class="img-fluid mt-2 img-thumbnail" width="150">');
}
});
});
// update post ajax request
$("#edit_post_form").submit(function(e) {
e.preventDefault();
const formData = new FormData(this);
if (!this.checkValidity()) {
e.preventDefault();
$(this).addClass('was-validated');
} else {
$("#edit_post_btn").text("Updating...");
$.ajax({
url: '<?= base_url('post/update') ?>',
method: 'post',
data: formData,
contentType: false,
cache: false,
processData: false,
dataType: 'json',
success: function(response) {
$("#edit_post_modal").modal('hide');
Swal.fire(
'Updated',
response.message,
'success'
);
fetchAllPosts();
$("#edit_post_btn").text("Update Post");
}
});
}
});
// delete post ajax request
$(document).delegate('.post_delete_btn', 'click', function(e) {
e.preventDefault();
const id = $(this).attr('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!'
}).then((result) => {
if (result.isConfirmed) {
$.ajax({
url: '<?= base_url('post/delete/') ?>/' + id,
method: 'get',
success: function(response) {
Swal.fire(
'Deleted!',
response.message,
'success'
)
fetchAllPosts();
}
});
}
})
});
// post detail ajax request
$(document).delegate('.post_detail_btn', 'click', function(e) {
e.preventDefault();
const id = $(this).attr('id');
$.ajax({
url: '<?= base_url('post/detail/') ?>/' + id,
method: 'get',
dataType: 'json',
success: function(response) {
$("#detail_post_image").attr('src', '<?= base_url('uploads/avatar/') ?>/' + response.message.image);
$("#detail_post_title").text(response.message.title);
$("#detail_post_category").text(response.message.category);
$("#detail_post_body").text(response.message.body);
$("#detail_post_created").text(response.message.created_at);
}
});
});
// fetch all posts ajax request
fetchAllPosts();
function fetchAllPosts() {
$.ajax({
url: '<?= base_url('post/fetch') ?>',
method: 'get',
success: function(response) {
$("#show_posts").html(response.message);
}
});
}
});
</script>
</body>
</html>
in the above index.php file I've designed the application using Bootstrap 5 and I've also used jQuery codes to send Ajax requests to the controller.
Step 4: (Working on Routes)
In this step, we'll define routes in the file app/Config/Routes.php file. Just open this file and use the below codes:
<?php
namespace Config;
// Create a new instance of our RouteCollection class.
$routes = Services::routes();
// Load the system's routing file first, so that the app and ENVIRONMENT
// can override as needed.
if (file_exists(SYSTEMPATH . 'Config/Routes.php')) {
require SYSTEMPATH . 'Config/Routes.php';
}
/*
* --------------------------------------------------------------------
* Router Setup
* --------------------------------------------------------------------
*/
$routes->setDefaultNamespace('App\Controllers');
$routes->setDefaultController('PostController');
$routes->setDefaultMethod('index');
$routes->setTranslateURIDashes(false);
$routes->set404Override();
$routes->setAutoRoute(false);
/*
* --------------------------------------------------------------------
* Route Definitions
* --------------------------------------------------------------------
*/
// We get a performance increase by specifying the default
// route since we don't have to scan directories.
$routes->get('/', 'PostController::index');
$routes->post('post/add', 'PostController::add');
$routes->get('post/fetch', 'PostController::fetch');
$routes->get('post/edit/(:num)', 'PostController::edit/$1');
$routes->get('post/delete/(:num)', 'PostController::delete/$1');
$routes->get('post/detail/(:num)', 'PostController::detail/$1');
$routes->post('post/update', 'PostController::update');
/*
* --------------------------------------------------------------------
* Additional Routing
* --------------------------------------------------------------------
*
* There will often be times that you need additional routing and you
* need it to be able to override any defaults in this file. Environment
* based routes is one such time. require() additional route files here
* to make that happen.
*
* You will have access to the $routes object within that file without
* needing to reload it.
*/
if (file_exists(APPPATH . 'Config/' . ENVIRONMENT . '/Routes.php')) {
require APPPATH . 'Config/' . ENVIRONMENT . '/Routes.php';
}
Now done! your application is completed now.
Step 5: (Some Screenshots of Application)





Sahil Kumar
Full Stack Web Developer
Hello! I'm a part-time blogger & YouTuber living in India. This is my personal blog where I write Web Design & Development tutorial posts!
Know more about me
SUBMIT GUEST POST 🚀
Ready to contribute? Head to the article dashboard to publish your own insightful articles and share them with our audience!
Go to dashboardRECENT POSTS

Laravel Filament and Livewire
2 months ago

Building a Secure Authentication System with PHP OOP, PDO, MySQL, and Bootstrap 5
7 months ago

10 Essential Web Design Principles for Creating a User-Friendly Website
7 months ago

Send Mail with Contact Form Using PHPMailer & Gmail SMTP | 2023
7 months ago

Full Stack Laravel 10 & React JS | SPA Authentication | Part 2
7 months ago
POSTS TAGS
- #crud app using codeigniter 4
- #codeIgniter crud application
- #codeIgniter 4 ajax crud app
- #jquery ajax & codeIgniter 4 crud app
- #crud app using ajax php
- #codeIgniter 4
- #ci crud app
- #crud application ci 4
- #codeIgniter 4 framework
- #codeIgniter 4 image upload
- #codeIgniter 4 file upload
- #codeIgniter 4 file validation
- #crud application using bootstrap 5 ajax ci
- #ci 4 crud application using ajax
- #ajax and codeigniter 4 crud app