Creating An Audit Trail/Logs in a Web App using PHP/OOP and Ajax Tutorial
In this tutorial, we will tackle about how to create an Audit Trail/Audit Log feature in a web application using PHP/OOP and jQuery Ajax. This feature is useful and a common feature for software or web applications. This is usually included in the project for an application that can be managed by multiple users. With this feature in your PHP Project, you can easily track or monitor the changes or actions made by the user.
Getting Started
In this tutorial, I will be using Bootrstrap for the design of the web app. Also, download jQuery for our ajax functions. I'll be using XAMPP as my local web server and database server.
Before we continue, please make sure that your Apache and MySQL.
Creating the Database
Open your PHPMyAdmin in browser and create new database naming audit_trailing. Next, navigate your database page into the SQL Tab and copy/paste
the SQL Script below. Then, click the Go Button to execute the script.
- (1, 'Clairy', 'Blake', '09123456789', 'Sample Address', '2021-10-07 11:51:27'),
- (2, 'John', 'Smith', '09123456789', 'Sample Address', '2021-10-07 11:55:00'),
- (5, 'Mikee', 'Williams', '09112655889', '23rd St. Here City', '2021-10-07 13:17:46');
- (1, 'Administrator', 'admin', '0192023a7bbd73250516f069df18b500', '2021-10-07 03:59:25'),
- (2, 'john Smitn', 'jsmith', '1254737c076cf867dc53d60a0364f38e', '2021-10-07 03:59:25');
Creating Our Database Connection
Create a new PHP File and copy/paste
the script below. The script contains our PHP Class for database connection. Save the file as DBConnection.php.
- <?php
- Class DBConnection{
- public $conn;
- function __construct(){
- $this->conn = new mysqli(host,username,password,db_tbl);
- if(!$this->conn){
- }
- }
- function __destruct(){
- $this->conn->close();
- }
- }
- $db = new DBConnection();
- $conn= $db->conn;
Creating the Interface
The below codes are the scripts for our pages interfaces. Copy/Paste the scripts below and save the files according to the file name stated before the scripts.
login.php
This file contains the html scripts for our login page interface. The Ajax Script for the form submission is also included in this file.
- <?php
- session_start();
- if(isset($_SESSION['id']) && $_SESSION['id'] > 0){
- header("Location:./");
- exit;
- }
- require_once('./DBConnection.php');
- ?>
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta http-equiv="X-UA-Compatible" content="IE=edge">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <link rel="stylesheet" href="./css/bootstrap.min.css">
- <style>
- html, body{
- height:100%;
- }
- </style>
- </head>
- <body class="bg-dark bg-gradient">
- <div class="h-100 d-flex jsutify-content-center align-items-center">
- <div class='w-100'>
- <div class="card my-3 col-md-4 offset-md-4">
- <div class="card-body">
- <form action="" id="login-form">
- <div class="form-group">
- <input type="text" id="username" autofocus name="username" class="form-control form-control-sm rounded-0" required>
- </div>
- <div class="form-group">
- <input type="password" id="password" autofocus name="password" class="form-control form-control-sm rounded-0" required>
- </div>
- <div class="form-group d-flex w-100 justify-content-end">
- </div>
- </form>
- </div>
- </div>
- </div>
- </div>
- </body>
- <script>
- $(function(){
- $('#login-form').submit(function(e){
- e.preventDefault();
- $('.pop_msg').remove()
- var _this = $(this)
- var _el = $('<div>')
- _el.addClass('pop_msg')
- _this.find('button').attr('disabled',true)
- _this.find('button[type="submit"]').text('Loging in...')
- $.ajax({
- url:'./Actions.php?a=login',
- method:'POST',
- data:$(this).serialize(),
- dataType:'JSON',
- error:err=>{
- console.log(err)
- _el.addClass('alert alert-danger')
- _el.text("An error occurred.")
- _this.prepend(_el)
- _el.show('slow')
- _this.find('button').attr('disabled',false)
- _this.find('button[type="submit"]').text('Save')
- },
- success:function(resp){
- if(resp.status == 'success'){
- _el.addClass('alert alert-success')
- setTimeout(() => {
- location.replace('./');
- }, 2000);
- }else{
- _el.addClass('alert alert-danger')
- }
- _el.text(resp.msg)
- _el.hide()
- _this.prepend(_el)
- _el.show('slow')
- _this.find('button').attr('disabled',false)
- _this.find('button[type="submit"]').text('Save')
- }
- })
- })
- })
- </script>
- </html>
index.php
The code below is the script for our page template. It contains the top bar navigation, dynamic modal, and confirmation scripts.
- <?php
- session_start();
- if(!isset($_SESSION['id']) || (isset($_SESSION['id']) && $_SESSION['id'] <= 0)){
- header("Location:./login.php");
- exit;
- }
- require_once('./DBConnection.php');
- $page = isset($_GET['page']) ? $_GET['page'] : 'home';
- ?>
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta http-equiv="X-UA-Compatible" content="IE=edge">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <link rel="stylesheet" href="./fontawesome/css/all.min.css">
- <link rel="stylesheet" href="./css/bootstrap.min.css">
- <link rel="stylesheet" href="./DataTables/datatables.min.css">
- <style>
- </style>
- </head>
- <body class="bg-light">
- <main>
- <nav class="navbar navbar-expand-lg navbar-dark bg-primary bg-gradient" id="topNavBar">
- <div class="container">
- <a class="navbar-brand" href="./">
- Audit Trailing
- </a>
- <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
- </button>
- <div class="collapse navbar-collapse" id="navbarNav">
- <ul class="navbar-nav">
- <li class="nav-item">
- </li>
- <li class="nav-item">
- </li>
- </ul>
- </div>
- <div>
- <?php if(isset($_SESSION['id'])): ?>
- <div class="dropdown">
- <button class="btn btn-secondary dropdown-toggle bg-transparent text-light border-0" type="button" id="dropdownMenuButton1" data-bs-toggle="dropdown" aria-expanded="false">
- Hello <?php echo $_SESSION['name'] ?>
- </button>
- <ul class="dropdown-menu" aria-labelledby="dropdownMenuButton1">
- </ul>
- </div>
- <?php endif; ?>
- </div>
- </div>
- </nav>
- <div class="container py-3" id="page-container">
- <?php
- if(isset($_SESSION['flashdata'])):
- ?>
- <div class="dynamic_alert alert alert-<?php echo $_SESSION['flashdata']['type'] ?>">
- <?php echo $_SESSION['flashdata']['msg'] ?>
- </div>
- <?php unset($_SESSION['flashdata']) ?>
- <?php endif; ?>
- <?php
- include $page.'.php';
- ?>
- </div>
- </main>
- <div class="modal fade" id="uni_modal" role='dialog' data-bs-backdrop="static" data-bs-keyboard="true">
- <div class="modal-dialog modal-md modal-dialog-centered" role="document">
- <div class="modal-content">
- <div class="modal-header py-2">
- </div>
- <div class="modal-body">
- </div>
- <div class="modal-footer py-1">
- </div>
- </div>
- </div>
- </div>
- <div class="modal fade" id="uni_modal_secondary" role='dialog' data-bs-backdrop="static" data-bs-keyboard="true">
- <div class="modal-dialog modal-md modal-dialog-centered" role="document">
- <div class="modal-content">
- <div class="modal-header py-2">
- </div>
- <div class="modal-body">
- </div>
- <div class="modal-footer py-1">
- </div>
- </div>
- </div>
- </div>
- <div class="modal fade" id="confirm_modal" role='dialog'>
- <div class="modal-dialog modal-md modal-dialog-centered" role="document">
- <div class="modal-content rounded-0">
- <div class="modal-header py-2">
- </div>
- <div class="modal-body">
- </div>
- <div class="modal-footer py-1">
- </div>
- </div>
- </div>
- </div>
- </body>
- </html>
home.php
This project is the script for our home page content. It contains our dummy data table.
- <div class="container py-5">
- <div class="card">
- <div class="card-body">
- <div class="col-12 my-2 d-flex justify-content-end">
- </div>
- <table class="table table-bordered table-striped table-hover">
- <thead>
- <tr>
- </tr>
- </thead>
- <tbody>
- <?php
- $qry = $conn->query("SELECT * FROM members order by firstname asc, lastname asc");
- $i = 1;
- while($row=$qry->fetch_assoc()):
- ?>
- <tr>
- <td class="py-1 px-2 text-center">
- <div class="btn-group" role="group">
- <button id="btnGroupDrop1" type="button" class="btn btn-primary dropdown-toggle btn-sm rounded-0 py-0" data-bs-toggle='dropdown' aria-expanded="false" >
- Action
- </button>
- <ul class="dropdown-menu" aria-labelledby="btnGroupDrop1">
- </ul>
- </div>
- </td>
- </tr>
- <?php endwhile; ?>
- <?php if($qry->num_rows <=0): ?>
- <tr>
- </tr>
- <?php endif; ?>
- </tbody>
- </table>
- </div>
- </div>
- </div>
- <script>
- $(function(){
- $('#add_new').click(function(){
- uni_modal('New Member',"manage_member.php");
- })
- $('.edit_data').click(function(){
- uni_modal('Edit Member Details',"manage_member.php?id="+$(this).attr('data-id'));
- })
- $('.delete_data').click(function(){
- })
- $('.view_data').click(function(){
- uni_modal('View Member Details',"view_member.php?id="+$(this).attr('data-id'));
- })
- })
- function delete_data($id){
- $('#confirm_modal button').attr('disabled',true)
- $.ajax({
- url:'./Actions.php?a=delete_member',
- method:'POST',
- data:{id:$id},
- dataType:'JSON',
- error:err=>{
- console.log(err)
- alert("An error occurred.")
- $('#confirm_modal button').attr('disabled',false)
- },
- success:function(resp){
- if(resp.status == 'success'){
- location.reload()
- }else{
- alert("An error occurred.")
- $('#confirm_modal button').attr('disabled',false)
- }
- }
- })
- }
- </script>
logs.php
The code below is the script which displays all the logs from database.
- <div class="container py-5">
- <div class="d-flex w-100">
- </div>
- <hr>
- <div class="card">
- <div class="card-body">
- <table class="table table-bordered table-striped table-hover">
- <thead>
- <tr>
- </tr>
- </thead>
- <tbody>
- <?php
- $qry = $conn->query("SELECT l.*,u.username FROM `logs` l inner join users u on l.user_id = u.id order by unix_timestamp(l.`date_created`) asc");
- $i = 1;
- while($row=$qry->fetch_assoc()):
- ?>
- <tr>
- </tr>
- <?php endwhile; ?>
- <?php if($qry->num_rows <=0): ?>
- <tr>
- </tr>
- <?php endif; ?>
- </tbody>
- </table>
- </div>
- </div>
- </div>
manage_member.php
This is the script for our member form which will be used in both adding and updating member's details. This will be shown in a modal.
- <?php
- require_once('./DBConnection.php');
- if(isset($_GET['id'])){
- $qry= $conn->query("SELECT * FROM members where id = '{$_GET['id']}'");
- if($qry->num_rows > 0){
- foreach($qry->fetch_array() as $k => $v){
- if(!is_numeric($k))
- $$k=$v;
- }
- }
- }
- ?>
- <div class="container-fluid">
- <form action="" id="member-form">
- <input type="hidden" name="id" value="<?php echo isset($id) ? $id : '' ?>">
- <div class="form-group">
- <input type="text" name="firstname" class="form-control form-control-sm rounded-0" value="<?php echo isset($firstname) ? $firstname : "" ?>" required>
- </div>
- <div class="form-group">
- <input type="text" name="lastname" class="form-control form-control-sm rounded-0" value="<?php echo isset($lastname) ? $lastname : "" ?>" required>
- </div>
- <div class="form-group">
- <input type="text" name="contact" class="form-control form-control-sm rounded-0" value="<?php echo isset($contact) ? $contact : "" ?>" required>
- </div>
- <div class="form-group">
- </div>
- </form>
- </div>
- <script>
- $(function(){
- $('#member-form').submit(function(e){
- e.preventDefault();
- $('.pop_msg').remove()
- var _this = $(this)
- var _el = $('<div>')
- _el.addClass('pop_msg')
- $('#uni_modal button').attr('disabled',true)
- $('#uni_modal button[type="submit"]').text('submitting form...')
- $.ajax({
- url:'./Actions.php?a=save_member',
- data: new FormData($(this)[0]),
- cache: false,
- contentType: false,
- processData: false,
- method: 'POST',
- type: 'POST',
- dataType: 'json',
- error:err=>{
- console.log(err)
- _el.addClass('alert alert-danger')
- _el.text("An error occurred.")
- _this.prepend(_el)
- _el.show('slow')
- $('#uni_modal button').attr('disabled',false)
- $('#uni_modal button[type="submit"]').text('Save')
- },
- success:function(resp){
- if(resp.status == 'success'){
- _el.addClass('alert alert-success')
- $('#uni_modal').on('hide.bs.modal',function(){
- location.reload()
- })
- if("<?php echo isset($enrollee_id) ?>" != 1)
- _this.get(0).reset();
- }else{
- _el.addClass('alert alert-danger')
- }
- _el.text(resp.msg)
- _el.hide()
- _this.prepend(_el)
- _el.show('slow')
- $('#uni_modal button').attr('disabled',false)
- $('#uni_modal button[type="submit"]').text('Save')
- }
- })
- })
- })
- </script>
view_member.php
This is the script which displays the details of selected member. This will be shown in a modal.
- <?php
- require_once('./DBConnection.php');
- $qry= $conn->query("SELECT * FROM members where id = '{$_GET['id']}'");
- if($qry->num_rows > 0){
- foreach($qry->fetch_array() as $k => $v){
- if(!is_numeric($k))
- $$k=$v;
- }
- }
- ?>
- <style>
- #uni_modal .modal-footer{
- display:none !important;
- }
- </style>
- <div class="container-fluid">
- <dl>
- </dl>
- <div class="col-12">
- <div class="w-100 d-flex justify-content-end">
- </div>
- </div>
- </div>
- <script>
- $(function(){
- $.ajax({
- url:'./Actions.php?a=save_log',
- method:'POST',
- data:{action_made:" viewed the data of <?php echo "[id={$id}]". $firstname.' '.$lastname ?>"},
- dataType:'json',
- error:err=>{
- console.log(err)
- },
- succuess:function(resp){
- if(resp == 1){
- console.log("Log successfully saved")
- }else{
- console.log("Log has failed to save.")
- }
- }
- })
- })
- </script>
Creating Our Custom Javascript Functions
The code below contains the scripts for our custome js/jquery functions of our dynamic modal and confirmation modal. In my case, I saved this file inside the js directory naming script.js. This file is included in the index.php file.
- window.uni_modal = function($title = '', $url = '', $size = "") {
- $.ajax({
- url: $url,
- error: err => {
- console.log()
- alert("An error occured")
- },
- success: function(resp) {
- if (resp) {
- $('#uni_modal .modal-title').html($title)
- $('#uni_modal .modal-body').html(resp)
- $('#uni_modal .modal-dialog').removeClass('large')
- $('#uni_modal .modal-dialog').removeClass('mid-large')
- $('#uni_modal .modal-dialog').removeClass('modal-md')
- if ($size == '') {
- $('#uni_modal .modal-dialog').addClass('modal-md')
- } else {
- $('#uni_modal .modal-dialog').addClass($size)
- }
- $('#uni_modal').modal({
- backdrop: 'static',
- keyboard: true,
- focus: true
- })
- $('#uni_modal').modal('show')
- }
- }
- })
- }
- window.uni_modal_secondary = function($title = '', $url = '', $size = "") {
- $.ajax({
- url: $url,
- error: err => {
- console.log()
- alert("An error occured")
- },
- success: function(resp) {
- if (resp) {
- $('#uni_modal_secondary .modal-title').html($title)
- $('#uni_modal_secondary .modal-body').html(resp)
- $('#uni_modal_secondary .modal-dialog').removeClass('large')
- $('#uni_modal_secondary .modal-dialog').removeClass('mid-large')
- $('#uni_modal_secondary .modal-dialog').removeClass('modal-md')
- if ($size == '') {
- $('#uni_modal_secondary .modal-dialog').addClass('modal-md')
- } else {
- $('#uni_modal_secondary .modal-dialog').addClass($size)
- }
- $('#uni_modal_secondary').modal({
- backdrop: 'static',
- keyboard: true,
- focus: true
- })
- $('#uni_modal_secondary').modal('show')
- }
- }
- })
- }
- window._conf = function($msg = '', $func = '', $params = []) {
- $('#confirm_modal #confirm').attr('onclick', $func + "(" + $params.join(',') + ")")
- $('#confirm_modal .modal-body').html($msg)
- $('#confirm_modal').modal('show')
- }
Creating the Main PHP Class
Lastly, copy/paste
the source code below andd save it as Actions.php. This file is our main PHP Class which contains the action queries and the audit trail/log saving function.
- <?php
- require_once('DBConnection.php');
- Class Actions extends DBConnection{
- function __construct(){
- parent::__construct();
- }
- function __destruct(){
- parent::__destruct();
- }
- // Data array paramateres
- // user_id = user unique id
- // action_made = action made by the user
- $sql = "INSERT INTO `logs` (`user_id`,`action_made`) VALUES ('{$user_id}','{$action_made}')";
- $save = $this->conn->query($sql);
- if(!$save){
- }
- }
- return true;
- }
- function login(){
- @$qry = $this->conn->query($sql)->fetch_array();
- if(!$qry){
- $resp['status'] = "failed";
- $resp['msg'] = "Invalid username or password.";
- }else{
- $resp['status'] = "success";
- $resp['msg'] = "Login successfully.";
- foreach($qry as $k => $v){
- $_SESSION[$k] = $v;
- }
- $log['user_id'] = $qry['id'];
- $log['action_made'] = "Logged in the system.";
- // audit log
- $this->save_log($log);
- }
- }
- function logout(){
- $log['user_id'] = $_SESSION['id'];
- $log['action_made'] = "Logged out.";
- // audit log
- $this->save_log($log);
- }
- function save_member(){
- $data = "";
- foreach($_POST as $k => $v){
- $data .= " `{$k}` = '{$v}' ";
- }
- }
- $sql = "INSERT INTO `members` set {$data}";
- }else{
- $sql = "UPDATE `members` set {$data} where id = '{$id}'";
- }
- $save = $this->conn->query($sql);
- if($save){
- $resp['status'] = 'success';
- $log['user_id'] = $_SESSION['id'];
- $resp['msg'] = "New Member successfully added.";
- $log['action_made'] = " added [id={$member_id}] {$firstname} {$lastname} into the member list.";
- }else{
- $resp['msg'] = "Member successfully updated.";
- $log['action_made'] = " updated the details of [id={$member_id}] member.";
- }
- // audit log
- $this->save_log($log);
- }else{
- $resp['status'] = 'failed';
- $resp['msg'] = "Error saving member details. Error: ".$this->conn->error;
- $resp['sql'] = $sql;
- }
- }
- function delete_member(){
- $mem = $this->conn->query("SELECT * FROM members where id = '{$id}'")->fetch_array();
- $delete = $this->conn->query("DELETE FROM members where id = '{$id}'");
- if($delete){
- $resp['status'] = 'success';
- $resp['msg'] = 'Member successfully deleted.';
- $log['user_id'] = $_SESSION['id'];
- $log['action_made'] = " deleted [id={$mem['id']}] {$mem['firstname']} {$mem['lastname']} from member list.";
- $_SESSION['flashdata']['type'] = 'success';
- $_SESSION['flashdata']['msg'] = $resp['msg'];
- // audit log
- $this->save_log($log);
- }else{
- $resp['status']='failed';
- $resp['msg']='Failed to delete member.';
- $resp['error']=$this->conn->error;
- }
- }
- }
- $action = new Actions();
- switch($a){
- case 'login':
- echo $action->login();
- break;
- case 'logout':
- echo $action->logout();
- break;
- case 'save_member':
- echo $action->save_member();
- break;
- case 'delete_member':
- echo $action->delete_member();
- break;
- case 'save_log':
- $log['user_id'] = $_SESSION['id'];
- $log['action_made'] = $_POST['action_made'];
- echo $action->save_log($log);
- break;
- default:
- // default action here
- echo "No Action given";
- break;
- }
DEMO VIDEO
Sample Access
Username: admin/jsmith
Password: admin123/jsmith123
That's it! You can now test the simple web application that has an Audit Trail/Logs Feature that we created on your end. If you have encountered any errors, please review the scripts above. You can also download the working source code I created for this tutorial. Download button is located below this content.
I hope this PHP/OOP and jQuery Ajax Tutorial will help you with what you are looking for and you'll find this userful for your future PHP Projects.
Happy Coding :)
Comments
Add new comment
- Add new comment
- 11214 views