PHP + React JS + MySQL CRUD Example

Hello everyone, today we will learn how to develop a full-stack web application that is a basic User Management Application using PHP, React JS and MySQL.
The GitHub repository link is provided at the end of this tutorial. You can download the source code.

After completing this tutorial what we will build? 

We will build a full-stack web application that is a basic User Management Application with CRUD features:
• Create User
• List User
• Update User 
• Delete User

-Add User:


-Retrieve all Users:

-View User:

-Update User:

We divided this tutorial into two parts. 

PART 1 - Rest APIs Development using PHP and MySQL
PART 2 - UI development using React JS

PART 1 - Rest APIs Development using PHP

Project Directory



Setting Up Database

Create Database "user_db" and create table "user"

--
-- Database: `user_db`
--
-- --------------------------------------------------------
--
-- Table structure for table `user`
--

CREATE TABLE `user` (
`id` bigint(20) UNSIGNED NOT NULL,
`first_name` varchar(50) NOT NULL,
`last_name` varchar(50) NOT NULL,
`email_id` varchar(50) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

ALTER TABLE `user`
ADD UNIQUE KEY `id` (`id`);

ALTER TABLE `user`
MODIFY `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=70;
COMMIT;


Database Connection [database.php]

<?php
class DB {
private $host = "localhost";
private $db = "user_db";
private $username = "root";
private $password = "";

public $conn;

public function getConnection(){
$this->conn = null;
try{
$this->conn = new PDO("mysql:host=" . $this->host .
";dbname=" . $this->db, $this->username, $this->password);
$this->conn->exec("set names utf8");
}catch(PDOException $exception){
echo "Database not connected: " . $exception->getMessage();
}
return $this->conn;
}
}
?>


Create User Class[user.php]

<?php
class User{

// conn
private $conn;

// table
private $dbTable = "user";

// col
public $id;
public $first_name;
public $last_name;
public $email_id;
// db conn
public function __construct($db){
$this->conn = $db;
}

// GET Users
public function getUsers(){
$sqlQuery = "SELECT id, first_name, last_name, email_id
FROM " . $this->dbTable . "";
$stmt = $this->conn->prepare($sqlQuery);
$stmt->execute();
return $stmt;
}

// CREATE User
public function createUser(){
$sqlQuery = "INSERT INTO
". $this->dbTable ."
SET
first_name = :first_name,
last_name = :last_name,
email_id = :email_id";
$stmt = $this->conn->prepare($sqlQuery);
// sanitize
$this->first_name=htmlspecialchars(strip_tags($this->first_name));
$this->last_name=htmlspecialchars(strip_tags($this->last_name));
$this->email_id=htmlspecialchars(strip_tags($this->email_id));
// bind data
$stmt->bindParam(":first_name", $this->first_name);
$stmt->bindParam(":last_name", $this->last_name);
$stmt->bindParam(":email_id", $this->email_id);
if($stmt->execute()){
return true;
}
return false;
}

// GET User
public function getSingleUser(){
$sqlQuery = "SELECT
id,
first_name,
last_name,
email_id
FROM
". $this->dbTable ."
WHERE
id = ?
LIMIT 0,1";

$stmt = $this->conn->prepare($sqlQuery);
$stmt->bindParam(1, $this->id);
$stmt->execute();
$dataRow = $stmt->fetch(PDO::FETCH_ASSOC);
$this->first_name = $dataRow['first_name'];
$this->last_name = $dataRow['last_name'];
$this->email_id = $dataRow['email_id'];
}

// UPDATE User
public function updateUser(){
$sqlQuery = "UPDATE
". $this->dbTable ."
SET
first_name = :first_name,
last_name = :last_name,
email_id = :email_id
WHERE
id = :id";
$stmt = $this->conn->prepare($sqlQuery);
$this->first_name=htmlspecialchars(strip_tags($this->first_name));
$this->last_name=htmlspecialchars(strip_tags($this->last_name));
$this->email_id=htmlspecialchars(strip_tags($this->email_id));
$this->id=htmlspecialchars(strip_tags($this->id));
// bind data
$stmt->bindParam(":first_name", $this->first_name);
$stmt->bindParam(":last_name", $this->last_name);
$stmt->bindParam(":email_id", $this->email_id);
$stmt->bindParam(":id", $this->id);
if($stmt->execute()){
return true;
}
return false;
}

// DELETE User
function deleteUser(){
$sqlQuery = "DELETE FROM " . $this->dbTable . " WHERE id = ?";
$stmt = $this->conn->prepare($sqlQuery);
$this->id=htmlspecialchars(strip_tags($this->id));
$stmt->bindParam(1, $this->id);
if($stmt->execute()){
return true;
}
return false;
}

}
?>


Create User [create.php]

<?php
header("Access-Control-Allow-Origin: *");
header("Content-Type: application/json;");
header("Access-Control-Allow-Methods: POST");
header("Access-Control-Max-Age: 3600");
header("Access-Control-Allow-Headers: Content-Type,
Access-Control-Allow-Headers, Authorization, X-Requested-With");

include_once 'config/database.php';
include_once 'class/user.php';

$database = new DB();
$db = $database->getConnection();

$item = new User($db);

$data = json_decode(file_get_contents("php://input"));

$item->first_name = $data->first_name;
$item->last_name = $data->last_name;
$item->email_id = $data->email_id;
if($item->createUser()){
echo json_encode("User created.");
} else{
echo json_encode("Failed to create user.");
}
?>



Get User Records [read.php]

<?php
header("Access-Control-Allow-Origin: *");
header("Content-Type: application/json;");
include_once 'config/database.php';
include_once 'class/user.php';

$database = new DB();
$db = $database->getConnection();

$items = new User($db);

$stmt = $items->getUsers();
$itemCount = $stmt->rowCount();

if($itemCount > 0){
$userArr = array();

while ($row = $stmt->fetch(PDO::FETCH_ASSOC)){
extract($row);
$e = array(
"id" => $id,
"first_name" => $first_name,
"last_name" => $last_name,
"email_id" => $email_id
);

array_push($userArr, $e);
}
echo json_encode($userArr);
}
else{
echo json_encode();
}

?>


Get User By Id [single_user.php]

<?php
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Headers: X-Requested-With,
Content-Type, Origin, Cache-Control, Pragma, Authorization,
Accept, Accept-Encoding");
header("Content-Type: application/json;");
include_once 'config/database.php';
include_once 'class/user.php';

$database = new DB();
$db = $database->getConnection();

$item = new User($db);

$item->id = isset($_GET['id']) ? $_GET['id'] : die();
$item->getSingleUser();

if($item != null){
$user_Arr = array(
"id" => $item->id,
"first_name" => $item->first_name,
"last_name" => $item->last_name,
"email_id" => $item->email_id
);
http_response_code(200);
echo json_encode($user_Arr);
}
else{
http_response_code(404);
echo json_encode("User record not found.");
}
?>



Update User [update.php]

<?php
header("Access-Control-Allow-Origin: *");
header("Content-Type: application/json; charset=UTF-8");
header("Access-Control-Allow-Methods: GET,POST,PUT,DELETE");
header("Access-Control-Max-Age: 3600");
header("Access-Control-Allow-Headers: Content-Type,
Access-Control-Allow-Headers, Authorization, X-Requested-With");

include_once 'config/database.php';
include_once 'class/user.php';
$database = new DB();
$db = $database->getConnection();
$item = new User($db);
$data = json_decode(file_get_contents("php://input"));
$item->id = $data->id;
$item->first_name = $data->first_name;
$item->last_name = $data->last_name;
$item->email_id = $data->email_id;
if($item->updateUser()){
echo json_encode("User record updated.");
} else{
echo json_encode("User record could not be updated.");
}
?>


Delete User [delete.php]

<?php

header("Access-Control-Allow-Origin: *");
header("Content-Type: application/json; charset=UTF-8");
header("Access-Control-Allow-Methods: GET,POST,PUT,DELETE");
header("Access-Control-Max-Age: 3600");
header("Access-Control-Allow-Headers: Content-Type,
Access-Control-Allow-Headers, Authorization, X-Requested-With");
include_once 'config/database.php';
include_once 'class/user.php';
$database = new DB();
$db = $database->getConnection();
$item = new User($db);
$item->id = isset($_GET['id']) ? $_GET['id'] : die();
if($item->deleteUser()){
echo json_encode("User deleted.");
} else{
echo json_encode("Not deleted");
}
?>




PART 2 - UI Development using React.js

Project Structure



package.json

It is used by the npm CLI (and yarn) to identify your project and understand how to handle the project's dependencies. It's the package. json file that enables npm to start your project, run scripts, install dependencies, publish to the NPM registry, and many other useful tasks.
{
"name": "react-frontend",
"version": "0.1.0",
"private": true,
"dependencies": {
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.5.0",
"@testing-library/user-event": "^7.2.1",
"axios": "^0.19.2",
"bootstrap": "^4.5.0",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"react-router-dom": "^5.2.0",
"react-scripts": "3.4.1"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": "react-app"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}



CreateUserComponent.jsx


import React, { Component } from 'react'
import UserService from '../services/UserService';
class CreateUserComponent extends Component {
constructor(props) {
super(props)
this.state = {
// step 2
id: this.props.match.params.id,
first_name: '',
last_name: '',
email_id: ''
}
this.changeFirstNameHandler = this.changeFirstNameHandler.bind(this);
this.changeLastNameHandler = this.changeLastNameHandler.bind(this);
this.saveOrUpdateUser = this.saveOrUpdateUser.bind(this);
}
// step 3
componentDidMount() {
// step 4
if (this.state.id === '_add') {
return
} else {
UserService.getUserById(this.state.id).then((res) => {
let user = res.data;
this.setState({
first_name: user.first_name,
last_name: user.last_name,
email_id: user.email_id
});
});
}
}
saveOrUpdateUser = (e) => {
e.preventDefault();
let user = {
id: this.state.id,
first_name: this.state.first_name,
last_name: this.state.last_name,
email_id: this.state.email_id
};
console.log('user => ' + JSON.stringify(user));
// step 5
if (this.state.id === '_add') {
UserService.createUser(user).then(res => {
this.props.history.push('/users');
});
} else {
UserService.updateUser(user).then(res => {
this.props.history.push('/users');
});
}
}
changeFirstNameHandler = (event) => {
this.setState({ first_name: event.target.value });
}
changeLastNameHandler = (event) => {
this.setState({ last_name: event.target.value });
}
changeEmailHandler = (event) => {
this.setState({ email_id: event.target.value });
}
cancel() {
this.props.history.push('/users');
}
getTitle() {
if (this.state.id === '_add') {
return <h3 className="text-center">Add User</h3>
} else {
return <h3 className="text-center">Update User</h3>
}
}
render() {
return (
<div>
<br></br>
<div className="container">
<div className="row">
<div className="card col-md-6 offset-md-3 offset-md-3">
{
this.getTitle()
}
<div className="card-body">
<form>
<div className="form-group">
<label> First Name: </label>
<input placeholder="First Name" name="first_name" className="form-control"
value={this.state.first_name} onChange={this.changeFirstNameHandler} />
</div>
<div className="form-group">
<label> Last Name: </label>
<input placeholder="Last Name" name="last_name" className="form-control"
value={this.state.last_name} onChange={this.changeLastNameHandler} />
</div>
<div className="form-group">
<label> Email Id: </label>
<input placeholder="Email Address" name="email_id" className="form-control"
value={this.state.email_id} onChange={this.changeEmailHandler} />
</div>
<button className="btn btn-success" onClick={this.saveOrUpdateUser}>
Save</button>
<button className="btn btn-danger" onClick={this.cancel.bind(this)} style=
{{ marginLeft: "10px" }}>Cancel</button>
</form>
</div>
</div>
</div>
</div>
</div>
)
}
}
export default CreateUserComponent




ListUserComponent.jsx

import React, { Component } from 'react'
import UserService from '../services/UserService'
class ListUserComponent extends Component {
constructor(props) {
super(props)
this.state = {
users: []
}
this.addUser = this.addUser.bind(this);
this.editUser = this.editUser.bind(this);
this.deleteUser = this.deleteUser.bind(this);
}
deleteUser(id) {
UserService.deleteUser(id).then(res => {
this.setState({ users: this.state.users.filter(user =>
user.id !== id) });
});
}
viewUser(id) {
this.props.history.push(`/view-user/${id}`);
}
editUser(id) {
this.props.history.push(`/add-user/${id}`);
}
componentDidMount() {
UserService.getUsers().then((res) => {
this.setState({ users: res.data });
});
}
addUser() {
this.props.history.push('/add-user/_add');
}
render() {
return (
<div>
<h2 className="text-center">Users List</h2>
<div className="row">
<button className="btn btn-primary" onClick={this.addUser}>
Add User</button>
</div>
<br></br>
<div className="row">
<table className="table table-striped table-bordered">
<thead>
<tr>
<th> User First Name</th>
<th> User Last Name</th>
<th> User Email Id</th>
<th> Actions</th>
</tr>
</thead>
<tbody>
{
this.state.users.map(
user =>
<tr key={user.id}>
<td> {user.first_name} </td>
<td> {user.last_name}</td>
<td> {user.email_id}</td>
<td>
<button onClick={() => this.editUser(user.id)} className="btn btn-warning">
Update </button>
<button style={{ marginLeft: "10px" }} onClick=
{() => this.deleteUser(user.id)} className="btn btn-danger">Delete </button>
<button style={{ marginLeft: "10px" }} onClick=
{() => this.viewUser(user.id)} className="btn btn-info">View </button>
</td>
</tr>
)
}
</tbody>
</table>
</div>
</div>
)
}
}
export default ListUserComponent


ViewUserComponent.jsx

import React, { Component } from 'react'
import UserService from '../services/UserService'
class ViewUserComponent extends Component {
constructor(props) {
super(props)
this.state = {
id: this.props.match.params.id,
user: {}
}
}
componentDidMount() {
UserService.getUserById(this.state.id).then(res => {
this.setState({ user: res.data });
})
}
render() {
return (
<div>
<br></br>
<div className="card col-md-6 offset-md-3">
<h3 className="text-center"> View User Details</h3>
<div className="card-body">
<div className="row">
<label> User First Name: </label>
<div> {this.state.user.first_name}</div>
</div>
<div className="row">
<label> User Last Name: </label>
<div> {this.state.user.last_name}</div>
</div>
<div className="row">
<label> User Email ID: </label>
<div> {this.state.user.email_id}</div>
</div>
</div>
</div>
</div>
)
}
}
export default ViewUserComponent


UserService.js

import axios from 'axios';

const USER_API_URL = "http://localhost:8080/php-mysql-crud-api";

class UserService {

getUsers(){
return axios.get(`${USER_API_URL}/read.php`);
}

createUser(user){
return axios.post(`${USER_API_URL}/create.php`, user);
}

getUserById(id){
return axios.get(`${USER_API_URL}/single_user.php`,
{ params: { id: id } });
}

updateUser(user){
return axios.put(`${USER_API_URL}/update.php`, user);
}

deleteUser(id){
return axios.delete(`${USER_API_URL}/delete.php`,
{ params: { id: id } });
}
}

export default new UserService()


App.css

.App {
text-align: center;
}

.App-logo {
height: 40vmin;
pointer-events: none;
}

@media (prefers-reduced-motion: no-preference) {
.App-logo {
animation: App-logo-spin infinite 20s linear;
}
}

.App-header {
background-color: #282c34;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
}

.App-link {
color: #61dafb;
}

@keyframes App-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}

.footer {
position: absolute;
bottom: 0;
width:100%;
height: 50px;
background-color: black;
text-align: center;
color: white;
}



App.js

import React from 'react';
import './App.css';
import {BrowserRouter as Router, Route, Switch} from 'react-router-dom'
import ListUserComponent from './components/ListUserComponent';
import HeaderComponent from './components/HeaderComponent';
import FooterComponent from './components/FooterComponent';
import CreateUserComponent from './components/CreateUserComponent';
import ViewUserComponent from './components/ViewUserComponent';

function App() {
return (
<div>
<Router>
<HeaderComponent />
<div className="container">
<Switch>
<Route path = "/" exact component = {ListUserComponent}></Route>
<Route path = "/users" component = {ListUserComponent}></Route>
<Route path = "/add-user/:id" component = {CreateUserComponent}>
</Route>
<Route path = "/view-user/:id" component = {ViewUserComponent}>
</Route>
</Switch>
</div>
<FooterComponent />
</Router>
</div>
);
}

export default App;



index.css

body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI',
'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans',
'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}

code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}



index.js

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import 'bootstrap/dist/css/bootstrap.min.css';

ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);

serviceWorker.unregister();

Download the complete source code - click here


Run


npm install 


 npm start





Download the source code

Popular posts from this blog

Learn Java 8 streams with an example - print odd/even numbers from Array and List

Java Stream API - How to convert List of objects to another List of objects using Java streams?

Registration and Login with Spring Boot + Spring Security + Thymeleaf

Java, Spring Boot Mini Project - Library Management System - Download

ReactJS, Spring Boot JWT Authentication Example

Spring Boot + Mockito simple application with 100% code coverage

Top 5 Java ORM tools - 2024

Java - Blowfish Encryption and decryption Example

Spring boot video streaming example-HTML5

Google Cloud Storage + Spring Boot - File Upload, Download, and Delete