PHP Demo Project Docu
PHP Demo Project Docu
By:
Jei Q. Pastrana
June 5, 2024
Table of Contents
1. Overview
o Sitemap Diagram
2. Processes
o User Flow
o System Architecture
3. Database Design
o ER Diagram
o Schema Description
o Relation Schema and SQL Implementation of the Design
4. Code Logic
o Directory Structure
o Core Modules
o API Documentation
o JavaScript Embedded Codes
o PHP Scripts
o Database Class
5. Testing
o Test Cases
1. Overview
This project is a PHP-based demo application designed to illustrate basic web development
concepts. It includes features such as user registration, login, course enrollment, and data fetching. The
application uses MySQL for database management and Bootstrap for frontend styling.
Login
Process Description
1. Form Submission User submits login credentials.
2. Validation Check credentials against the database.
3. Session Creation Create a session for authenticated users.
4. Redirection Redirect to the user dashboard if account_type = 0 and to the admin
dashboard if account_type = 1.
Table 2. Login
Course Enrollment
Process Description
1. Course Listing User submits login credentials.
2. Enrollment Check credentials against the database.
3. Database Update Create a session for authenticated users.
4. Confirmation Redirect to the user dashboard if account_type = 0 and to the admin
dashboard if account_type = 1.
Table 3. Course Enrollment
Data Fetching
Process Description
1. User Data Fetch and display user-specific data with the use of viewdata($id)
mthod.
2. Course Data Fetch and display course details.
Table 4. Data Fetching
A. Client-Side
• Technologies: HTML, CSS, JavaScript
• Libraries: Bootstrap for styling
B. Server-Side
• Language: PHP
• Frameworks: None
C. Database
• Type: MySQL
• Management Tools: phpMyAdmin for database management
3. Database Design
1. Relation: Users
Attributes:
• user_id
• user_firstname (INT, Primary Key)
• user_lastname (VarChar 255)
• user_sex (VarChar 255)
• user_email (VarChar 255)
• user_name (VarChar 255)
• user_pass (VarChar 255)
• user_profile_picture (VarChar 255)
• account_type (VarChar 255)
Constraints:
• Primary Key: user_id
SQL Query
2. Relation: User_Address
Attributes:
• address_id
• user_id (INT, Primary Key)
• street (VarChar 255)
• barangay (VarChar 255)
• city VarChar 255)
• province (VarChar 255)
Constraints:
• Primary Key: address_id
• Foreign Key: user_id references Users(user_id)
SQL Query
4. Code Logic
/phpdemo
/bootstrap-4.5.3-dist
/bootstrap-5.3.3-dist
/classes
/includes
/ph-json
/uploads
check_email.php
check_file_size.php
...
userinfo.php
Code Snippet:
<?php
require_once('classes/database.php');
if (isset($_POST['email'])) {
$email = $_POST['email'];
$con = new database();
$query = $con->opencon()->prepare("SELECT user_email FROM users WHERE
user_email = ?");
$query->execute([$email]);
$existingUser = $query->fetch();
if ($existingUser) {
echo json_encode(['exists' => true]);
} else {
echo json_encode(['exists' => false]);
}
}
?>
Code:
<?php
if ($_SERVER["REQUEST_METHOD"] == "POST") {
if(isset($_FILES["profile_picture"])) {
$file_size = $_FILES["profile_picture"]["size"];
if ($file_size > 500000) {
echo json_encode(array("error" => "File size exceeds 500KB limit."));
} else {
echo json_encode(array("success" => "File size is within the limit."));
}
} else {
echo json_encode(array("error" => "No file uploaded."));
}
} else {
echo json_encode(array("error" => "Invalid request."));
}
?>
Code:
<?php
session_start();
require_once('classes/database.php');
header('Content-Type: application/json');
if (isset($_POST['current_password'])) {
$currentPassword = $_POST['current_password'];
$userId = $_SESSION['user_id'];
Code:
<?php
require_once('classes/database.php');
try {
$connection = $con->opencon();
if ($connection) {
$query = $connection->prepare("SELECT users.user_id,
users.user_firstname, users.user_lastname, users.user_birthday, users.user_sex,
users.user_name, users.user_profile_picture, CONCAT(user_address.city,', ',
user_address.province) AS address FROM users INNER JOIN user_address ON
users.user_id = user_address.user_id WHERE users.user_name LIKE ? OR users.user_id
LIKE ? OR CONCAT(user_address.city,', ', user_address.province) LIKE ? ");
$query->execute(["%$searchterm%","%$searchterm%","%$searchterm%"]);
$users = $query->fetchAll(PDO::FETCH_ASSOC);
$html = '';
foreach ($users as $user) {
$html .= '<tr>';
$html .= '<td>' . $user['user_id'] . '</td>';
$html .= '<td><img src="' .
htmlspecialchars($user['user_profile_picture']) . '" alt="Profile Picture"
style="width: 50px; height: 50px; border-radius: 50%;"></td>';
$html .= '<td>' . $user['user_firstname'] . '</td>';
$html .= '<td>' . $user['user_lastname'] . '</td>';
$html .= '<td>' . $user['user_birthday'] . '</td>';
$html .= '<td>' . $user['user_sex'] . '</td>';
$html .= '<td>' . $user['user_name'] . '</td>';
$html .= '<td>' . $user['address'] . '</td>';
$html .= '<td>';
$html .= '<form action="update.php" method="post"
style="display: inline;">';
$html .= '<input type="hidden" name="id" value="' .
$user['user_id'] . '">';
$html .= '<button type="submit" class="btn btn-primary btn-
sm">Edit</button>';
$html .= '</form>';
$html .= '<form method="POST" style="display: inline;">';
$html .= '<input type="hidden" name="id" value="' .
$user['user_id'] . '">';
$html .= '<input type="submit" name="delete" class="btn btn-
danger btn-sm" value="Delete" onclick="return confirm(\'Are you sure you want to
delete this user?\')">';
$html .= '</form>';
$html .= '</td>';
$html .= '</tr>';
}
echo $html;
} else {
echo json_encode(['error' => 'Database connection failed.']);
}
} catch (PDOException $e) {
echo json_encode(['error' => $e->getMessage()]);
}
} else {
echo json_encode(['error' => 'No search query provided.']);
}
}
?>
<script>
<?php
session_start();
require_once('classes/database.php');
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$connection = $con->opencon();
if (!$connection) {
echo json_encode(['error' => 'Database connection failed.']);
exit;
}
$action = $_POST['action'];
$courseId = isset($_POST['course_id']) ? (int)$_POST['course_id'] : 0;
switch ($action) {
case 'add':
if (!in_array($courseId, $_SESSION['selected_courses'])) {
$_SESSION['selected_courses'][] = $courseId;
}
break;
case 'remove':
if (($key = array_search($courseId, $_SESSION['selected_courses'])) !==
false) {
unset($_SESSION['selected_courses'][$key]);
}
break;
case 'save':
$userId = $_SESSION['user_id'];
echo json_encode
<script>
$(document).ready(function(){
$('#username').on('input', function(){
var username = $(this).val();
if(username.length > 0) {
$.ajax({
url: 'check_username.php',
method: 'POST',
data: {username: username},
dataType: 'json',
success: function(response) {
if(response.exists) {
$('#username').removeClass('is-valid').addClass('is-
invalid');
$('#usernameFeedback').text('Username is already taken.');
$('#nextButton').prop('disabled', true); // Disable the
Next button
} else {
$('#username').removeClass('is-invalid').addClass('is-
valid');
$('#usernameFeedback').text('');
$('#nextButton').prop('disabled', false); // Enable the
Next button
}
}
});
} else {
$('#username').removeClass('is-valid is-invalid');
$('#usernameFeedback').text('');
$('#nextButton').prop('disabled', false); // Enable the Next button if
username is empty
}
});
});
<script>
$(document).ready(function(){
$('#email').on('input', function(){
var email = $(this).val();
if(email.length > 0) {
$.ajax({
url: 'check_email.php',
method: 'POST',
data: {email: email},
dataType: 'json',
success: function(response) {
if(response.exists) {
$('#email').removeClass('is-valid').addClass('is-invalid');
$('#emailFeedback').text('Email is already taken.');
$('#nextButton').prop('disabled', true); // Disable the
Next button
} else {
$('#email').removeClass('is-invalid').addClass('is-valid');
$('#emailFeedback').text('');
$('#nextButton').prop('disabled', false); // Enable the
Next button
}
},
error: function(xhr, status, error) {
console.error('AJAX Error:', status, error);
}
});
} else {
$('#email').removeClass('is-valid is-invalid');
$('#emailFeedback').text('');
$('#nextButton').prop('disabled', false); // Enable the Next button if
email is empty
}
});
});
<script>
document.addEventListener("DOMContentLoaded", () => {
const form = document.querySelector("form");
const birthdayInput = document.getElementById("birthday");
const steps = document.querySelectorAll(".form-step");
let currentStep = 0;
window.nextStep = () => {
if (validateStep(currentStep)) {
steps[currentStep].classList.remove("form-step-active");
currentStep++;
steps[currentStep].classList.add("form-step-active");
}
};
window.prevStep = () => {
steps[currentStep].classList.remove("form-step-active");
currentStep--;
steps[currentStep].classList.add("form-step-active");
};
function validateStep(step) {
let valid = true;
const stepInputs = steps[step].querySelectorAll("input, select");
stepInputs.forEach(input => {
if (!validateInput(input)) {
valid = false;
}
});
return valid;
}
function validateInput(input) {
if (input.name === 'password') {
return validatePassword(input);
} else if (input.name === 'confirmPassword') {
return validateConfirmPassword(input);
} else {
if (input.checkValidity()) {
input.classList.remove("is-invalid");
input.classList.add("is-valid");
return true;
} else {
input.classList.remove("is-valid");
input.classList.add("is-invalid");
return false;
}
}
}
function validatePassword(passwordInput) {
const password = passwordInput.value;
const regex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[\W_]).{8,}$/;
if (regex.test(password)) {
passwordInput.classList.remove("is-invalid");
passwordInput.classList.add("is-valid");
return true;
} else {
passwordInput.classList.remove("is-valid");
passwordInput.classList.add("is-invalid");
return false;
}
}
function validateConfirmPassword(confirmPasswordInput) {
const passwordInput = form.querySelector("input[name='password']");
const password = passwordInput.value;
const confirmPassword = confirmPasswordInput.value;
<script>
var my_handlers = {
// fill province
fill_provinces: function() {
//selected region
var region_code = $(this).val();
// set selected text to input
var region_text = $(this).find("option:selected").text();
let region_input = $('#region-text');
region_input.val(region_text);
//clear province & city & barangay input
$('#province-text').val('');
$('#city-text').val('');
$('#barangay-text').val('');
//province
let dropdown = $('#province');
dropdown.empty();
// dropdown.append('<option selected="true" disabled>Choose
State/Province</option>');
dropdown.prop('selectedIndex', 0);
//city
let city = $('#city');
city.empty();
city.append('<option selected="true" disabled></option>');
city.prop('selectedIndex', 0);
//barangay
let barangay = $('#barangay');
barangay.empty();
barangay.append('<option selected="true" disabled></option>');
barangay.prop('selectedIndex', 0);
result.sort(function(a, b) {
return a.province_name.localeCompare(b.province_name);
});
});
},
//added selector
// fill city
fill_cities: function() {
//selected province
var province_code = $(this).val();
//city
let dropdown = $('#city');
dropdown.empty();
dropdown.append('<option selected="true" disabled>Choose
city/municipality</option>');
dropdown.prop('selectedIndex', 0);
//barangay
let barangay = $('#barangay');
barangay.empty();
barangay.append('<option selected="true" disabled></option>');
barangay.prop('selectedIndex', 0);
result.sort(function(a, b) {
return a.city_name.localeCompare(b.city_name);
});
});
},
// fill barangay
fill_barangays: function() {
// selected barangay
var city_code = $(this).val();
// barangay
let dropdown = $('#barangay');
dropdown.empty();
dropdown.append('<option selected="true" disabled>Choose
barangay</option>');
dropdown.prop('selectedIndex', 0);
result.sort(function(a, b) {
return a.brgy_name.localeCompare(b.brgy_name);
});
});
},
onchange_barangay: function() {
// set selected text to input
var barangay_text = $(this).find("option:selected").text();
let barangay_input = $('#barangay-text');
barangay_input.val(barangay_text);
},
};
$(function() {
// events
$('#region').on('change', my_handlers.fill_provinces);
$('#province').on('change', my_handlers.fill_cities);
$('#city').on('change', my_handlers.fill_barangays);
$('#barangay').on('change', my_handlers.onchange_barangay);
// load region
let dropdown = $('#region');
dropdown.empty();
dropdown.append('<option selected="true" disabled>Choose Region</option>');
dropdown.prop('selectedIndex', 0);
const url = 'ph-json/region.json';
// Populate dropdown with list of regions
$.getJSON(url, function(data) {
$.each(data, function(key, entry) {
dropdown.append($('<option></option>').attr('value',
entry.region_code).text(entry.region_name));
})
});
});
Page login.php
This script handles user login, session management, and redirects
Usage
users to a certain page based on account type.
Code
require_once('classes/database.php');
Includes the database class $con = new database();
and starts a session. session_start();
if (isset($_SESSION['username']) &&
isset($_SESSION['account_type'])) {
if ($_SESSION['account_type'] == 0) {
Redirects users if they are header('location:index.php');
already logged in based on } else if ($_SESSION['account_type'] == 1) {
header('location:user_account.php');
their account type.
exit();
}
$error = ""; // Initialize error variable
if (isset($_POST['login'])) {
$username = $_POST['user'];
$password = $_POST['pass'];
$result = $con->check($username, $password);
if ($result) {
$_SESSION['username'] = $result['user_name'];
$_SESSION['account_type'] =
$result['account_type'];
Validates login credentials, $_SESSION['user_id'] = $result['user_id'];
starts a session, and redirects $_SESSION['profilepicture'] =
$result['user_profile_picture'];
based on account type. if ($result['account_type'] == 0) {
header('location:index.php');
} else if ($result['account_type'] == 1) {
header('location:user_account.php');
}
exit();
} else {
$error = "Incorrect username or password. Please
try again.";
}
}
Page user_account.php
This script allows logged-in users to update their password and
Usage
address.
Code
require_once('classes/database.php');
$con = new Database();
session_start();
Starts a session, checks if the
if (!isset($_SESSION['username']) ||
user is logged in, and retrieves $_SESSION['account_type'] != 1) {
user data. header('location:login.php');
exit();
}
$id = $_SESSION['user_id'];
$data = $con->viewdata($id);
if (isset($_POST['updatepassword'])) {
$userId = $_SESSION['user_id'];
$currentPassword = $_POST['current_password'];
$newPassword = $_POST['new_password'];
$confirmPassword = $_POST['confirm_password'];
if ($con->validateCurrentPassword($userId,
$currentPassword)) {
if ($currentPassword === $newPassword) {
header('Location:
user_account.php?status=samepassword');
exit();
}
Method opencon()
Description Establishes a connection to the MySQL database using PDO.
Return PDO instance representing the database connection.
Code:
function opencon(){
return new PDO('mysql:host=localhost; dbname=student', 'root', '');
}
Method check()
Description Checks if a user with the provided username exists and if the password matches.
$username: Username of the user to be checked.
Parameters
$password: Password to be verified.
Return User data as an associative array if authentication succeeds, otherwise false.
Code:
function check($username, $password) {
// Open database connection
$con = $this->opencon();
Method signupUser()
Description Registers a new user into the database.
$firstname: First name of the user.
$lastname: Last name of the user.
$birthday: Date of birth of the user.
$sex: Sex of the user.
Parameters
$email: Email address of the user.
$username: Username chosen by the user.
$password: Password chosen by the user (hashed).
$profilePicture: Path to the user's profile picture.
Return ID of the newly inserted user.
Code:
function signupUser($firstname, $lastname, $birthday, $sex, $email, $username,
$password, $profilePicture)
{
$con = $this->opencon();
// Save user data along with profile picture path to the database
$con->prepare("INSERT INTO users (user_firstname, user_lastname,
user_birthday, user_sex, user_email, user_name, user_pass, user_profile_picture)
VALUES (?,?,?,?,?,?,?,?)")->execute([$firstname, $lastname, $birthday, $sex,
$email, $username, $password, $profilePicture]);
return $con->lastInsertId();
}
Method insertAddress()
Description Inserts address information for a user into the database.
$user_id: ID of the user whose address is being inserted.
Parameters
$street: Street name.
$barangay: Barangay name.
$city: City name.
$province: Province name.
Return Boolean indicating success or failure of the insertion.
Code:
function insertAddress($user_id, $street, $barangay, $city, $province)
{
try
{
$con = $this->opencon();
$con->beginTransaction();
$con->prepare("INSERT INTO user_address (user_id, street, barangay, city,
province) VALUES (?,?,?,?,?)")->execute([$user_id, $street, $barangay, $city,
$province]);
$con->commit();
return true;
}
catch (PDOException $e) {
$con->rollBack();
return false;
}
Method view()
Description Retrieves a list of users along with their addresses from the database.
Return Array of user data with addresses.
Code:
function view()
{
$con = $this->opencon();
return $con->query("SELECT users.user_id, users.user_firstname,
users.user_lastname, users.user_birthday, users.user_sex, users.user_name,
users.user_profile_picture, CONCAT(user_address.city,', ', user_address.province)
AS address from users INNER JOIN user_address ON users.user_id =
user_address.user_id")->fetchAll();
}
Method delete()
Description Deletes a user and their associated address from the database.
Parameters $id: ID of the user to be deleted.
Return Boolean indicating success or failure of the deletion.
Code:
function delete($id)
{
try {
$con = $this->opencon();
$con->beginTransaction();
// Delete user
$query2 = $con->prepare("DELETE FROM users WHERE user_id = ?");
$query2->execute([$id]);
$con->commit();
return true; // Deletion successful
} catch (PDOException $e) {
$con->rollBack();
return false;
}
}
Method viewdata()
Description Retrieves detailed information of a specific user from the database.
Parameters $id: ID of the user whose data is being retrieved.
Return User data as an associative array.
Code:
function viewdata($id){
try {
$con = $this->opencon();
$query = $con->prepare("SELECT users.user_id, users.user_name,
users.user_firstname, users.user_lastname, users.user_birthday, users.user_sex,
users.user_pass, users.user_profile_picture, user_address.street,
user_address.barangay, user_address.city, user_address.province FROM users INNER
JOIN user_address ON users.user_id = user_address.user_id WHERE users.user_id = ?
");
$query->execute([$id]);
return $query->fetch();
} catch (PDOException $e) {
// Handle the exception (e.g., log error, return empty array, etc.)
return [];
}
}
Method updateUser()
Description Updates user information in the database.
$user_id: ID of the user to be updated.
$firstname: Updated first name.
$lastname: Updated last name.
Parameters $birthday: Updated date of birth.
$sex: Updated gender.
$username: Updated username.
$password: Updated password (hashed).
Return Boolean indicating success or failure of the update.
Code:
function updateUser($user_id, $firstname, $lastname, $birthday,$sex, $username,
$password) {
try {
$con = $this->opencon();
$con->beginTransaction();
$query = $con->prepare("UPDATE users SET user_firstname=?,
user_lastname=?,user_birthday=?, user_sex=?,user_name=?, user_pass=? WHERE
user_id=?");
$query->execute([$firstname, $lastname,$birthday,$sex,$username, $password,
$user_id]);
// Update successful
$con->commit();
return true;
} catch (PDOException $e) {
// Handle the exception (e.g., log error, return false, etc.)
$con->rollBack();
return false; // Update failed
}
}
Method updateUserAddress ()
Description Updates address information for a user in the database.
$user_id: ID of the user whose address is being updated.
$street: Updated street name.
Parameters $barangay: Updated barangay name.
$city: Updated city name.
$province: Updated province name.
Return Boolean indicating success or failure of the update.
Code:
function updateUserAddress($user_id, $street, $barangay, $city, $province){
try {
$con = $this->opencon();
$con->beginTransaction();
$query = $con->prepare("UPDATE user_address SET street=?, barangay=?,
city=?, province=? WHERE user_id=?");
$query->execute([$street, $barangay, $city, $province, $user_id]);
$con->commit();
return true; // Update successful
} catch (PDOException $e) {
// Handle the exception (e.g., log error, return false, etc.)
$con->rollBack();
return false; // Update failed
}
}
Method getusercount()
Description Retrieves the count of male and female users from the database.
Return Associative array containing counts of male and female users.
Code:
function getusercount()
{
$con = $this->opencon();
return $con->query("SELECT SUM(CASE WHEN user_sex = 'Male' THEN 1 ELSE 0 END)
AS male_count,
SUM(CASE WHEN user_sex = 'Female' THEN 1 ELSE 0 END) AS female_count FROM
users;")->fetch();
}
Method checkEmailExists()
Description Checks if an email address already exists in the database.
Parameters $email: Email address to be checked.
Return Email address if it exists, otherwise false.
Code:
function checkEmailExists($email) {
$con = $this->opencon();
$query = $this->$con->prepare("SELECT user_email FROM users WHERE user_email =
?");
$query->execute([$email]);
return $query->fetch();
}
Method validateCurrentPassword()
Description Validates the current password of a user.
$userId: ID of the user whose password is being validated.
Parameters
$currentPassword: Current password to be validated.
Return Email address if it exists, otherwise false.
Code:
function validateCurrentPassword($userId, $currentPassword) {
// Open database connection
$con = $this->opencon();
Code:
function updatePassword($userId, $hashedPassword){
try {
$con = $this->opencon();
$con->beginTransaction();
$query = $con->prepare("UPDATE users SET user_pass = ? WHERE user_id = ?");
$query->execute([$hashedPassword, $userId]);
// Update successful
$con->commit();
return true;
} catch (PDOException $e) {
// Handle the exception (e.g., log error, return false, etc.)
$con->rollBack();
return false; // Update failed
}
}
Method updateUserProfilePicture()
Description Updates the profile picture path of a user in the database.
$userID: ID of the user whose profile picture path is being updated.
Parameters
$profilePicturePath: New profile picture path.
Return Boolean indicating success or failure of the update.
Code:
function updateUserProfilePicture($userID, $profilePicturePath) {
try {
$con = $this->opencon();
$con->beginTransaction();
$query = $con->prepare("UPDATE users SET user_profile_picture = ? WHERE user_id
= ?");
$query->execute([$profilePicturePath, $userID]);
// Update successful
$con->commit();
return true;
} catch (PDOException $e) {
// Handle the exception (e.g., log error, return false, etc.)
$con->rollBack();
return false; // Update failed
}
}
Method fetchAvailableCourses ()
Description Retrieves available courses for a user to enroll in.
Parameters $userId: ID of the user for whom available courses are fetched.
Return Array of available courses.
Code:
function fetchAvailableCourses($userId) {
try {
$con = $this->opencon();
$query = $con->prepare("
SELECT c.course_id, c.course_name, c.course_description,
CASE WHEN e.course_id IS NOT NULL THEN 'Enrolled' ELSE 'Not Enrolled'
END AS enrolled_status
FROM courses c
LEFT JOIN enrollments e ON c.course_id = e.course_id AND e.user_id = ?
WHERE e.course_id IS NULL OR e.user_id != ?
");
$query->execute([$userId, $userId]);
return $query->fetchAll(PDO::FETCH_ASSOC);
} catch (PDOException $e) {
// Handle the exception (e.g., log error, return false, etc.)
return [];
}
}
Method fetchSelectedCourses ()
Description Retrieves details of selected courses based on provided IDs.
Parameters $selectedCourseIds: Array of course IDs for which details are to be fetched.
Return Array of selected courses details.
Code:
function fetchSelectedCourses($selectedCourseIds) {
try {
$con = $this->opencon();
$placeholders = str_repeat('?,', count($selectedCourseIds) - 1) . '?';
$query = $con->prepare("SELECT course_id, course_name, course_description
FROM courses WHERE course_id IN ($placeholders)");
$query->execute($selectedCourseIds);
return $query->fetchAll(PDO::FETCH_ASSOC);
} catch (PDOException $e) {
// Handle the exception (e.g., log error, return false, etc.)
return [];
}
}
5. Testing
Test cases are detailed, step-by-step instructions designed to validate that a software application
performs as expected. Each test case includes specific inputs, execution conditions, and expected
outcomes. They are essential components of software testing, ensuring comprehensive examination of
functionality, usability, and performance.
1. Input Validation
2. File Upload
3. Authentication