In our previous Python tutorial, we have explained how to develop School Management System with PHP & MySQL. In this tutorial, we will explain how to develop Project Management System with PHP and MySQL.
The Project Management System (PMS) is a web application used for project planning, resource allocation, organize, tracking, estimation etc. Depending on the requirement, the project management systems are developed to manage project components such as cost control, budget management, quality management, estimation, planning, assignment and much more.
So if you’re are developer and looking for solution to develop your own web based project management system, then you’re here at the right place. In this tutorial, you will learn how to develop your own project management system with PHP and MySQL.
Here we will develop a simple project management system with live demo to manage clients, projects and tasks with milestone, costs and hours. You can easily customize this projects according to your requirements.
Also, read:
- User Management System with PHP & MySQL
- Build Helpdesk System with jQuery, PHP & MySQL
- Build Online Voting System with PHP & MySQL
- School Management System with PHP & MySQL
- Hospital Management System with PHP & MySQL
So let’s implement Project Management System with PHP and MySQL. The major files are:
- index.php
- clients.php
- projects.php
- tasks.php
- User.php: A class contains users methods.
- Clients.php: A class contains clients methods.
- Project.php: A class contains project methods.
- Tasks.php: A class contains task methods.
Step1: Create MySQL Database Table
First we will create MySQL database tables for our project management system. The major tables are following.
We will create pm_users
table to store users details.
CREATE TABLE `pm_users` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `email` varchar(255) DEFAULT NULL, `first_name` varchar(255) DEFAULT NULL, `last_name` varchar(255) DEFAULT NULL, `role` enum('admin','manager','employee') DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
We will create pm_clients
table to store clients details.
CREATE TABLE `pm_clients` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `name` varchar(255) DEFAULT NULL, `website` varchar(255) DEFAULT NULL, `industry` varchar(255) DEFAULT NULL, `revenue` varchar(255) DEFAULT NULL, `description` varchar(512) DEFAULT '', `phone` varchar(255) DEFAULT NULL, `street1` varchar(255) DEFAULT NULL, `street2` varchar(255) DEFAULT NULL, `city` varchar(255) DEFAULT NULL, `state` varchar(255) DEFAULT NULL, `zip` int(11) DEFAULT NULL, `country` varchar(255) DEFAULT NULL, `notes` text, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
We will create pm_projects
table to store project details.
CREATE TABLE `pm_projects` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `project_name` varchar(255) DEFAULT NULL, `project_manager_id` int(11) unsigned NOT NULL, `start_date` date DEFAULT NULL, `hourly_rate` float DEFAULT NULL, `budget` float DEFAULT NULL, `active` int(11) unsigned NOT NULL, `status_id` int(11) unsigned NOT NULL, `client_id` int(11) unsigned DEFAULT NULL, `total_hours` float DEFAULT NULL, `labor_costs` float DEFAULT NULL, `material_cost` float DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
We will create pm_tasks
table to store tasks details.
CREATE TABLE `pm_tasks` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `task_name` varchar(255) DEFAULT NULL, `instruction` varchar(255) DEFAULT NULL, `status` int(11) unsigned NOT NULL, `total_hours` float DEFAULT NULL, `milestone` int(11) unsigned DEFAULT NULL, `project_id` int(11) unsigned NOT NULL, `employee_id` int(11) unsigned DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
We will create pms_hours
table to store hours details.
CREATE TABLE `pms_hours` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `date` date DEFAULT NULL, `time` float DEFAULT NULL, `work_completed` varchar(255) DEFAULT NULL, `task_id` int(11) unsigned NOT NULL, `project_id` int(11) unsigned NOT NULL, `employee_id` int(11) unsigned NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Step2: Create User Login Form
We will design user login form to allow login to access system. The manager will manage the clients and projects section while employee will manage tasks section to add work hours.
<div class="panel panel-info"> <div class="panel-heading" style="background:#00796B;color:white;"> <div class="panel-title">Log In</div> </div> <div style="padding-top:30px" class="panel-body" > <?php if ($loginMessage != '') { ?> <div id="login-alert" class="alert alert-danger col-sm-12"><?php echo $loginMessage; ?></div> <?php } ?> <form id="loginform" class="form-horizontal" role="form" method="POST" action=""> <div style="margin-bottom: 25px" class="input-group"> <span class="input-group-addon"><i class="glyphicon glyphicon-user"></i></span> <input type="text" class="form-control" id="email" name="email" value="<?php if(!empty($_POST["email"])) { echo $_POST["email"]; } ?>" placeholder="email" style="background:white;" required> </div> <div style="margin-bottom: 25px" class="input-group"> <span class="input-group-addon"><i class="glyphicon glyphicon-lock"></i></span> <input type="password" class="form-control" id="password" name="password" value="<?php if(!empty($_POST["password"])) { echo $_POST["password"]; } ?>" placeholder="password" required> </div> <div style="margin-top:10px" class="form-group"> <div class="col-sm-12 controls"> <input type="submit" name="login" value="Login" class="btn btn-info"> </div> </div> </form> </div> </div>
We will implement user login functionality using login()
method on login form submit. We will display the system section according to user role.
$loginMessage = ''; if(!empty($_POST["login"]) && !empty($_POST["email"]) && !empty($_POST["password"])) { $user->email = $_POST["email"]; $user->password = $_POST["password"]; if($user->login()) { if($_SESSION["role"] == 'manager') { header("Location: clients.php"); } else if($_SESSION["role"] == 'employee') { header("Location: tasks.php"); } } else { $loginMessage = 'Invalid login! Please try again.'; } } else { $loginMessage = 'Fill all fields.'; }
In class User.php
, we will implement login()
method and store user login details to SESSION variables.
public function login(){ if($this->email && $this->password) { $sqlQuery = " SELECT * FROM ".$this->userTable." WHERE email = ? AND password = ?"; $stmt = $this->conn->prepare($sqlQuery); $password = md5($this->password); $stmt->bind_param("ss", $this->email, $password); $stmt->execute(); $result = $stmt->get_result(); if($result->num_rows > 0){ $user = $result->fetch_assoc(); $_SESSION["userid"] = $user['id']; $_SESSION["role"] = $user['role']; $_SESSION["name"] = $user['first_name']." ".$user['last_name']; return 1; } else { return 0; } } else { return 0; } }
Step3: Manage Clients
We will create HTML to display clients list with jQuery Datatables
with Ajax.
<table id="clientListing" class="table table-bordered table-striped"> <thead> <tr> <th>#</th> <th>Name</th> <th>Website</th> <th>Industry</th> <th>Phone</th> <th></th> <th></th> <th></th> </tr> </thead> </table>
We will initialize DataTable
and make ajax request to client_action.php
with action listClients
to load clients list.
var clientRecords = $('#clientListing').DataTable({ "lengthChange": false, "processing":true, "serverSide":true, "bFilter": false, 'serverMethod': 'post', "order":[], "ajax":{ url:"client_action.php", type:"POST", data:{action:'listClients'}, dataType:"json" }, "columnDefs":[ { "targets":[0, 5, 6, 7], "orderable":false, }, ], "pageLength": 10 });
We will create method listClients()
in class Clients.php
to return clients list as JSON data.
public function listClients(){ $sqlQuery = "SELECT * FROM ".$this->clientsTable." "; if(!empty($_POST["search"]["value"])){ $sqlQuery .= 'where(id LIKE "%'.$_POST["search"]["value"].'%" '; $sqlQuery .= ' OR name LIKE "%'.$_POST["search"]["value"].'%" '; $sqlQuery .= ' OR website LIKE "%'.$_POST["search"]["value"].'%" '; $sqlQuery .= ' OR industry LIKE "%'.$_POST["search"]["value"].'%" '; $sqlQuery .= ' OR phone LIKE "%'.$_POST["search"]["value"].'%") '; } if(!empty($_POST["order"])){ $sqlQuery .= 'ORDER BY '.$_POST['order']['0']['column'].' '.$_POST['order']['0']['dir'].' '; } else { $sqlQuery .= 'ORDER BY id DESC '; } if($_POST["length"] != -1){ $sqlQuery .= 'LIMIT ' . $_POST['start'] . ', ' . $_POST['length']; } $stmt = $this->conn->prepare($sqlQuery); $stmt->execute(); $result = $stmt->get_result(); $stmtTotal = $this->conn->prepare("SELECT * FROM ".$this->clientsTable); $stmtTotal->execute(); $allResult = $stmtTotal->get_result(); $allRecords = $allResult->num_rows; $displayRecords = $result->num_rows; $records = array(); while ($client = $result->fetch_assoc()) { $rows = array(); $rows[] = $client['id']; $rows[] = ucfirst($client['name']); $rows[] = $client['website']; $rows[] = $client['industry']; $rows[] = $client['phone']; $rows[] = '<button type="button" name="view" id="'.$client["id"].'" class="btn btn-info btn-xs view"><span class="glyphicon glyphicon-file" title="View"></span></button>'; $rows[] = '<button type="button" name="update" id="'.$client["id"].'" class="btn btn-warning btn-xs update"><span class="glyphicon glyphicon-edit" title="Edit"></span></button>'; $rows[] = '<button type="button" name="delete" id="'.$client["id"].'" class="btn btn-danger btn-xs delete" ><span class="glyphicon glyphicon-remove" title="Delete"></span></button>'; $records[] = $rows; } $output = array( "draw" => intval($_POST["draw"]), "iTotalRecords" => $displayRecords, "iTotalDisplayRecords" => $allRecords, "data" => $records ); echo json_encode($output); }
We will also handle functionality to add, edit, view and delete functionality. We will insert new clients using insert()
methods.
public function insert(){ if($this->name) { $stmt = $this->conn->prepare(" INSERT INTO ".$this->clientsTable."(`name`, `website`, `industry`, `description`, `phone`,`address`,`country`) VALUES(?,?,?,?,?,?,?)"); $this->name = htmlspecialchars(strip_tags($this->name)); $this->website = htmlspecialchars(strip_tags($this->website)); $this->industry = htmlspecialchars(strip_tags($this->industry)); $this->description = htmlspecialchars(strip_tags($this->description)); $this->phone = htmlspecialchars(strip_tags($this->phone)); $this->address = htmlspecialchars(strip_tags($this->address)); $this->country = htmlspecialchars(strip_tags($this->country)); $stmt->bind_param("sssssss", $this->name, $this->website, $this->industry, $this->description, $this->phone, $this->address, $this->country); if($stmt->execute()){ return true; } } }
Step4: Manage Projects
We will create HTML to display project listing with jQuery Datatable
with ajax request.
<table id="projectListing" class="table table-bordered table-striped"> <thead> <tr> <th>#</th> <th>Client</th> <th>Project Name</th> <th>Status</th> <th>Project Manager</th> <th>Hourly Rate</th> <th>Budget</th> <th></th> <th></th> <th></th> </tr> </thead> </table>
We will initialize jQuery Datatable and make ajax request to project_action.php
with action listProjects
to display project listing.
var clientRecords = $('#projectListing').DataTable({ "lengthChange": false, "processing":true, "serverSide":true, "bFilter": false, 'serverMethod': 'post', "order":[], "ajax":{ url:"project_action.php", type:"POST", data:{action:'listProjects'}, dataType:"json" }, "columnDefs":[ { "targets":[0, 7, 8, 9], "orderable":false, }, ], "pageLength": 10 });
We will create method listProjects()
in class Project.php
to get project list and return as JSON data to display jQuery Datatable.
public function listProjects(){ $sqlQuery = "SELECT p.id, p.project_name, p.hourly_rate, p.budget, u.first_name, u.last_name, s.status, c.name as client FROM ".$this->projectTable." p LEFT JOIN ".$this->usersTable." u ON u.id = p.project_manager_id LEFT JOIN ".$this->statusTable." s ON s.id = p.status_id LEFT JOIN ".$this->clientTable." c ON c.id = p.client_id "; if(!empty($_POST["search"]["value"])){ $sqlQuery .= ' WHERE (p.project_name LIKE "%'.$_POST["search"]["value"].'%" '; $sqlQuery .= ' OR p.hourly_rate LIKE "%'.$_POST["search"]["value"].'%" '; $sqlQuery .= ' OR p.budget LIKE "%'.$_POST["search"]["value"].'%") '; } if(!empty($_POST["order"])){ $sqlQuery .= 'ORDER BY '.$_POST['order']['0']['column'].' '.$_POST['order']['0']['dir'].' '; } else { $sqlQuery .= 'ORDER BY p.id DESC '; } if($_POST["length"] != -1){ $sqlQuery .= 'LIMIT ' . $_POST['start'] . ', ' . $_POST['length']; } $stmt = $this->conn->prepare($sqlQuery); $stmt->execute(); $result = $stmt->get_result(); $stmtTotal = $this->conn->prepare("SELECT * FROM ".$this->projectTable); $stmtTotal->execute(); $allResult = $stmtTotal->get_result(); $allRecords = $allResult->num_rows; $displayRecords = $result->num_rows; $records = array(); while ($project = $result->fetch_assoc()) { $rows = array(); $rows[] = $project['id']; $rows[] = ucfirst($project['client']); $rows[] = ucfirst($project['project_name']); $rows[] = $project['status']; $rows[] = ucfirst($project['first_name'])." ".ucfirst($project['last_name']); $rows[] = "$".$project['hourly_rate']; $rows[] = "$".$project['budget']; $rows[] = '<button type="button" name="view" id="'.$project["id"].'" class="btn btn-info btn-xs view"><span class="glyphicon glyphicon-file" title="View"></span></button>'; $rows[] = '<button type="button" name="update" id="'.$project["id"].'" class="btn btn-warning btn-xs update"><span class="glyphicon glyphicon-edit" title="Edit"></span></button>'; $rows[] = '<button type="button" name="delete" id="'.$project["id"].'" class="btn btn-danger btn-xs delete" ><span class="glyphicon glyphicon-remove" title="Delete"></span></button>'; $records[] = $rows; } $output = array( "draw" => intval($_POST["draw"]), "iTotalRecords" => $displayRecords, "iTotalDisplayRecords" => $allRecords, "data" => $records ); echo json_encode($output); }
We will create new project using insert()
method from Project.php
class.
public function insert(){ if($this->project_name) { $stmt = $this->conn->prepare(" INSERT INTO ".$this->projectTable."(`project_name`, `client_id`, `project_manager_id`, `active`, `hourly_rate`,`budget`,`status_id`) VALUES(?,?,?,?,?,?,?)"); $this->project_name = htmlspecialchars(strip_tags($this->project_name)); $this->client_id = htmlspecialchars(strip_tags($this->client_id)); $this->project_manager_id = htmlspecialchars(strip_tags($this->project_manager_id)); $this->active = htmlspecialchars(strip_tags($this->active)); $this->hourly_rate = htmlspecialchars(strip_tags($this->hourly_rate)); $this->budget = htmlspecialchars(strip_tags($this->budget)); $this->status_id = htmlspecialchars(strip_tags($this->status_id)); $stmt->bind_param("siiissi", $this->project_name, $this->client_id, $this->project_manager_id, $this->active, $this->hourly_rate, $this->budget, $this->status_id); if($stmt->execute()){ return true; } } }
Step5: Manage Tasks
We will create HTML to display tasks listing with jQuery Datatable.
<table id="taskListing" class="table table-bordered table-striped"> <thead> <tr> <th></th> <th>Id</th> <th>Project Name</th> <th>Task Name</th> <th>Milestone</th> <th>Total Hours</th> <th>Status</th> <th>Instructions</th> </tr> </thead> </table>
We will initialize the jQuery Datatable with action listTasks
to list the tasks details.
var taskRecords = $('#taskListing').DataTable({ "lengthChange": false, "processing":true, "serverSide":true, "bFilter": false, 'serverMethod': 'post', "order":[], "ajax":{ url:"tasks_action.php", type:"POST", data:{action:'listTasks'}, dataType:"json" }, "columnDefs":[ { "targets":[0], "orderable":false, "className": 'details-control', } ], "pageLength": 10 });
We will create method listTasks()
in class Tasks.php
to get the list of tasks with details and return as JSON data to display in jQuery Datatable.
public function listTasks(){ $sqlQuery = "SELECT t.id, t.task_name, t.instruction, t.total_hours, s.status, p.project_name, m.name as milestone, u.first_name, u.last_name FROM ".$this->tasksTable." t LEFT JOIN ".$this->projectsTable." p ON p.id = t.project_id LEFT JOIN ".$this->milestoneTable." m ON m.id = t.milestone_id LEFT JOIN ".$this->userTable." u ON u.id = t.employee_id LEFT JOIN ".$this->taskstatusTable." s ON s.id = t.status_id WHERE t.employee_id = '".$_SESSION["userid"]."'"; if(!empty($_POST["search"]["value"])){ $sqlQuery .= ' AND (t.task_name LIKE "%'.$_POST["search"]["value"].'%" '; $sqlQuery .= ' OR t.status_id LIKE "%'.$_POST["search"]["value"].'%" '; $sqlQuery .= ' OR t.total_hours LIKE "%'.$_POST["search"]["value"].'%") '; } if(!empty($_POST["order"])){ $sqlQuery .= 'ORDER BY '.$_POST['order']['0']['column'].' '.$_POST['order']['0']['dir'].' '; } else { $sqlQuery .= 'ORDER BY t.id DESC '; } if($_POST["length"] != -1){ $sqlQuery .= 'LIMIT ' . $_POST['start'] . ', ' . $_POST['length']; } $stmt = $this->conn->prepare($sqlQuery); $stmt->execute(); $result = $stmt->get_result(); $stmtTotal = $this->conn->prepare("SELECT * FROM ".$this->tasksTable." WHERE employee_id = '".$_SESSION["userid"]."'"); $stmtTotal->execute(); $allResult = $stmtTotal->get_result(); $allRecords = $allResult->num_rows; $displayRecords = $result->num_rows; $records = array(); while ($task = $result->fetch_assoc()) { $rows = array(); $rows[] = '<span id="'.$task['id'].'"> </span>'; $rows[] = $task['id']; $rows[] = ucfirst($task['project_name']); $rows[] = ucfirst($task['task_name']); $rows[] = $task['milestone']; $rows[] = $task['total_hours']; $rows[] = $task['status']; $rows[] = $task['instruction']; $records[] = $rows; } $output = array( "draw" => intval($_POST["draw"]), "iTotalRecords" => $displayRecords, "iTotalDisplayRecords" => $allRecords, "data" => $records ); echo json_encode($output); }
We will also display tasks’s hours details by clicking +
button to add more hours details for the tasks.
$('#taskListing tbody').on('click', 'td.details-control', function () { var tr = $(this).closest('tr'); var row = taskRecords.row( tr ); if ( row.child.isShown() ) { row.child.hide(); tr.removeClass('shown'); } else { var id = $(this).find('span').attr('id'); var action = "getHours"; $.ajax({ url:"tasks_action.php", method:"POST", dataType:"json", data:{id:id, action:action}, success:function(data) { row.child( format(id, data)).show(); tr.addClass('shown'); } }); } });
We will insert the tasks hours details with insertHours()
method from class Tasks.php
public function insertHours(){ if($this->task_id && $this->time && $this->work && $this->date) { $stmt = $this->conn->prepare(" INSERT INTO ".$this->hoursTable."(`date`, `time`, `work_completed`, `task_id`, `employee_id`) VALUES(?,?,?,?,?)"); $this->date = htmlspecialchars(strip_tags($this->date)); $this->time = htmlspecialchars(strip_tags($this->time)); $this->work = htmlspecialchars(strip_tags($this->work)); $this->task_id = htmlspecialchars(strip_tags($this->task_id)); $stmt->bind_param("sssii", $this->date, $this->time, $this->work, $this->task_id, $_SESSION["userid"]); if($stmt->execute()){ return true; } } }
You may also like:
- User Management System with PHP & MySQL
- Datatables Add Edit Delete with Ajax, PHP & MySQL
- Build Helpdesk System with jQuery, PHP & MySQL
- Build Online Voting System with PHP & MySQL
- School Management System with PHP & MySQL
- DataTables Add Edit Delete with CodeIgniter
- Create RESTful API using CodeIgniter
- Build Reusable Captcha Script with PHP
- Product Search Filtering using Ajax, PHP & MySQL
- Image Upload and Crop in Modal with jQuery, PHP & MySQL
- Build Push Notification System with PHP & MySQL
- Project Management System with PHP and MySQL
- Hospital Management System with PHP & MySQL
- Build Newsletter System with PHP and MySQL
- Skeleton Screen Loading Effect with Ajax and PHP
- Build Discussion Forum with PHP and MySQL
- Customer Relationship Management (CRM) System with PHP & MySQL
- Online Exam System with PHP & MySQL
- Expense Management System with PHP & MySQL
You can view the live demo from the Demo link and can download the script from the Download link below.
Employee Add Task button not working in the demo or the downloaded content. The modal for the add Task is missing on the task.php page.
I keep getting this error: (Uncaught TypeError: $(…).DataTable is not a function) on the floowing code:
var taskRecords = $(‘#taskListing’).DataTable({
I moved the jquery lib to the top of the header and still getting the error.
Employee –> Add Task not working..Model Window not available in the Code
You can download the project zip file for all files. thanks!
I mean to say ..still Employee –> Add Task is not working even in Demo as well. Thanks. I like this application.
I am checking this. thanks!
not working, invalid json response and
Parse error: syntax error, unexpected ‘list’ (T_LIST), expecting identifier (T_STRING) in C:\xampp\htdocs\coba5\class\Clients.php on line 90
Try to debug, may there are error. Thanks!
not working
You can share more details with code to help you. thanks!
Hello,A
And you can enter the update function without refreshing the page?
Your tutorials are all great, but I miss the automatic update without refreshing the page.
Regards
You can check the demo, we have handled record add, update and delete without page refresh. Thanks!
WOW !!! Your blog is very beautiful and many helpful for us. I can learn a lot. Thanks for having us.