Build a Project Management Application From Scratch

pm employee screenshot 1

What is a Project Management Application?

A project management application is a software system used for project planning, resource allocation, tracking of project components, and change management.

What is in a Project Management Application?

We will be creating a simple and easily customizable PM system to tracks projects, including milestones, tasks, hours, costs, and more. Since every project is unique, this tutorial merely means to build an application foundation; you should be able to easily extend it based on your requirements.

In our project management application, employees will be able to:

  • View their tasks

  • Log in hours worked

Managers will be able to:

  • Manage projects

  • Manage milestones

  • Manage tasks

  • Manage costs and hours

  • Manage employee resources

The Building Blocks of a Project Management System

Here are the primary functions of a project management system:

  • Project planning: To define a project schedule, a project manager may use the software to map project tasks and visually describe task interactions.

  • Task management: Allows the project manager to create and assign tasks, establish deadlines, and produce status reports.

  • Resource management: Defines responsibilities – who is supposed to do what.

  • Budgeting and cost tracking: A good project management application facilitates budget reporting as well as viewing, notifying, and updating costs for stakeholders.

  • Time tracking: The software must have the ability to track time spent on all tasks and maintain records for third-party consultants.

enter image description here

System Requirements

  • PHP 5.3+

  • MySQL or MariaDB

  • phpGrid

Creating a Project Management Database

We will start by creating our project management database. The main tables we will use are:

  • Clients – customer company data

  • Contacts – client contact data. A client can have one or more contacts.

  • Projects – project information

  • Milestones – project milestone

  • Tasks – project tasks

  • Hours – time spent on each task

  • Costs – cost of a task

  • Users – user data; one can be either an employee or a manager

db table list

Other tables (lookup tables):

  • ms_status –

  • proj_status –

  • task_status –

Complete Database Schema Diagram

A database schema is the structure that represents the logical view of the entire database: tables, views, and primary and foreign keys. A database schema includes all entities and the relationships between them.

Below is the database diagram of our simple project management application. The key symbol in each table represents the table’s primary key while the magnifying glass indicates a foreign key linking it to another table (lookup table) in the database.

The diagram is made with DBSchema.

schema diagram

Setup phpGrid

Our simple project management contains many DataGrids. The DataGrid is a spreadsheet-like data table which displays rows and columns representing records and fields from the database table. The DataGrid provides the end-user with the ability to read and write to the database tables on a web page.

To create the DataGrid, we use a dataGrid tool from phpGrid . The reason why we use a tool instead of building our grids from scratch is that developing a DataGrid in php is usually extremely tedious and prone to errors. The phpGrid DataGrid library will handle all internal database CRUD (Create, Remove, Update, and Delete) operations for us offering faster and better results with minimal coding.

To install phpGrid, follow these steps:

  1. Unzip the phpGrid download file.
  2. Upload the phpGrid folder to the phpGrid folder.
  3. Complete the installation by configuring the conf.php file.

Before we begin coding, we must include the following information in “conf.php,” the phpGrid configuration file. Here is an example of the database connection settings:

1
2
3
4
5
6
define('PHPGRID_DB_HOSTNAME', 'localhost');
define('PHPGRID_DB_USERNAME', 'root');
define('PHPGRID_DB_PASSWORD', '');
define('PHPGRID_DB_NAME', 'custom_pm');
define('PHPGRID_DB_TYPE', 'mysql');
define('PHPGRID_DB_CHARSET','utf8');
  • PHPGRID_DB_HOSTNAME – web server IP or host name

  • PHPGRID_DB_USERNAME – database user name

  • PHPGRID_DB_PASSWORD – database password

  • PHPGRID_DB_NAME – database name

  • PHPGRID_DB_TYPE – type of database

  • PHPGRID_DB_CHARSET – always ‘utf8’ in MySQL

Page Template

Our page will be comprised of a header, menu, body, and footer. Instead of creating the same page elements repeatedly, we will start by creating a reusable page template.

head.php

This is a basic HTML5 template header; it includes a link to a custom stylesheet that will be created in a later step.

1
2
3
4
5
6
7
8
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<title>My Custom Project Management Application</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<link type="text/css" rel="Stylesheet" href="../css/style.css" />
</head>
<body>
1
2
3
4
5
6
<div id="menu">
<ul>
<li><a href="clients.php" <?php if($_GET['currentPage'] == 'clients') echo 'class="active"'; ?>>Clients</a></li>
<li><a href="projects.php" <?php if($_GET['currentPage'] == 'projects') echo 'class="active"'; ?>>Projects</a></li>
</ul>
</div>

menu bar

Notice the usage of $_GET[‘currentPage’]. Each page will set a value that will highlight the name of the current page on the top menu bar.

Include the following code in style.css for menu styling; it will transform the above unordered list into a menu.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#menu ul {
    list-style-type: none;
    margin: 0;
    padding: 0;
    overflow: hidden;
    background-color: #1590C2;
}

#menu ul li {
    float: left;
}

#menu ul li a {
    display: block;
    color: white;
    text-align: center;
    padding: 14px 16px;
    text-decoration: none;
}

#menu ul li a:hover,
#menu .active {
    background-color: #ddd;
    color:black;
}

footer.php

Simply includes the closing tags for the elements we opened in the header:

1
2
</body>
</html>

Our Complete Reusable Page Template

The main content will go after the section title.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
include_once("../phpGrid/conf.php");
include_once('../inc/head.php');
?>

<h1>My Custom Project Management Application</h1>

<?php include_once('../inc/menu.php'); ?>

<h3>Section Title</h3>

<?php
// Your main content (datagrid eg.), such as a datagrid, goes here
?>

<?php include_once('../inc/footer.php'); ?>

Project Management Main Pages

Our project management application for managers has four pages:

  • Clients

  • Client Details

  • Projects

  • Project Details

The Clients page displays a list of clients with links to individual client details (Client Details page).

The Projects page displays a list of projects being managed with links to project details (Project Details page).

Design Mockup

Here is our project management application design mockup for project managers who manage one or more projects and assign tasks to employees.

design mockup

Clients

When a manager logs in to the project management system, the first page he sees is the Clients page which contains a complete list of companies.

The following code will give us a list of clients.

1
2
3
4
$dgClient = new C_DataGrid("SELECT * FROM clients", "id", "clients");
$dgClient->set_col_dynalink("Name", "client-details.php", "id");
$dgClient->enable_edit();
$dgClient->display();
  • The first line creates a phpGrid object by passing the SELECT SQL statement with its primary key id followed by the name of the database table – clients.

  • The second line creates a dynamic URL from the primary key “id”; it uses a function called set_col_dynalink(). This function sets a specific column-to-display HTML hyperlink URL based on dynamic values. If the primary key “id” has the value 100, it will display a URL like this client-details.php?id=100 which drills down the client detail page.

  • The third line, enable_edit(), makes the DataGrid editable and all CRUD (Create, Read, Update, Delete) operations are now accessible.

  • The last line calls the display() function to render the DataGrid on the screen.

You can check out more demos below:

Client Details

From the Clients page, the client name has a hyperlink that redirects to the Client Details page for that client when clicked.

client details

From the Client Details page, we need to obtain the Client ID that is passed as the URL parameter.

1
$clientId = (isset($_GET['id'])) ? intval($_GET['id']) : 0;

In our application, the Client ID should always be an integer. Thus, we use the PHP intval() function to ensure the Client ID is returned as an integer.

The following code displays projects associated with the current $clientId using the filter function set_query_filter(). In addition, we make the DataGrid editable with the enable_edit() function; phpGrid will take care of any CRUD operations for us.

Client Details > Projects

As you may notice, we again use the same function, set_col_dynalink(), to create hyperlinks to the Project Details table using the Project ID. We will get into the project-details.php page next.

1
2
3
4
5
6
7
8
9
<h4>Projects</h4>

<?php
$dgProj = new C_DataGrid("SELECT * FROM projects", "id", "projects");
$dgProj->set_query_filter(" ClientID = $clientId ");
$dgProj->set_col_dynalink("ProjectName", "project-details.php", "id");
$dgProj->enable_edit();
$dgProj->display();
?>
Client Details > Contacts

Under the Projects DataGrid, a list of contacts associated with the $clientid is displayed using the same functions set_query_filter() and enable_edit().

Contacts

1
2
3
4
5
6
<?php
$dgCont = new C_DataGrid("SELECT * FROM contacts", "id", "contacts");
$dgCont->set_query_filter(" ClientID = $clientId ");
$dgCont->enable_edit();
$dgCont->display();
?>

Projects

Now, let’s build the Projects page.

The Projects page displays a list of managed projects. It is very similar to the Clients page except that the database table is “Projects,” and the hyperlinks have the URL project-details.php instead of client-details.php.

1
2
3
4
5
6
<?php
$dgProj = new C_DataGrid("SELECT * Budget FROM projects", "id", "projects");
$dgProj->set_col_dynalink("ProjectName", "project-details.php", "id");
$dgProj->enable_edit();
$dgProj->display();
?>

Project Details

From the Projects page, each project name has a hyperlink which redirects to each individual Project Details page when clicked.

And from the Project Details page, we retrieve the Project ID for the URL parameter.

1
$projectId = (isset($_GET["id"])) ? intval($_GET["id"]) : 0;

Look familiar? It should because both Projects and Project Details pretty much follow the same coding pattern used in the Clients and Client Details pages; there are not really that many surprises.

The Project Details page is made of the following grids, all filtered by the $projectId obtained from the URL parameter.

  • Milestones

  • Tasks

  • Hours

  • Costs

Milestones

A milestone marks a major event in a project timeline. Here, we can easily display all the milestones of a project by filtering the value $projectId. Project managers have the necessary access rights to modify the milestones.

1
2
3
4
5
6
7
8
<h4>Milestones</h4>

<?php
$dgMS = new C_DataGrid("SELECT * FROM milestones", "id", "milestones");
$dgMS->set_query_filter(" ProjectID = $projectId ");
$dgMS->enable_edit();
$dgMS->display();
?>

Likewise, we can easily filter and display a list of tasks for the current project.

1
2
3
4
5
6
7
8
<h4>Tasks</h4>

<?php
$dgTask = new C_DataGrid("SELECT * FROM tasks", "id", "tasks");
$dgTask->set_query_filter(" ProjectID = $projectId ");
$dgTask->enable_edit();
$dgTask->display();
?>

I think you are probably getting the hang of it now. Here’s the code for the two remaining DataGrids.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<h4>Hours</h4>

<?php
$dgHour = new C_DataGrid("SELECT * FROM hours", "id", "hours");
$dgHour->set_query_filter(" ProjectID = $projectId ");
$dgHour->enable_edit();
$dgHour->display();
?>

<h4>Costs</h4>

<?php
$dgCost = new C_DataGrid("SELECT * FROM costs", "id", "costs");
$dgCost->set_query_filter(" ProjectID = $projectId ");
$dgCost->enable_edit();
$dgCost->display();
?>

Employees Page

We can now move on to the final part of the tutorial, the Employees page. Employees can login to view active project tasks assigned to them, track task hours, and costs. Their responsibility is simple: to monitor the tasks and log hours worked on any specific project task.

Design Mockup

employees screen

The Employees page has only one menu item: Tasks.

employees menu

My Active Tasks

The first part of the page shows a list of active tasks assigned to the current employee. Each task will have the hours reported by the current employee; this is a perfect situation in which to use a phpGrid subgrid.

We also need to use set_query_filter() to display only active tasks that have Status value “2”, and only for the current employee.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
$empId = intval(2); // hard code for the demo. In real world, the id should be retrieved in PHP SESSION

// active (StatusID = 2) tasks datagrid for the current employee
$dgTask = new C_DataGrid("SELECT * FROM tasks", "id", "tasks");
$dgTask->set_query_filter(" EmployeeID = $empId AND StatusID = 2 "); // Active value is 2

// hours reported for the current employee
$dgHour = new C_DataGrid("SELECT * FROM hours", "id", "hours");
$dgHour->set_query_filter(" EmployeeID = $empId ");
$dgHour->enable_edit();

// hours datagrid is a subgrid of the tasks datagrid
$dgTask->set_subgrid($dgHour, "TaskID", "id");
$dgTask->display();
?>

For demo purposes, we hard-coded the Employee ID to 2. In a real-world application, the Employee ID value should be stored and retrieved using PHP SESSION.

We then create the active Tasks DataGrid for the current employee. Notice the filter function.

Once we’ve defined the grid for the active tasks, we create a DataGrid to log the hours reported by the current employee.

Lastly, set_subgrid() causes the Hours DataGrid to become a subgrid of the Tasks DataGrid. The linking field in the Hours subgrid is ‘TaskID’, which is the second parameter, and in the main grid Tasks, it is “id,” the third parameter.

1
$dgTask->set_subgrid($dgHour, "TaskID", "id");

active tasks

My Hours History

Lastly, we would like to display a read-only DataGrid using data from the Hours table for the current employee for reviewing purposes.

1
2
3
4
5
6
<?php
$dgHourHist = new C_DataGrid("SELECT * FROM hours", "id", "hours");
$dgHourHist->set_jq_gridName("myHoursHistory"); // the grid name must be unique
$dgHourHist->set_query_filter(" EmployeeID = $empId ");
$dgHourHist->display();
?>

Notice we used a function called set_jq_gridName(). You can find more documentation here. This function sets a unique object name for the DataGrid. By default, phpGrid uses the database table name as its internal object name. Since we already created a DataGrid from the Hours table in the last part, we must set a unique name for our second Hours DataGrid.

Screenshots

Managers

managers screenshot 1

managers screenshot 2

Employee

employees screen

Run Demo Get this App *

* Included in Premium and Ultimate editions.