A CRUD API allows you to interact with a database to perform these essential operations, enabling you to build dynamic and data-driven web applications. In this step-by-step tutorial guide, your will learn how to create a simple CRUD API using PHP along with MySQL database.
Step 1: Setup Your Database & Its Table
- Database name:
php_api
- Table name:
posts
- “posts” table columns:
id
title
content
author
created_at
updated_at
Use the following SQL code to create the posts
table and its columns.
CREATE TABLE `posts` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`title` varchar(100) NOT NULL,
`content` text NOT NULL,
`author` varchar(30) NOT NULL,
`created_at` date NOT NULL DEFAULT current_timestamp(),
`updated_at` date NOT NULL DEFAULT current_timestamp(),
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
Step 2: Create the Project Folder
Open your xampp htdocs
folder or your localhost www
directory and create a new folder called php-crud-api
for this crud api project.
Here is the structure of the php-crud-api
folder:
Step 3: Create the Classes
In the project folder create a folder called classes/
. All the classes will be inside the folder. Now we need to create three classes –
1. Database.php
– A class with a constructor method that establishes a connection to a MySQL database using PDO (PHP Data Objects).
<?php
class Database
{
// Replace the database details according to yours
private $db_host = 'localhost';
private $db_name = 'php_api';
private $db_username = 'root';
private $db_password = '';
// Constructor to establish a database connection
function __construct()
{
try {
// Define Data Source Name (DSN)
$dsn = "mysql:host={$this->db_host};dbname={$this->db_name};charset=utf8";
// Create a new PDO instance for database connection
$db_connection = new PDO($dsn, $this->db_username, $this->db_password);
// Set PDO attributes for error mode
$db_connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// Return the database connection
return $db_connection;
} catch (PDOException $e) {
// If connection fails, catch and display the error message
echo "Connection error " . $e->getMessage();
exit; // Exit the script upon connection failure
}
}
}
?>
2. Main.php
– This class contains several static methods for handling requests and generating JSON responses. Each method is commented to explain its purpose and functionality.
<?php
class Main
{
// Checks the Request Method
static function check($req)
{
// Check if the request method matches the provided method
if ($_SERVER["REQUEST_METHOD"] === $req) {
return true; // Return true if method matches
}
// If request method is not as expected, return error response
static::json(0, 405, "Invalid Request Method. HTTP method should be $req");
}
// Returns the response in JSON format
static function json(int $ok, $status, $msg, $key = false, $value = false)
{
// Initialize response array
$res = ["ok" => $ok];
// Set HTTP status code if provided
if ($status !== null) {
http_response_code($status);
$res["status"] = $status;
}
// Set message if provided
if ($msg !== null) {
$res["message"] = $msg;
}
// Set additional key-value pair if provided
if ($value) {
if ($key) {
$res[$key] = $value;
} else {
$res["data"] = $value;
}
}
// Output response as JSON and exit
echo json_encode($res);
exit;
}
// Returns the 404 Not found response
static function _404()
{
static::json(0, 404, "Not Found!");
}
}
3. Post.php
– A class that performs CRUD operations on a database table named posts. Each method is thoroughly commented to explain its purpose and functionality.
<?php
// Include necessary files
require_once __DIR__ . "/Database.php";
require_once __DIR__ . "/Main.php";
use Main as Response;
class Post extends Database
{
private $DB;
// Constructor to initialize database connection
function __construct()
{
$this->DB = Database::__construct();
}
// Function to filter input data
private function filter($data)
{
return htmlspecialchars(trim(htmlspecialchars_decode($data)), ENT_NOQUOTES);
}
// Create a new post
public function create(string $title, string $content, string $author)
{
// Sanitize input data
$title = $this->filter($title);
$content = $this->filter($content);
$author = $this->filter($author);
try {
// Prepare SQL statement
$sql = "INSERT INTO `posts` (`title`,`content`,`author`) VALUES (:title,:content,:author)";
$stmt = $this->DB->prepare($sql);
// Bind parameters
$stmt->bindParam(":title", $title, PDO::PARAM_STR);
$stmt->bindParam(":content", $content, PDO::PARAM_STR);
$stmt->bindParam(":author", $author, PDO::PARAM_STR);
// Execute query
$stmt->execute();
// Get the last inserted ID
$last_id = $this->DB->lastInsertId();
// Send JSON response
Response::json(1, 201, "Post has been created successfully", "post_id", $last_id);
} catch (PDOException $e) {
// Handle exception
Response::json(0, 500, $e->getMessage());
}
}
// Fetch all posts or Get a single post through the post ID
public function read($id = false, $return = false)
{
try {
// Construct SQL query
$sql = "SELECT * FROM `posts`";
// If post ID is provided
if ($id !== false) {
// Validate post ID
if (is_numeric($id)) {
$sql = "SELECT * FROM `posts` WHERE `id`='$id'";
} else {
// Return 404 response if invalid post ID
Response::_404();
}
}
// Execute query
$query = $this->DB->query($sql);
// If query returned results
if ($query->rowCount() > 0) {
$allPosts = $query->fetchAll(PDO::FETCH_ASSOC);
// If single post requested
if ($id !== false) {
// Return single post if $return flag is set
if ($return) return $allPosts[0];
Response::json(1, 200, null, "post", $allPosts[0]);
}
// Return multiple posts
Response::json(1, 200, null, "posts", $allPosts);
}
// If post ID does not exist
if ($id !== false) {
Response::_404();
}
// If no posts in the database
Response::json(1, 200, "Please Insert Some posts...", "posts", []);
} catch (PDOException $e) {
// Handle exception
Response::json(0, 500, $e->getMessage());
}
}
// Update an existing post
public function update(int $id, Object $data)
{
try {
// Check if post exists
$sql = "SELECT * FROM `posts` WHERE `id`='$id'";
$query = $this->DB->query($sql);
if ($query->rowCount() > 0) {
$the_post = $query->fetch(PDO::FETCH_OBJ);
// Sanitize and validate input data
$title = (isset($data->title) && !empty(trim($data->title))) ? $this->filter($data->title) : $the_post->title;
$content = (isset($data->body) && !empty(trim($data->body))) ? $this->filter($data->body) : $the_post->content;
$author = (isset($data->author) && !empty(trim($data->author))) ? $this->filter($data->author) : $the_post->author;
// Prepare update SQL statement
$update_sql = "UPDATE `posts` SET `title`=:title,`content`=:content,`author`=:author,`updated_at`=NOW() WHERE `id`='$id'";
$stmt = $this->DB->prepare($update_sql);
// Bind parameters
$stmt->bindParam(":title", $title, PDO::PARAM_STR);
$stmt->bindParam(":content", $content, PDO::PARAM_STR);
$stmt->bindParam(":author", $author, PDO::PARAM_STR);
// Execute update query
$stmt->execute();
// Send JSON response with updated post details
Response::json(1, 200, "Post Updated Successfully", "post", $this->read($id, true));
}
// If post does not exist
Response::json(0, 404, "Invalid Post ID.");
} catch (PDOException $e) {
// Handle exception
Response::json(0, 500, $e->getMessage());
}
}
// Delete a Post
public function delete(int $id)
{
try {
// Construct delete SQL query
$sql = "DELETE FROM `posts` WHERE `id`='$id'";
$query = $this->DB->query($sql);
// If deletion successful
if ($query->rowCount() > 0) {
// Send success response
Response::json(1, 200, "Post has been deleted successfully.");
}
// If post ID does not exist
Response::json(0, 404, "Invalid Post ID.");
} catch (PDOException $e) {
// Handle exception
Response::json(0, 500, $e->getMessage());
}
}
}
Step 4: Create Rest of the PHP Files
After creating the classes, we need to create rest of the PHP files – config.php
, create.php
, get.php
, update.php
, delete.php
. Let’s create one-by-one:
1. “config.php” the Configuration File
<?php
// Set default method to "GET" if $allow_method is not defined
if (!isset($allow_method)) $allow_method = "GET";
// Set headers for CORS (Cross-Origin Resource Sharing)
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Headers: access");
header("Access-Control-Allow-Methods: $allow_method");
header("Content-Type: application/json; charset=UTF-8");
header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, X-Requested-With");
// Include required files
require_once __DIR__ . "/classes/Main.php";
require_once __DIR__ . "/classes/Post.php";
2. “create.php” For Inserting New Posts
<?php
// Set the allowed method to "POST"
$allow_method = "POST";
// Include configuration file
require_once __DIR__ . "/config.php";
// Import necessary classes
use Main as Request;
use Main as Response;
// Check if the request method matches "POST"
if (Request::check("POST")) {
// Get JSON data from request body
$data = json_decode(file_get_contents("php://input"));
// Check if required fields are present
if (
!isset($data->title) ||
!isset($data->body) ||
!isset($data->author)
) :
// Define required fields
$fields = [
"title" => "Post title",
"body" => "Post content",
"author" => "Author name"
];
// Send error response if required fields are missing
Response::json(0, 400, "Please fill all the required fields", "fields", $fields);
// Check for empty fields
elseif (
empty(trim($data->title)) ||
empty(trim($data->body)) ||
empty(trim($data->author))
) :
// Identify empty fields
$empty_fields = [];
foreach ($data as $key => $val) {
if (empty(trim($val))) array_push($empty_fields, $key);
}
// Send error response if empty fields are detected
Response::json(0, 400, "Oops! empty field detected.", "empty_fields", $empty_fields);
else :
// Create a new Post object and call the create method
$Post = new Post();
$Post->create($data->title, $data->body, $data->author);
endif;
}
POST: http://localhost/php-crud-api/create.php
Body (application/json):
{
"title":"Post Title",
"body":"Post Body",
"author":"Author Name"
}
3. “get.php” Fetch Posts from the Database
<?php
// Include configuration file
require_once __DIR__ . "/config.php";
// Import necessary classes
use Main as Request;
// Check if the request method is "GET"
if (Request::check("GET")) {
// Create a new Post object
$Post = new Post();
// If an ID parameter is provided, fetch a single post
if (isset($_GET['id']))
$Post->read(trim($_GET['id']));
// Fetch all posts
$Post->read();
}
# Fetch all posts
GET: http://localhost/php-crud-api/get.php
# Fetch a single post
GET: http://localhost/php-crud-api/get.php?id={post_id}
4. “update.php” For Update an Existing Post
<?php
// Set the allowed method to "PUT"
$allow_method = "PUT";
// Include configuration file
require_once __DIR__ . "/config.php";
// Import necessary classes
use Main as Request;
use Main as Response;
// Check if the request method is "PUT"
if (Request::check("PUT")) {
// Get JSON data from request body
$data = json_decode(file_get_contents("php://input"));
// Define fields with their descriptions
$fields = [
"id" => "Post ID (Required)",
"title" => "Post title (Optional)",
"body" => "Post content (Optional)",
"author" => "Author name (Optional)"
];
// Check if the ID field is provided and valid
if (!isset($data->id) || !is_numeric($data->id)) :
// Send error response if ID is missing or invalid
Response::json(0, 400, "Please provide the valid Post ID and at least one field.", "fields", $fields);
endif;
// Check for empty fields
$isEmpty = true;
$empty_fields = [];
// Loop through data to check for empty fields
foreach((array)$data as $key => $val){
if (in_array($key, ["title","body","author"])){
if(!empty(trim($val))){
$isEmpty = false;
}
else{
array_push($empty_fields, $key);
}
}
}
// If all fields are empty or only the ID is provided, send error response
if($isEmpty){
$has_empty_fields = count($empty_fields);
Response::json(0, 400,
$has_empty_fields ? "Oops! empty field detected." : "Please provide the Post ID and at least one field.",
$has_empty_fields ? "empty_fields" : "fields",
$has_empty_fields ? $empty_fields : $fields);
}
// Create a new Post object and call the update method
$Post = new Post();
$Post->update($data->id, $data);
}
PUT: http://localhost/php-crud-api/update.php
Body (application/json):
{
"id": "post id",
"field_name (title | body | author)" : "New Value"
}
5. “delete.php” Delete an Existing Post
<?php
// Set the allowed method to "DELETE"
$allow_method = "DELETE";
// Include configuration file
require_once __DIR__ . "/config.php";
// Import necessary classes
use Main as Request;
use Main as Response;
// Check if the request method is "DELETE"
if (Request::check("DELETE")) {
// Get JSON data from request body
$data = json_decode(file_get_contents("php://input"));
// Check if the ID field is provided and valid
if (!isset($data->id) || !is_numeric($data->id)) :
// Send error response if ID is missing or invalid
Response::json(0, 400, "Please provide the valid Post ID");
endif;
// Create a new Post object and call the delete method
$Post = new Post();
$Post->delete($data->id);
}
DELETE: http://localhost/php-crud-api/delete.php
Body (application/json):
{
"id": "post id"
}
Congratulations! You have successfully created a basic CRUD API using PHP and MySQL. Download the PHP CRUD API source code from GitHub. Thank You.. 🙏🏻❤️❤️