In this tutorial we’ll be creating a complete Create, Read, Update, and Delete application with PHP, PDO, and MySQL using jQuery Ajax and Bootstrap. We’ll be creating the app completely from scratch, no additional frameworks required.
A CRUD app is often used in conjunction with a database, interacting with records in a table. We’ll be using MySQL as our database management system in our app.
We’ll create a database with a players table, we’ll be able to manipulate these players in our CRUD application, the players table will contain name, emails, phone numbers and profile photo.
1. Getting Started
1.1. What You Will Learn in this Tutorial
- Create MySQL Records — Insert new records into the players table.
- Read MySQL Records — Reading MySQL records and display them in an HTML table.
- Update MySQL Records — Update existing MySQL records in the players table.
- Delete MySQL Records — Confirm and delete records from the players table.
- GET and POST Requests — Send data to our app from an HTML form and URL parameters.
- Prepared Statements — Secure our SQL statements with prepared statements.
1.2. Requirements
- Web Server — I recommend you download and install XAMPP on your local computer system, this server package includes MySQL, PHP, phpMyAdmin, and the PDO extension.
- PHP — I recommend you use the latest version of PHP, but older versions should work just fine (skip if you installed XAMPP).
- PDO Extension — Should be enabled by default if you’re using XAMPP, but if it’s not you’ll need to enable/install it.
1.3. File Structure & Setup
Navigate to C:\xampp\htdocs (XAMPP) and create the below directories and files.
What each file will contain:
- index.php — Home page for our CRUD app.
- ajax.php — used for ajax request
- form.php — used for player add/edit form.
- profile.php — to show the player profile
- playerstable.php — to display the players list
- includes/Database.php — Database connection class file.
- includes/Player.php — Player model file (common functions).
- css/style.css — The stylesheet for our app, this will change the appearance of our app.
- js/script.js — jQuery Ajax related or common javascript functions.
2. Creating the Database and setting-up Tables
The MySQL database we’ll use to store players and retrieve them with PHP. If you’re using XAMPP follow the below instructions.
- Navigate to http://localhost/phpmyadmin/
- Click Databases at the top
- Under Create database input playersdb and select utf8_general_ci as the collation
- Click Create
- Select the newly created database
- Click the SQL tab and execute the below SQL:
CREATE TABLE `players` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`pname` varchar(50) NOT NULL,
`email` varchar(100) NOT NULL,
`phone` varchar(15) NOT NULL,
`photo` varchar(100) NOT NULL,
`status` enum('1','0') NOT NULL DEFAULT '1',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8
The above SQL will create the table: players, we’ll be using this table in our app.
There are 6 columns in the players table (id, pname, email, phone, photo, and status).
In phpMyAdmin, the database should look like the following:
3. Creating the Stylesheet
The stylesheet will change the appearance of our app, edit the css/style.css file and add the following code:
.container {
padding-top: 10px;
}
.rounded {
width: 100px;
}
.img-thumbnail {
width: 80px !important;
}
.message {
display: none;
}
#overlay {
background: #ffffff;
color: #666666;
position: fixed;
height: 100%;
width: 100%;
z-index: 5000;
top: 0;
left: 0;
float: left;
text-align: center;
padding-top: 25%;
opacity: 0.8;
}
4. Creating the CRUD Application
We can finally start to code the CRUD app with PHP. Before we get started, make sure you followed the previous steps and have the MySQL database ready.
4.1. Creating the Database Connection
Edit the includes/Database.php file and add the following code:
<?php
class Database
{
private $dbServer = 'localhost';
private $dbUser = 'root';
private $dbPassword = '';
private $dbName = 'playersdb';
protected $conn;
public function __construct()
{
try {
$dsn = "mysql:host={$this->dbServer}; dbname={$this->dbName}; charset=utf8";
$options = array(PDO::ATTR_PERSISTENT);
$this->conn = new PDO($dsn, $this->dbUser, $this->dbPassword, $options);
} catch (PDOException $e) {
echo "Connection Error: " . $e->getMessage();
}
}
}
4.2 Creating Player Class (Model) for Add, Read, Edit, Delete.
Edit the includes/Player.php file and add the following code:
<?php
require_once 'Database.php';
class Player extends Database
{
// table name
protected $tableName = 'players';
/**
* function is used to add record
* @param array $data
* @return int $lastInsertedId
*/
public function add($data)
{
if (!empty($data)) {
$fileds = $placholders = [];
foreach ($data as $field => $value) {
$fileds[] = $field;
$placholders[] = ":{$field}";
}
}
$sql = "INSERT INTO {$this->tableName} (" . implode(',', $fileds) . ") VALUES (" . implode(',', $placholders) . ")";
$stmt = $this->conn->prepare($sql);
try {
$this->conn->beginTransaction();
$stmt->execute($data);
$lastInsertedId = $this->conn->lastInsertId();
$this->conn->commit();
return $lastInsertedId;
} catch (PDOException $e) {
echo "Error: " . $e->getMessage();
$this->conn->rollback();
}
}
/**
* function is used to get single record based on the column value
* @param string $fileds
* @param any $value
* @return array $results
*/
public function getRow($field, $value)
{
$sql = "SELECT * FROM {$this->tableName} WHERE {$field}=:{$field}";
$stmt = $this->conn->prepare($sql);
$stmt->execute([":{$field}" => $value]);
if ($stmt->rowCount() > 0) {
$result = $stmt->fetch(PDO::FETCH_ASSOC);
} else {
$result = [];
}
return $result;
}
/**
* funciton is used to upload file
* @param array $file
* @return string $newFileName
*/
public function uploadPhoto($file)
{
if (!empty($file)) {
$fileTempPath = $file['tmp_name'];
$fileName = $file['name'];
$fileSize = $file['size'];
$fileType = $file['type'];
$fileNameCmps = explode('.', $fileName);
$fileExtension = strtolower(end($fileNameCmps));
$newFileName = md5(time() . $fileName) . '.' . $fileExtension;
$allowedExtn = ["jpg", "png", "gif", "jpeg"];
if (in_array($fileExtension, $allowedExtn)) {
$uploadFileDir = getcwd() . '/uploads/';
$destFilePath = $uploadFileDir . $newFileName;
if (move_uploaded_file($fileTempPath, $destFilePath)) {
return $newFileName;
}
}
}
}
}
4.1 Creating Home Page
Edit the form.php file and add the following code:
<!-- add/edit form modal -->
<div class="modal fade" id="userModal" tabindex="-1" role="dialog" aria-labelledby="userModalLabel"
aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Add/Edit User <i class="fa fa-user-circle-o"
aria-hidden="true"></i></h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<form id="addform" method="POST" enctype="multipart/form-data">
<div class="modal-body">
<div class="form-group">
<label for="recipient-name" class="col-form-label">Name:</label>
<div class="input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text" id="basic-addon1"><i class="fa fa-user-circle-o"
aria-hidden="true"></i>
</div>
<input type="text" class="form-control" id="username" name="username" required="required">
</div>
</div>
<div class="form-group">
<label for="message-text" class="col-form-label">Email:</label>
<div class="input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text" id="basic-addon1"><i class="fa fa-envelope-o"
aria-hidden="true"></i></span>
</div>
<input type="email" class="form-control" id="email" name="email" required="required">
</div>
</div>
<div class="form-group">
<label for="message-text" class="col-form-label">Phone:</label>
<div class="input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text" id="basic-addon1"><i class="fa fa-phone"
aria-hidden="true"></i></span>
</div>
<input type="phone" class="form-control" id="phone" name="phone" required="required" maxLength="10"
minLength="10">
</div>
</div>
<div class="form-group">
<label for="message-text" class="col-form-label">Photo:</label>
<div class="input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text" id="inputGroupFileAddon01"><i class="fa fa-picture-o"
aria-hidden="true"></i></span>
</div>
<div class="custom-file">
<input type="file" class="custom-file-input" name="photo" id="userphoto">
<label class="custom-file-label" for="userphoto">Choose file</label>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-danger" data-dismiss="modal">Close</button>
<button type="submit" class="btn btn-success" id="addButton">Submit</button>
<input type="hidden" name="action" value="adduser">
<input type="hidden" name="userid" id="userid" value="">
</div>
</form>
</div>
</div>
</div>
<!-- add/edit form modal end -->
Edit the profile.php file and add the following code:
<!-- profile modal start -->
<div class="modal fade" id="userViewModal" tabindex="-1" role="dialog" aria-labelledby="userViewModalLabel"
aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Profile <i class="fa fa-user-circle-o"
aria-hidden="true"></i></h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<div class="container" id="profile">
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
</div>
</form>
</div>
</div>
</div>
<!-- profile modal end -->
Edit the playerstable.php file and add the following code:
<!-- table -->
<table class="table" id="userstable">
<thead>
<tr>
<th scope="col"></th>
<th scope="col">Name</th>
<th scope="col">Email</th>
<th scope="col">Phone</th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
</tbody>
</table>
<!-- table -->
Edit the index.php file and add the following code:
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>PHP CRUD Application Using jQuery Ajax</title>
<link rel="stylesheet" href="css/style.css">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk" crossorigin="anonymous">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" integrity="sha384-wvfXpqpZZVQGK6TAh5PVlGOfQNHSoD2xbE+QkPxCAFlNEevoEH3Sl0sibVcOQVnN" crossorigin="anonymous">
<div class="container">
<div class="alert alert alert-primary" role="alert">
<h4 class="text-primary text-center">PHP CRUD Application Using jQuery Ajax</h4>
</div>
<div class="alert alert-success text-center message" role="alert">
</div>
<!--?php
include_once 'form.php';
include_once 'profile.php';
?-->
<div class="row mb-3">
<div class="col-3">
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#userModal" id="addnewbtn">Add New <i class="fa fa-user-circle-o"></i></button>
</div>
<div class="col-9">
<div class="input-group input-group-lg">
<div class="input-group-prepend">
<span class="input-group-text" id="basic-addon2"><i class="fa fa-search" aria-hidden="true"></i></span>
</div>
<input type="text" class="form-control" aria-label="Sizing example input" aria-describedby="inputGroup-sizing-lg" placeholder="Search..." id="searchinput">
</div>
</div>
</div>
<!--?php
include_once 'playerstable.php';
?-->
<nav id="pagination">
</nav>
<input type="hidden" name="currentpage" id="currentpage" value="1">
</div>
<div>
<!-- JS, Popper.js, and jQuery -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js"></script>
<script src="js/script.js"></script>
</div>
<div id="overlay" style="display:none;">
<div class="spinner-border text-danger" style="width: 3rem; height: 3rem;"></div>
<br>
Loading...
</div>
Edit the ajax.php file and add the following code:
<?php
$action = $_REQUEST['action'];
if (!empty($action)) {
require_once 'includes/Player.php';
$obj = new Player();
}
if ($action == 'adduser' && !empty($_POST)) {
$pname = $_POST['username'];
$email = $_POST['email'];
$phone = $_POST['phone'];
$photo = $_FILES['photo'];
$playerId = (!empty($_POST['userid'])) ? $_POST['userid'] : '';
// file (photo) upload
$imagename = '';
if (!empty($photo['name'])) {
$imagename = $obj->uploadPhoto($photo);
$playerData = [
'pname' => $pname,
'email' => $email,
'phone' => $phone,
'photo' => $imagename,
];
} else {
$playerData = [
'pname' => $pname,
'email' => $email,
'phone' => $phone,
];
}
if ($playerId) {
$obj->update($playerData, $playerId);
} else {
$playerId = $obj->add($playerData);
}
if (!empty($playerId)) {
$player = $obj->getRow('id', $playerId);
echo json_encode($player);
exit();
}
}
if ($action == "getusers") {
$page = (!empty($_GET['page'])) ? $_GET['page'] : 1;
$limit = 4;
$start = ($page - 1) * $limit;
$players = $obj->getRows($start, $limit);
if (!empty($players)) {
$playerslist = $players;
} else {
$playerslist = [];
}
$total = $obj->getCount();
$playerArr = ['count' => $total, 'players' => $playerslist];
echo json_encode($playerArr);
exit();
}
if ($action == "getuser") {
$playerId = (!empty($_GET['id'])) ? $_GET['id'] : '';
if (!empty($playerId)) {
$player = $obj->getRow('id', $playerId);
echo json_encode($player);
exit();
}
}
if ($action == "deleteuser") {
$playerId = (!empty($_GET['id'])) ? $_GET['id'] : '';
if (!empty($playerId)) {
$isDeleted = $obj->deleteRow($playerId);
if ($isDeleted) {
$message = ['deleted' => 1];
} else {
$message = ['deleted' => 0];
}
echo json_encode($message);
exit();
}
}
if ($action == 'search') {
$queryString = (!empty($_GET['searchQuery'])) ? trim($_GET['searchQuery']) : '';
$results = $obj->searchPlayer($queryString);
echo json_encode($results);
exit();
}
Edit the js/script.js file and add the following code:
// get pagination
function pagination(totalpages, currentpage) {
var pagelist = "";
if (totalpages > 1) {
currentpage = parseInt(currentpage);
pagelist += `<ul class="pagination justify-content-center">`;
const prevClass = currentpage == 1 ? " disabled" : "";
pagelist += `<li class="page-item${prevClass}"><a class="page-link" href="#" data-page="${
currentpage - 1
}">Previous</a></li>`;
for (let p = 1; p <= totalpages; p++) {
const activeClass = currentpage == p ? " active" : "";
pagelist += `<li class="page-item${activeClass}"><a class="page-link" href="#" data-page="${p}">${p}</a></li>`;
}
const nextClass = currentpage == totalpages ? " disabled" : "";
pagelist += `<li class="page-item${nextClass}"><a class="page-link" href="#" data-page="${
currentpage + 1
}">Next</a></li>`;
pagelist += `</ul>`;
}
$("#pagination").html(pagelist);
}
// get player row
function getplayerrow(player) {
var playerRow = "";
if (player) {
const userphoto = player.photo ? player.photo : "default.png";
playerRow = `<tr>
<td class="align-middle"><img src="uploads/${userphoto}" class="img-thumbnail rounded float-left"></td>
<td class="align-middle">${player.pname}</td>
<td class="align-middle">${player.email}</td>
<td class="align-middle">${player.phone}</td>
<td class="align-middle">
<a href="#" class="btn btn-success mr-3 profile" data-toggle="modal" data-target="#userViewModal"
title="Prfile" data-id="${player.id}"><i class="fa fa-address-card-o" aria-hidden="true"></i></a>
<a href="#" class="btn btn-warning mr-3 edituser" data-toggle="modal" data-target="#userModal"
title="Edit" data-id="${player.id}"><i class="fa fa-pencil-square-o fa-lg"></i></a>
<a href="#" class="btn btn-danger deleteuser" data-userid="14" title="Delete" data-id="${player.id}"><i
class="fa fa-trash-o fa-lg"></i></a>
</td>
</tr>`;
}
return playerRow;
}
// get players list
function getplayers() {
var pageno = $("#currentpage").val();
$.ajax({
url: "/phpcrudajax/ajax.php",
type: "GET",
dataType: "json",
data: { page: pageno, action: "getusers" },
beforeSend: function () {
$("#overlay").fadeIn();
},
success: function (rows) {
console.log(rows);
if (rows.players) {
var playerslist = "";
$.each(rows.players, function (index, player) {
playerslist += getplayerrow(player);
});
$("#userstable tbody").html(playerslist);
let totalPlayers = rows.count;
let totalpages = Math.ceil(parseInt(totalPlayers) / 4);
const currentpage = $("#currentpage").val();
pagination(totalpages, currentpage);
$("#overlay").fadeOut();
}
},
error: function () {
console.log("something went wrong");
},
});
}
$(document).ready(function () {
// add/edit user
$(document).on("submit", "#addform", function (event) {
event.preventDefault();
var alertmsg =
$("#userid").val().length > 0
? "Player has been updated Successfully!"
: "New Player has been added Successfully!";
$.ajax({
url: "/phpcrudajax/ajax.php",
type: "POST",
dataType: "json",
data: new FormData(this),
processData: false,
contentType: false,
beforeSend: function () {
$("#overlay").fadeIn();
},
success: function (response) {
console.log(response);
if (response) {
$("#userModal").modal("hide");
$("#addform")[0].reset();
$(".message").html(alertmsg).fadeIn().delay(3000).fadeOut();
getplayers();
$("#overlay").fadeOut();
}
},
error: function () {
console.log("Oops! Something went wrong!");
},
});
});
// pagination
$(document).on("click", "ul.pagination li a", function (e) {
e.preventDefault();
var $this = $(this);
const pagenum = $this.data("page");
$("#currentpage").val(pagenum);
getplayers();
$this.parent().siblings().removeClass("active");
$this.parent().addClass("active");
});
// form reset on new button
$("#addnewbtn").on("click", function () {
$("#addform")[0].reset();
$("#userid").val("");
});
// get user
$(document).on("click", "a.edituser", function () {
var pid = $(this).data("id");
$.ajax({
url: "/phpcrudajax/ajax.php",
type: "GET",
dataType: "json",
data: { id: pid, action: "getuser" },
beforeSend: function () {
$("#overlay").fadeIn();
},
success: function (player) {
if (player) {
$("#username").val(player.pname);
$("#email").val(player.email);
$("#phone").val(player.phone);
$("#userid").val(player.id);
}
$("#overlay").fadeOut();
},
error: function () {
console.log("something went wrong");
},
});
});
// delete user
$(document).on("click", "a.deleteuser", function (e) {
e.preventDefault();
var pid = $(this).data("id");
if (confirm("Are you sure want to delete this?")) {
$.ajax({
url: "/phpcrudajax/ajax.php",
type: "GET",
dataType: "json",
data: { id: pid, action: "deleteuser" },
beforeSend: function () {
$("#overlay").fadeIn();
},
success: function (res) {
if (res.deleted == 1) {
$(".message")
.html("Player has been deleted successfully!")
.fadeIn()
.delay(3000)
.fadeOut();
getplayers();
$("#overlay").fadeOut();
}
},
error: function () {
console.log("something went wrong");
},
});
}
});
// get profile
$(document).on("click", "a.profile", function () {
var pid = $(this).data("id");
$.ajax({
url: "/phpcrudajax/ajax.php",
type: "GET",
dataType: "json",
data: { id: pid, action: "getuser" },
success: function (player) {
if (player) {
const userphoto = player.photo ? player.photo : "default.png";
const profile = `<div class="row">
<div class="col-sm-6 col-md-4">
<img src="uploads/${userphoto}" class="rounded responsive" />
</div>
<div class="col-sm-6 col-md-8">
<h4 class="text-primary">${player.pname}</h4>
<p class="text-secondary">
<i class="fa fa-envelope-o" aria-hidden="true"></i> ${player.email}
<br />
<i class="fa fa-phone" aria-hidden="true"></i> ${player.phone}
</p>
</div>
</div>`;
$("#profile").html(profile);
}
},
error: function () {
console.log("something went wrong");
},
});
});
// searching
$("#searchinput").on("keyup", function () {
const searchText = $(this).val();
if (searchText.length > 1) {
$.ajax({
url: "/phpcrudajax/ajax.php",
type: "GET",
dataType: "json",
data: { searchQuery: searchText, action: "search" },
success: function (players) {
if (players) {
var playerslist = "";
$.each(players, function (index, player) {
playerslist += getplayerrow(player);
});
$("#userstable tbody").html(playerslist);
$("#pagination").hide();
}
},
error: function () {
console.log("something went wrong");
},
});
} else {
getplayers();
$("#pagination").show();
}
});
// load players
getplayers();
});
10 replies on “PHP PDO CRUD with Ajax jQuery and Bootstrap”
Hi, thank you for sharing such a good tutorial.
I am using sample PHP MVC, and I am beginner if you could help to develop such data table using Model, View and Controller.
Thanks in advanced.
Hello, carefully copied but after start up the loading wheel keeps turning and thats all
Hi, at the INDEX.PHP comment this code:
Loading …
This way:
<!–
Carregando …
–>
And insert one register. After this, you delete the comments at the code and the program plays.
PHP PDO CRUD with Ajax jQuery and Bootstrap this code is open source or its copyrighted code? Can i use this code for Commercial use
Yes, you can use.
Brother it’s not working… just loding & loding… please solve this issue… i use for my own work..
It’s not working Sir, please help…the index page keeps on loading for hours…even I have changed the directory name but it’s still not working….
Delete the ID=”OVERLAY” (INDEX PHP) and insert one register. After this you can put the ID overlay one more time at the code. Try this. Good luck.
The best crud example I have ever got. Advanced yet simple.
It’s not working Sir, please help…the index page keeps on loading for hours…even I have changed the directory name but it’s still not working….