ANGULAR CRUD OPERATIONS WITH PHP MYSQL


After studying some of the basics of Angular, this blog is dedicated to a simple Tutorial based on Angular with PHP backend and MySQL. As we all know that, Angular is a front-end framework. So, we need to require good technology that handles the backend operations. Here for the backend, we’re going to use MySQL for database and PHP. Also, we’re are using the Bootstrap 4 data table. So, we are going to start with how to use Angular with PHP backend


Install and Setup Angular App

First of all, let’s create an Angular app. Note that here please we need to make sure that we are using the latest version of  Angular CLI.
We can check the Angular CLI version using the below command.

ng --version

Now, we are creating a new app using Angular CLI.

ng new angular-crud

Now, we are ready with the setup of your angular app.

Install Bootstrap 4 In Angular App

Now we need to move to the folder of our Angular App. For that, we can use the below command

cd angular-crud

Now we need to install bootstrap 4 in our Angular app using bootstrap 4 NPM package.

npm install bootstrap

Now open the angular.json file and add the bootstrap path to the styles section as I show below

"styles": [
  "src/styles.css",
  "node_modules/bootstrap/dist/css/bootstrap.min.css"
],

Install Bootstrap Datatable

Next, we are installing the bootstrap 4 data table in our Angular app using the below command

npm install angular-4-data-table-bootstrap-4 --save

Then, we need to import DataTableModule and other dependencies in our main module to start working with the data table.

/src/app/app.module.ts

//Other dependencies..
import { CommonModule } from '@angular/common';
//Import form modules
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
//Import DataTable Module
import { DataTableModule } from 'angular-4-data-table-bootstrap-4';
Here, don’t forget to add these modules in import sections as well.

@NgModule({
  declarations: [
      AppComponent
    ],
  imports: [
      BrowserModule,
      CommonModule,
    FormsModule,
    ReactiveFormsModule,
    DataTableModule
    ],
  providers: [],
    bootstrap: [AppComponent]
})
export class AppModule { }

Now our application is set to use data table, we can now run the application using below command

ng serve

Use PHP Backend + MySQL

Here, we are going to design the database design for our application. For that, we require a MySQL database. Here I am using Xampp. If you do not already install the  Apache server, PHP and MySQL then install and continue. Now, I am going to create a separate new with our backend code.

Design MySQL Database

Now we are going to design a simple MySQL database and table. Let us go to phpmyadmin using the localhost and create a database named angular-crud. And now use the below query in your SQL for design a simple table named users.

CREATE TABLE `users` (
  `id` int(11) NOT NULL,
  `first_name` varchar(255) NOT NULL,
  `last_name` varchar(255) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO `users` (`id`, `first_name`, `last_name`) VALUES
(19, 'ABC', 'ABC'),
(21, 'XYZ', 'XYZ'),

Sorry I forget to use indexing, execute the below query as well

ALTER TABLE `users`
  ADD PRIMARY KEY (`id`);
AUTO_INCREMENT for dumped tables
AUTO_INCREMENT for table `users`
ALTER TABLE `users`
  MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;

Design PHP Backend With API

Here, we are going to create a file that will interact with our Angular app. And there is a class that will interact with the database and we will handle the CRUD operations.

/php-rest-api/index.php

This above file will handle the requests coming and based on that call it will handle the database operations.

<?php
if (isset($_SERVER['HTTP_ORIGIN'])) {
    header("Access-Control-Allow-Origin: {$_SERVER['HTTP_ORIGIN']}");
    //If required
    header('Access-Control-Allow-Credentials: true');
    header('Access-Control-Max-Age: 86400');    // cache for 1 day
}
if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
  if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD']))
        header("Access-Control-Allow-Methods: GET, POST, PUT, OPTIONS");        

    if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']))
        header("Access-Control-Allow-Headers: {$_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']}");
    exit(0);
}

// Connecting to database

$conn = mysqli_connect('host','username','password','angular4-crud');
include_once('users.php');
$request_method = $_SERVER["REQUEST_METHOD"];
$data = json_decode(file_get_contents("php://input"));
$user = new Users;
switch($request_method)
{
  case 'GET':
    // Retrive Users
    if(!empty($_GET["user_id"]))
    {
      $user_id=intval($_GET["user_id"]);
      $user->getUsers($user_id);
    }
    else
    {
      $user->getUsers();
    }
    break;
  case 'POST':
    // Insert User
    $user->saveUser($data);
    break;
  case 'PUT':
    $user->updateUser($data);
    break;
  case 'DELETE':
    // Delete User
    $user->deleteUser($data);
    break;
  default:
    // Invalid Request Method
    header("HTTP/1.0 405 Method Not Allowed");
    break;
}

/php-rest-api/users.php

The user's file will handle the database based operations on the call by index.php

<?php
class Users{
  //Get users
  function getUsers()
  {
    global $conn;
    $query="SELECT * FROM users ORDER BY id DESC";
    $response=array();
    $result=mysqli_query($conn, $query);
    while($row = mysqli_fetch_assoc($result))
    {
      $response[]=$row;
    }
    header('Content-Type: application/json');
    echo json_encode($response);
  }
  //Save user
  function saveUser($data){
    global $conn;
    $query="INSERT INTO users (first_name, last_name) VALUES ('".$data->first_name."', '".$data->last_name."')";
    echo $result=mysqli_query($conn, $query);
    header('Content-Type: application/json');
    //Respond success / error messages
  }
  //Update user
  function updateUser($data){
    global $conn;
    $query = "UPDATE users SET first_name='".$data->first_name."', last_name='".$data->last_name."' WHERE id=$data->id.";
    echo $result=mysqli_query($conn, $query);
    header('Content-Type: application/json');
    //Respond success / error messages
  }
  //Delete user
  function deleteUser($data){
    global $conn;
    $query = "DELETE FROM users WHERE id=".$data->id;
    echo $result=mysqli_query($conn, $query);
    header('Content-Type: application/json');
    //Respond success / error messages
  }
}

/php-rest-api/.htaccess

Now, we need .htaccess to hide the PHP extension from the URL.

RewriteEngine On
# Unless directory, remove trailing slash
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^([^/]+)/$ https://www.thetechieshouse.com/php-rest-api/$1 [R=301,L]
# Redirect external .php requests to extensionless url
RewriteCond %{THE_REQUEST} ^(.+)\.php([#?][^\ ]*)?\ HTTP/
RewriteRule ^(.+)\.php$ https://www.thetechieshouse.com/php-rest-api/$1 [R=301,L]
# Resolve .php file for extensionless php urls
RewriteRule ^([^/.]+)$ $1.php [L]

Note that If we are running PHP and Angular app both on your localhost, we will get some difficulties of CORS enabled or 405 or 401. Please check StackOverflow it will help you find the CORS issue.

Here we go, we are all set with our PHP backend code for our API.

Angular CRUD Example with PHP MySQL

Now, let us start writing the PHP APIs for our Angular app. So for this, we should define an Angular service that will handle the communication with PHP backend. Now we will define Angular Service. First of all, let us create a new service using Angular CLI.

ng generate service DbOperations

Now, let us design the service which will handle API calls.

/src/app/db-operations.service.ts

//Import Injectable
import { Injectable } from '@angular/core';
//Import http modules
import {  Http, Response, Headers, RequestOptions } from '@angular/http';
//Import observable
import { Observable } from 'rxjs';
import 'rxjs/Rx';
@Injectable()
export class DbOperationsService {
  apiURL = "https://www.thetechieshouse.com/php-rest-api";
    constructor(private http: Http){}
    //Save users
  saveUsers(users: any[]){
    console.log(users);
    this.http.post(this.apiURL, users)
      .subscribe(
        (val) => {
            console.log("POST call successful value returned in body",
                        val);
        },
        response => {
            console.log("POST call in error", response);
        },
        () => {
            console.log("The POST observable is now completed.");
        });
  }
  //Get all users
  getUsers() {
    const headers = new Headers();
    headers.append("Cache-Control", "no-cache");
        headers.append('Access-Control-Allow-Origin', '*');
        headers.append('Access-Control-Allow-Methods', 'GET, POST');
        headers.append('Access-Control-Max-Age', '1728000');
    headers.append('Content-Type', 'application/x-www-form-urlencoded');
      return this.http.get(this.apiURL);
  }
  //Update user
  updateUser(user)
  {
    return this.http.put(this.apiURL, user).subscribe(
        (val) => {
            console.log("UPDATE call successful value returned in body",
                        val);
        },
        response => {
            console.log("UPDATE call in error", response);
        },
        () => {
            console.log("The UPDATE observable is now completed.");
        });
  }
  //Delete user
  deleteUser(user){
    return this.http.delete(this.apiURL, new RequestOptions({body : user
      })).subscribe(
        (val) => {
            console.log("DELETE call successful value returned in body",
                        val);
        },
        response => {
            console.log("DELETE call in error", response);
        },
        () => {
            console.log("The DELETE observable is now completed.");
        });
  }
}

Here, we need to make sure that we are importing service and other our needed dependencies in your app main module.

/src/app/app.module.ts

//Import browser module
import { BrowserModule } from '@angular/platform-browser';
//Import core
import { NgModule } from '@angular/core';
//Import common
import { CommonModule } from '@angular/common';
//Import forms module
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
//Import DataTable
import { DataTableModule } from 'angular-4-data-table-bootstrap-4';
//Import HTTP for API call
import { HttpModule  } from '@angular/http';
//Import app component
import { AppComponent } from './app.component';
//Import service
import { DbOperationsService } from './db-operations.service';
@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    CommonModule,
    HttpModule,
    FormsModule,
    ReactiveFormsModule,
    DataTableModule
  ],
  providers: [DbOperationsService],
  bootstrap: [AppComponent]
})
export class AppModule { }

Now, we are set with our service which will handle the API calls. Next, we will go to CRUD operations on Angular

List / Retrieve Operation

Firstly, let us define the app component and we need a template to work with the CRUD operations.

/src/app/app.component.ts

So we will import some of the dependencies in our app component. Next, let us have a class with a few of the basic methods which help to render the data table.

//Import core modules

import { Component, OnInit, ViewChild, ElementRef } from '@angular/core';
//Import forms modules
import { FormGroup, FormControl, Validators} from '@angular/forms';
//Import DataTable
import { DataTableResource } from 'angular-4-data-table-bootstrap-4';
//Import HTTP
import {  Http, Response, Headers } from '@angular/http';
//Import DB service
import { DbOperationsService } from './db-operations.service';
@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
    title = 'Angular  With PHP Backend ';
    userForm: FormGroup;
    @ViewChild('modalClose') modalClose:ElementRef;
  persons: any[] = [];
  itemResource;
  items = [];
  itemCount = 0;
  params = {offset: 0, limit: 10}; //Static can be changed as per your need
  formFlag = 'add';
  constructor(private db:DbOperationsService, private http: Http){
    //DB service function called
      db.getUsers().subscribe(
        (response: Response) => {
          this.persons = response.json();
          this.reloadItems(this.params);
        } ,
        (error) => {console.log(error);}
    );;
  } 
    reloadItems(params) {
    this.itemResource = new DataTableResource(this.persons);
    this.itemResource.count().then(count => this.itemCount = count);
      this.itemResource.query(params).then(items => this.items = items);
    }
    // special properties:
    rowClick(rowEvent) {
        console.log('Clicked: ' + rowEvent.row.item.id);
    }
    rowDoubleClick(rowEvent) {
        alert('Double clicked: ' + rowEvent.row.item.id);
    }
  rowTooltip(item) { return item.jobTitle; }
  //Init method
  ngOnInit(){
    this.userForm = new FormGroup({
      'id': new FormControl(null),
      'first_name': new FormControl(null, Validators.required),
      'last_name': new FormControl(null, Validators.required)
    });
  }
  initUser(){
    //User form reset
    this.userForm.reset();
    this.formFlag = 'add';
  }
}

/src/app/app.component.html

Let us have a template with datatable and a modal popup which will add the values into the table. Also, we have used the reactive form approach for the validations.

<div style="margin: auto; max-width: 1000px; margin-bottom: 50px;">
  <div class="modal fade" id="add-edit-Modal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
    <div class="modal-dialog" role="document">
      <div class="modal-content">
        <div class="modal-header">
          <h5 class="modal-title" id="exampleModalLabel">Add/Edit Form</h5>
          <button type="button" class="close" data-dismiss="modal" aria-label="Close">
          <span aria-hidden="true">×</span>
          </button>
        </div>
        <form [formGroup]="userForm" (ngSubmit)="saveUser(formFlag)">
        <div class="modal-body">
          <input type="hidden" class="input-sm form-control" formControlName="id">
          <div class="form-group">
            <input type="text" class="input-sm form-control" formControlName="first_name">
            <p class="help-block" *ngIf="!userForm.get('first_name').valid && userForm.get('first_name').touched">Please enter first name.</p>
          </div>
          <div class="form-group">
            <input type="text" class="input-sm form-control" formControlName="last_name">
            <p class="help-block" *ngIf="!userForm.get('last_name').valid && userForm.get('last_name').touched">Please enter last name.</p>
          </div>
        </div>
        <div class="modal-footer">
          <button type="button" class="btn btn-secondary" #modalClose data-dismiss="modal">Close</button>
          <button type="submit" class="btn btn-primary" [disabled]="!userForm.valid">Save changes</button>
        </div>
        </form>
      </div>
    </div>
  </div>
  <button type="button" class="btn btn-primary" data-toggle="modal" data-target="#add-edit-Modal"  (click)="initUser()">Add</button>
  <data-table id="persons-grid"
    headerTitle="Angular  CRUD Example With Bootstrap 4 Datatable"
    [items]="items"
    [itemCount]="itemCount"
    (reload)="reloadItems($event)"
    (rowClick)="rowClick($event)"
    (rowDoubleClick)="rowDoubleClick($event)"
    [rowTooltip]="rowTooltip"
    >
    <data-table-column
        [property]="'first_name'"
        [header]="'First Name'"
        [sortable]="true"
        [resizable]="true"
        >
    </data-table-column>
    <data-table-column
        [property]="'last_name'"
        [header]="'Last Name'"
        [sortable]="true"
        >       
    </data-table-column>
    <data-table-column
      [property]="'Actions'"
      [header]="'Actions'">
      <template #dataTableCell let-item="item">
            <span style="color: rgb(232, 0, 0)">
            <a href="javascript:void(0);" (click)="getData(item)" data-toggle="modal" data-target="#add-edit-Modal">Edit</a>
            </span>
            <span style="color: rgb(232, 0, 0)">
            <a href="javascript:void(0);" (click)="delData(item)">Delete</a>
            </span>
      </template>
    </data-table-column>
  </data-table>
</div>

Please don’t forget to mention our style for validations messages.

/src/app/app.component.css

input.ng-invalid.ng-touched {
  border: 1px solid red;
}
p.help-block{
  color: red !important;
}
.data-table-header{
  text-align: center !important;
}

Create / Add and Edit / Update Operations

Here, we are using the same modal popup for add and edit operations by just switching it by using a formFlag.Based on our current flow, we are performing the service method call and then we will update the view.

//Save user's data

saveUser(){
  if(this.formFlag == 'add')
  {
    this.userForm.value.id= this.persons.length + 1;
    this.persons.unshift(this.userForm.value);
    //Save method
    this.db.saveUsers(this.userForm.value);
  }
  else
  {
    //Update database
    this.db.updateUser(this.userForm.value);
    var index = this.persons.findIndex(x => x.id== this.userForm.value.id);
    if (index !== -1) {
      this.persons[index] = this.userForm.value;
    }
  }
  this.reloadItems(this.params);
  //Close modal
  this.modalClose.nativeElement.click();
  //User form reset
  this.userForm.reset();
}
//Get data while edit
getData(item)
{
  //Here you can fetch data from database
  this.userForm.patchValue(item);
  this.formFlag = 'edit';
}

Delete Operation With DELETE Method

So, let us implement the delete operation. Here, we can use the confirmation pop up as per our need.

//Delete user's data
delData(item){
  //Call service
  this.db.deleteUser(item);
        //Delete from array
  this.persons.splice(this.persons.indexOf(item), 1);
  this.reloadItems(this.params);
}


We are ready and we can run our application and enjoy it. Hope it helps you

Comments