PHP + Angular + MySQL CRUD Example

In this section, we will learn how to develop a full-stack web application that is a basic User Management Application using PHP, Angular 10 and MySQL. 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

User Interface

-Add User:


-Retrieve all Users:


-Find User by ID:


-Update User:


We divided this tutorial into two parts.

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

PART 1 - Rest APIs Development using PHP

Final 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;

Create database.php

This file is to create a connection with our database.

<?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 

This file is for inserting the data in the specific table of the database after getting the data.

<?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->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

This file is for fetching the data of table from database.

<?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();

$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);
}
?>

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

This file is for updating the specific data of the table.

<?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

This file is for deleting data from the table.

<?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 Angular 

Final Project Structure: 


package.json 

It holds metadata pertinent to the project and is utilized for managing the project's dependencies, scripts, version, and a whole lot more.

{
"name": "front-end-simple-crud",
"version": "0.0.0",
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e"
},
"private": true,
"dependencies": {
"@angular/animations": "~10.1.2",
"@angular/common": "~10.1.2",
"@angular/compiler": "~10.1.2",
"@angular/core": "~10.1.2",
"@angular/forms": "~10.1.2",
"@angular/platform-browser": "~10.1.2",
"@angular/platform-browser-dynamic": "~10.1.2",
"@angular/router": "~10.1.2",
"bootstrap": "^4.5.3",
"jquery": "^3.5.1",
"rxjs": "~6.6.0",
"tslib": "^2.0.0",
"zone.js": "~0.10.2"
},
"devDependencies": {
"@angular-devkit/build-angular": "^0.1100.3",
"@angular/cli": "~10.1.2",
"@angular/compiler-cli": "~10.1.2",
"@types/jasmine": "~3.5.0",
"@types/jasminewd2": "~2.0.3",
"@types/node": "^12.11.1",
"codelyzer": "^6.0.0",
"jasmine-core": "~3.6.0",
"jasmine-spec-reporter": "~5.0.0",
"karma": "~5.0.0",
"karma-chrome-launcher": "~3.1.0",
"karma-coverage-istanbul-reporter": "~3.0.2",
"karma-jasmine": "~4.0.0",
"karma-jasmine-html-reporter": "^1.5.0",
"protractor": "~7.0.0",
"ts-node": "~8.3.0",
"tslint": "~6.1.0",
"typescript": "~4.0.2"
}
}

The User.ts file(User Model)

Path - src/app/user.ts 

Before defining the UserListComponent, let’s define a User class for working with users. create a new file user.ts inside src/app folder and add the following code to it -

export class User {
id: string;
first_name: string;
last_name: string;
email_id: string;
active: boolean;
}

Create User List Template and Component 

User List Component 

Path - src/app/user-list/user-list.component.ts 


Let's create the UserListComponent component which will be used to display a list of users, create a new user, and delete a user. 

import { UserDetailsComponent } from '../user-details/user-details.component';
import { Observable } from "rxjs";
import { UserService } from "../user.service";
import { User } from "../user";
import { Component, OnInit } from "@angular/core";
import { Router } from '@angular/router';

@Component({
selector: "app-user-list",
templateUrl: "./user-list.component.html",
styleUrls: ["./user-list.component.css"]
})
export class UserListComponent implements OnInit {
users: Observable<User[]>;

constructor(private userService: UserService,
private router: Router) {}

ngOnInit() {
this.reloadData();
}

reloadData() {
this.users = this.userService.getUsersList();
}

deleteUser(id: string) {
this.userService.deleteUser(id)
.subscribe(
data => {
console.log(data);
this.reloadData();
},
error => console.log(error));
}
updateUser(id: string){
this.router.navigate(['update', id]);
}
userDetails(id: string){
this.router.navigate(['details', id]);
}
}

User List Template 

Path - src/app/user-list/user-list.component.html 


Add user-list.component.html file with the following code to it - 

<div class="panel panel-primary">
<div class="panel-heading">
<h2>User List</h2>
</div>
<div class="panel-body">
<table class="table table-striped">
<thead>
<tr>
<th>Firstname</th>
<th>Lastname</th>
<th>Email</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let user of users | async">
<td>{{user.first_name}}</td>
<td>{{user.last_name}}</td>
<td>{{user.email_id}}</td>
<td>
<button (click)="updateUser(user.id)" class="btn btn-warning"
style="margin-left: 10px">Update</button>
<button (click)="deleteUser(user.id)" class="btn btn-danger"
style="margin-left: 10px">Delete</button>
<button (click)="userDetails(user.id)" class="btn btn-info"
style="margin-left: 10px">Details</button>
</td>
</tr>
</tbody>
</table>
</div>
</div>

Create Add User Template and Component

Add User Component 

Path - src/app/create-user/create-user.component.ts 


CreateUserComponent is used to create and handle new user form data. Add the following code to it -

import { UserService } from '../user.service';
import { User } from '../user';
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';

@Component({
selector: 'app-create-user',
templateUrl: './create-user.component.html',
styleUrls: ['./create-user.component.css']
})
export class CreateUserComponent implements OnInit {

user: User = new User();
submitted = false;

constructor(private userService: UserService,
private router: Router) { }

ngOnInit() {
}

newUser(): void {
this.submitted = false;
this.user = new User();
}

save() {
this.userService
.createUser(this.user).subscribe(data => {
console.log(data)
this.user = new User();
this.gotoList();
},
error => console.log(error));
}

onSubmit() {
this.submitted = true;
this.save();
}

gotoList() {
this.router.navigate(['/users']);
}
}

Create User Template 

<h3>Create User</h3>
<div [hidden]="submitted" style="width: 400px;">
<form (ngSubmit)="onSubmit()">
<div class="form-group">
<label for="name">First Name</label>
<input type="text" class="form-control" id="first_name"
required [(ngModel)]="user.first_name" name="first_name">
</div>

<div class="form-group">
<label for="name">Last Name</label>
<input type="text" class="form-control" id="last_name"
required [(ngModel)]="user.last_name" name="last_name">
</div>

<div class="form-group">
<label for="name">Email Id</label>
<input type="text" class="form-control" id="email_id"
required [(ngModel)]="user.email_id" name="email_id">
</div>

<button type="submit" class="btn btn-danger">Submit</button>
</form>
</div>

<div [hidden]="!submitted">
<h4>Successfully submitted!</h4>
</div>


Update User Template and Component 

Update User Component 

Path - src/app/update-user/update-user.component.ts

 UpdateUserComponent is used to update an existing user. 

import { Component, OnInit } from '@angular/core';
import { User } from '../user';
import { ActivatedRoute, Router } from '@angular/router';
import { UserService } from '../user.service';

@Component({
selector: 'app-update-user',
templateUrl: './update-user.component.html',
styleUrls: ['./update-user.component.css']
})
export class UpdateUserComponent implements OnInit {

id: string;
user: User;

constructor(private route: ActivatedRoute,private router: Router,
private userService: UserService) { }

ngOnInit() {
this.user = new User();

this.id = this.route.snapshot.params['id'];
this.userService.getUser(this.id)
.subscribe(data => {
this.user = data;
}, error => console.log(error));
}

updateUser() {
this.userService.updateUser(this.user)
.subscribe(data => {
console.log(data);
this.user = new User();
this.gotoList();
}, error => console.log(error));
}

onSubmit() {
this.updateUser();
}

gotoList() {
this.router.navigate(['/users']);
}
}

Update User Template 

Path - src/app/update-user/update-user.component.html

The update-user.component.html shows the updated user HTML form.

<h3>Update User</h3>
<div style="width: 500px;">
<form (ngSubmit)="onSubmit()">
<div class="form-group">
<label for="name">First Name</label>
<input type="text" class="form-control" id="first_name"
required [(ngModel)]="user.first_name" name="first_name">
</div>

<div class="form-group">
<label for="name">Last Name</label>
<input type="text" class="form-control" id="last_name"
required [(ngModel)]="user.last_name" name="last_name">
</div>

<div class="form-group">
<label for="name">Email Id</label>
<input type="text" class="form-control" id="email_id"
required [(ngModel)]="user.email_id" name="email_id">
</div>

<button type="submit" class="btn btn-success">Submit</button>
</form>
</div>

Create a View User Details Template and Component 

User Details Component 

Path - src/app/user-details/user-details.component.ts 

The UserDetailsComponent component is used to display a particular user detail. Add the following code to it - 

import { User } from '../user';
import { Component, OnInit, Input } from '@angular/core';
import { UserService } from '../user.service';
import { Router, ActivatedRoute } from '@angular/router';

@Component({
selector: 'app-user-details',
templateUrl: './user-details.component.html',
styleUrls: ['./user-details.component.css']
})
export class UserDetailsComponent implements OnInit {

id: string;
user: User;

constructor(private route: ActivatedRoute,private router: Router,
private userService: UserService) { }

ngOnInit() {
this.user = new User();

this.id = this.route.snapshot.params['id'];
console.log("id:"+this.id);
this.userService.getUser(this.id)
.subscribe(data => {
console.log(data)
this.user = data;
}, error => console.log(error));
}

list(){
this.router.navigate(['users']);
}
}

User Details  Template 

Path - src/app/user-details/user-details.component.html 

The user-details.component.html displays a particular user detail. Add the following code to it -

<h2>User Details</h2>

<hr/>
<div *ngIf="user">
<div>
<label><b>First Name: </b></label> {{user.first_name}}
</div>
<div>
<label><b>Last Name: </b></label> {{user.last_name}}
</div>
<div>
<label><b>Email Id: </b></label> {{user.email_id}}
</div>
</div>

<br>
<br>
<button (click)="list()" class="btn btn-primary">User List</button><br>

User Service 

Path - src/app/user.service.ts 

The UserService will be used to get the data from the backend by calling  APIs. Update the user.service.ts file inside src/app directory with the following code to it -

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';

@Injectable({
providedIn: 'root'
})
export class UserService {

private baseUrl = 'http://localhost:8080/php-mysql-crud-api';

constructor(private http: HttpClient) { }

getUser(id: string): Observable<any> {
return this.http.get(`${this.baseUrl}/single_user.php`,
{ params: { id: id } });
}

createUser(user: Object): Observable<Object> {
return this.http.post(`${this.baseUrl}/create.php`, user);
}

updateUser(user: Object): Observable<Object> {
return this.http.put(`${this.baseUrl}/update.php`, user);
}

deleteUser(id: string): Observable<any> {
return this.http.delete(`${this.baseUrl}/delete.php`,
{ params: { id: id } },
);
}

getUsersList(): Observable<any> {
return this.http.get(`${this.baseUrl}/read.php`);
}
}

App Routing Module 

Path: /src/app/app.routing.module.ts 

Routing for the Angular app is configured as an array of Routes, each component is mapped to a path so the Angular Router knows which component to display based on the URL in the browser address bar.

import { UserDetailsComponent } from './user-details/user-details.component';
import { CreateUserComponent } from './create-user/create-user.component';
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import {UserListComponent } from './user-list/user-list.component';
import { UpdateUserComponent } from './update-user/update-user.component';

const routes: Routes = [
{ path: '', redirectTo: 'user', pathMatch: 'full' },
{ path: 'users', component: UserListComponent },
{ path: 'add', component: CreateUserComponent },
{ path: 'update/:id', component: UpdateUserComponent },
{ path: 'details/:id', component: UserDetailsComponent },
];

@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }

App Component 

Path: /src/app/app.component.ts 

The app component is the root component of the application, it defines the root tag of the app as with the selector property of the @Component decorator. 

import { Component } from '@angular/core';

@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'PHP + Angular 10 + MySQL CRUD ';
}


App Component Template 

Path: /src/app/app.component.html 

Defines the HTML template associated with the root AppComponent. 

<nav class="navbar navbar-expand-sm bg-success navbar-dark">
<!-- Links -->
<ul class="navbar-nav">
<li class="nav-item">
<a routerLink="users" class="nav-link"
routerLinkActive="active">User List</a>
</li>
<li class="nav-item">
<a routerLink="add" class="nav-link"
routerLinkActive="active">Add User</a>
</li>
</ul>
</nav>
<div class="container">
<br>
<h2 style="text-align: center;">{{title}}</h2>
<hr>
<div class="card">
<div class="card-body">
<router-outlet></router-outlet>
</div>
</div>
</div>

<footer class="footer">
<div class="container">
<span>All Rights Reserved 2020 @Knowledge Factory</span>
</div>
</footer>

App Module 

Path: /src/app/app.module.ts 

Defines the root module, named AppModule, that tells Angular how to assemble the application. Initially declares only the AppComponent. 

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { CreateUserComponent } from './create-user/create-user.component';
import { UserDetailsComponent } from './user-details/user-details.component';
import { UserListComponent } from './user-list/user-list.component';
import { UpdateUserComponent } from './update-user/update-user.component';
import { HttpClientModule } from '@angular/common/http';
@NgModule({
declarations: [
AppComponent,
CreateUserComponent,
UserDetailsComponent,
UserListComponent,
UpdateUserComponent
],
imports: [
BrowserModule,
AppRoutingModule,
FormsModule,
HttpClientModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }

index.html 

Path: /src/index.html 

The main index.html file is the initial page loaded by the browser that kicks everything off. 

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Angular 10 CRUD</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
<app-root></app-root>
</body>
</html>

Main File 

Path: /src/main.ts 

The main file is the entry point used by angular to launch and bootstrap the application. 

import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
import { environment } from './environments/environment';

if (environment.production) {
enableProdMode();
}

platformBrowserDynamic().bootstrapModule(AppModule)
.catch(err => console.error(err));

Download the dependencies using the following command: 

npm install 


Run the frontend application using the following command: 

ng serve

Hit http://localhost:4200 link in a browser that will host this Angular CRUD app. 



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