Skip to main content

Course Progress

Loading...

Final Project: Task Management System

Duration: 4-6 hours
Module 3: Final Project

Project Objectives

  • Build a complete PHP application with MySQL backend
  • Implement full CRUD (Create, Read, Update, Delete) functionality
  • Apply database normalization principles
  • Use prepared statements for security
  • Handle errors gracefully
  • Create a user-friendly interface
  • Implement user authentication and authorization

Project Overview

You will build a complete Task Management System that allows users to register, log in, and manage their personal tasks. This project combines everything you've learned about PHP and MySQL into a practical, real-world application.

💡
Project Scope
This project simulates a real-world development scenario. You'll create a system similar to popular task management tools like Todoist or Trello, but simplified for learning purposes.

System Requirements

Functional Requirements

graph TD A[Task Management System] --> B[User Management] A --> C[Task Operations] A --> D[Category Management] A --> E[Dashboard] B --> B1[Registration] B --> B2[Login/Logout] B --> B3[Profile Management] B --> B4[Password Reset] C --> C1[Create Tasks] C --> C2[View Tasks] C --> C3[Update Tasks] C --> C4[Delete Tasks] C --> C5[Mark Complete] D --> D1[Create Categories] D --> D2[Assign Categories] D --> D3[Filter by Category] E --> E1[Statistics] E --> E2[Recent Tasks] E --> E3[Upcoming Deadlines] style A fill:#0073aa,color:#fff style B fill:#21759b,color:#fff style C fill:#00a0d2,color:#fff

Technical Requirements

  • PHP Version:7.4 or higher
  • MySQL Version:5.7 or higher
  • Architecture:MVC pattern (simplified)
  • Security:Prepared statements, password hashing, CSRF protection
  • Frontend:HTML5, CSS3, JavaScript (optional)
  • Responsive:Mobile-friendly design

Database Design

Entity Relationship Diagram

erDiagram USERS ||--o{ TASKS : creates USERS ||--o{ CATEGORIES : creates CATEGORIES ||--o{ TASKS : contains USERS { int user_id PK string username UK string email UK string password_hash datetime created_at datetime last_login boolean is_active } CATEGORIES { int category_id PK int user_id FK string name string color string icon datetime created_at } TASKS { int task_id PK int user_id FK int category_id FK string title text description date due_date enum priority enum status datetime created_at datetime updated_at datetime completed_at }

Database Schema

-- Create database
CREATE DATABASE IF NOT EXISTS task_manager
CHARACTER SET utf8mb4
COLLATE utf8mb4_general_ci;

USE task_manager;

-- Users table
CREATE TABLE users (
    user_id INT AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(50) UNIQUE NOT NULL,
    email VARCHAR(100) UNIQUE NOT NULL,
    password_hash VARCHAR(255) NOT NULL,
    first_name VARCHAR(50),
    last_name VARCHAR(50),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    last_login TIMESTAMP NULL,
    is_active BOOLEAN DEFAULT TRUE,
    reset_token VARCHAR(100) NULL,
    reset_expires TIMESTAMP NULL,
    INDEX idx_username (username),
    INDEX idx_email (email)
) ENGINE=InnoDB;

-- Categories table
CREATE TABLE categories (
    category_id INT AUTO_INCREMENT PRIMARY KEY,
    user_id INT NOT NULL,
    name VARCHAR(50) NOT NULL,
    color VARCHAR(7) DEFAULT '#0073aa',
    icon VARCHAR(50) DEFAULT 'folder',
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE,
    UNIQUE KEY unique_user_category (user_id, name),
    INDEX idx_user_categories (user_id)
) ENGINE=InnoDB;

-- Tasks table
CREATE TABLE tasks (
    task_id INT AUTO_INCREMENT PRIMARY KEY,
    user_id INT NOT NULL,
    category_id INT,
    title VARCHAR(255) NOT NULL,
    description TEXT,
    due_date DATE,
    priority ENUM('low', 'medium', 'high', 'urgent') DEFAULT 'medium',
    status ENUM('pending', 'in_progress', 'completed', 'cancelled') DEFAULT 'pending',
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    completed_at TIMESTAMP NULL,
    FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE,
    FOREIGN KEY (category_id) REFERENCES categories(category_id) ON DELETE SET NULL,
    INDEX idx_user_tasks (user_id),
    INDEX idx_status (status),
    INDEX idx_due_date (due_date),
    INDEX idx_priority (priority)
) ENGINE=InnoDB;

-- Session table for remember me functionality
CREATE TABLE user_sessions (
    session_id INT AUTO_INCREMENT PRIMARY KEY,
    user_id INT NOT NULL,
    session_token VARCHAR(255) NOT NULL UNIQUE,
    ip_address VARCHAR(45),
    user_agent VARCHAR(255),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    expires_at TIMESTAMP NOT NULL,
    FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE,
    INDEX idx_session_token (session_token),
    INDEX idx_expires (expires_at)
) ENGINE=InnoDB;

-- Activity log table
CREATE TABLE activity_logs (
    log_id INT AUTO_INCREMENT PRIMARY KEY,
    user_id INT NOT NULL,
    action VARCHAR(50) NOT NULL,
    entity_type VARCHAR(50),
    entity_id INT,
    details JSON,
    ip_address VARCHAR(45),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE,
    INDEX idx_user_activity (user_id),
    INDEX idx_action (action),
    INDEX idx_created (created_at)
) ENGINE=InnoDB;

Project Structure

Directory Structure

task-manager/
├── index.php
├── config/
│   ├── database.php
│   └── config.php
├── includes/
│   ├── functions.php
│   ├── validation.php
│   └── session.php
├── classes/
│   ├── Database.php
│   ├── User.php
│   ├── Task.php
│   └── Category.php
├── public/
│   ├── css/
│   │   └── style.css
│   ├── js/
│   │   └── app.js
│   └── images/
├── templates/
│   ├── header.php
│   ├── footer.php
│   └── nav.php
├── auth/
│   ├── login.php
│   ├── register.php
│   ├── logout.php
│   └── reset-password.php
├── dashboard/
│   └── index.php
├── tasks/
│   ├── index.php
│   ├── create.php
│   ├── edit.php
│   ├── delete.php
│   └── view.php
├── categories/
│   ├── index.php
│   ├── create.php
│   └── delete.php
├── profile/
│   ├── index.php
│   └── edit.php
└── .htaccess

Key Components Visualization

Core Implementation

1. Database Connection Class

<?php
// classes/Database.php
class Database {
    private $host = 'localhost';
    private $db_name = 'task_manager';
    private $username = 'root';
    private $password = '';
    private $conn;
    
    public function connect() {
        $this->conn = null;
        
        try {
            $this->conn = new PDO(
                "mysql:host=" . $this->host . ";dbname=" . $this->db_name,
                $this->username,
                $this->password
            );
            $this->conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
            $this->conn->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
            $this->conn->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
        } catch(PDOException $e) {
            error_log("Connection Error: " . $e->getMessage());
            die("Connection failed. Please try again later.");
        }
        
        return $this->conn;
    }
}
?>

2. User Authentication Class

<?php
// classes/User.php
class User {
    private $conn;
    private $table = 'users';
    
    public $user_id;
    public $username;
    public $email;
    public $password;
    public $first_name;
    public $last_name;
    
    public function __construct($db) {
        $this->conn = $db;
    }
    
    public function register() {
        $query = "INSERT INTO " . $this->table . " 
                  (username, email, password_hash, first_name, last_name) 
                  VALUES (:username, :email, :password, :first_name, :last_name)";
        
        $stmt = $this->conn->prepare($query);
        
        // Hash password
        $password_hash = password_hash($this->password, PASSWORD_DEFAULT);
        
        // Bind values
        $stmt->bindParam(':username', $this->username);
        $stmt->bindParam(':email', $this->email);
        $stmt->bindParam(':password', $password_hash);
        $stmt->bindParam(':first_name', $this->first_name);
        $stmt->bindParam(':last_name', $this->last_name);
        
        if($stmt->execute()) {
            $this->user_id = $this->conn->lastInsertId();
            return true;
        }
        
        return false;
    }
    
    public function login() {
        $query = "SELECT user_id, username, email, password_hash 
                  FROM " . $this->table . " 
                  WHERE (username = :login OR email = :login) 
                  AND is_active = 1";
        
        $stmt = $this->conn->prepare($query);
        $stmt->bindParam(':login', $this->username);
        $stmt->execute();
        
        if($row = $stmt->fetch()) {
            if(password_verify($this->password, $row['password_hash'])) {
                $this->user_id = $row['user_id'];
                $this->email = $row['email'];
                
                // Update last login
                $this->updateLastLogin();
                
                return true;
            }
        }
        
        return false;
    }
    
    private function updateLastLogin() {
        $query = "UPDATE " . $this->table . " 
                  SET last_login = NOW() 
                  WHERE user_id = :user_id";
        
        $stmt = $this->conn->prepare($query);
        $stmt->bindParam(':user_id', $this->user_id);
        $stmt->execute();
    }
}
?>

3. Task Management Class

<?php
// classes/Task.php
class Task {
    private $conn;
    private $table = 'tasks';
    
    public $task_id;
    public $user_id;
    public $category_id;
    public $title;
    public $description;
    public $due_date;
    public $priority;
    public $status;
    
    public function __construct($db) {
        $this->conn = $db;
    }
    
    public function create() {
        $query = "INSERT INTO " . $this->table . " 
                  (user_id, category_id, title, description, due_date, priority) 
                  VALUES (:user_id, :category_id, :title, :description, :due_date, :priority)";
        
        $stmt = $this->conn->prepare($query);
        
        $stmt->bindParam(':user_id', $this->user_id);
        $stmt->bindParam(':category_id', $this->category_id);
        $stmt->bindParam(':title', $this->title);
        $stmt->bindParam(':description', $this->description);
        $stmt->bindParam(':due_date', $this->due_date);
        $stmt->bindParam(':priority', $this->priority);
        
        if($stmt->execute()) {
            $this->task_id = $this->conn->lastInsertId();
            return true;
        }
        
        return false;
    }
    
    public function getUserTasks($user_id, $filters = []) {
        $query = "SELECT t.*, c.name as category_name, c.color as category_color 
                  FROM " . $this->table . " t
                  LEFT JOIN categories c ON t.category_id = c.category_id 
                  WHERE t.user_id = :user_id";
        
        // Apply filters
        if(!empty($filters['status'])) {
            $query .= " AND t.status = :status";
        }
        if(!empty($filters['category'])) {
            $query .= " AND t.category_id = :category";
        }
        if(!empty($filters['priority'])) {
            $query .= " AND t.priority = :priority";
        }
        
        $query .= " ORDER BY 
                    CASE WHEN t.status = 'completed' THEN 1 ELSE 0 END,
                    t.due_date ASC,
                    t.priority DESC";
        
        $stmt = $this->conn->prepare($query);
        $stmt->bindParam(':user_id', $user_id);
        
        if(!empty($filters['status'])) {
            $stmt->bindParam(':status', $filters['status']);
        }
        if(!empty($filters['category'])) {
            $stmt->bindParam(':category', $filters['category']);
        }
        if(!empty($filters['priority'])) {
            $stmt->bindParam(':priority', $filters['priority']);
        }
        
        $stmt->execute();
        return $stmt->fetchAll();
    }
    
    public function update() {
        $query = "UPDATE " . $this->table . " 
                  SET title = :title,
                      description = :description,
                      category_id = :category_id,
                      due_date = :due_date,
                      priority = :priority,
                      status = :status
                  WHERE task_id = :task_id AND user_id = :user_id";
        
        $stmt = $this->conn->prepare($query);
        
        $stmt->bindParam(':title', $this->title);
        $stmt->bindParam(':description', $this->description);
        $stmt->bindParam(':category_id', $this->category_id);
        $stmt->bindParam(':due_date', $this->due_date);
        $stmt->bindParam(':priority', $this->priority);
        $stmt->bindParam(':status', $this->status);
        $stmt->bindParam(':task_id', $this->task_id);
        $stmt->bindParam(':user_id', $this->user_id);
        
        return $stmt->execute();
    }
    
    public function delete() {
        $query = "DELETE FROM " . $this->table . " 
                  WHERE task_id = :task_id AND user_id = :user_id";
        
        $stmt = $this->conn->prepare($query);
        $stmt->bindParam(':task_id', $this->task_id);
        $stmt->bindParam(':user_id', $this->user_id);
        
        return $stmt->execute();
    }
    
    public function markComplete() {
        $query = "UPDATE " . $this->table . " 
                  SET status = 'completed',
                      completed_at = NOW()
                  WHERE task_id = :task_id AND user_id = :user_id";
        
        $stmt = $this->conn->prepare($query);
        $stmt->bindParam(':task_id', $this->task_id);
        $stmt->bindParam(':user_id', $this->user_id);
        
        return $stmt->execute();
    }
}
?>

User Interface

Dashboard Statistics

Sample Dashboard HTML

<!-- dashboard/index.php -->
<?php
session_start();
require_once '../config/database.php';
require_once '../classes/Database.php';
require_once '../classes/Task.php';

// Check if user is logged in
if(!isset($_SESSION['user_id'])) {
    header('Location: ../auth/login.php');
    exit();
}

$database = new Database();
$db = $database->connect();
$task = new Task($db);

// Get user tasks
$tasks = $task->getUserTasks($_SESSION['user_id']);

// Calculate statistics
$total_tasks = count($tasks);
$completed = count(array_filter($tasks, function($t) { return $t['status'] == 'completed'; }));
$in_progress = count(array_filter($tasks, function($t) { return $t['status'] == 'in_progress'; }));
$overdue = count(array_filter($tasks, function($t) { 
    return $t['status'] != 'completed' && $t['due_date'] < date('Y-m-d'); 
}));
?>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Dashboard - Task Manager</title>
    <link rel="stylesheet" href="../public/css/style.css">
</head>
<body>
    <?php include '../templates/nav.php'; ?>
    
    <div class="container">
        <h1>Welcome, <?php echo htmlspecialchars($_SESSION['username']); ?>!</h1>
        
        <div class="stats-grid">
            <div class="stat-card">
                <div class="stat-value"><?php echo $total_tasks; ?></div>
                <div class="stat-label">Total Tasks</div>
            </div>
            
            <div class="stat-card success">
                <div class="stat-value"><?php echo $completed; ?></div>
                <div class="stat-label">Completed</div>
            </div>
            
            <div class="stat-card warning">
                <div class="stat-value"><?php echo $in_progress; ?></div>
                <div class="stat-label">In Progress</div>
            </div>
            
            <div class="stat-card danger">
                <div class="stat-value"><?php echo $overdue; ?></div>
                <div class="stat-label">Overdue</div>
            </div>
        </div>
        
        <div class="dashboard-grid">
            <div class="recent-tasks">
                <h2>Recent Tasks</h2>
                <ul class="task-list">
                    <?php 
                    $recent = array_slice($tasks, 0, 5);
                    foreach($recent as $task): 
                    ?>
                    <li class="task-item">
                        <span class="task-title">
                            <?php echo htmlspecialchars($task['title']); ?>
                        </span>
                        <span class="task-priority priority-<?php echo $task['priority']; ?>">
                            <?php echo ucfirst($task['priority']); ?>
                        </span>
                    </li>
                    <?php endforeach; ?>
                </ul>
            </div>
            
            <div class="upcoming-deadlines">
                <h2>Upcoming Deadlines</h2>
                <ul class="deadline-list">
                    <?php 
                    $upcoming = array_filter($tasks, function($t) {
                        return $t['status'] != 'completed' && $t['due_date'] != null;
                    });
                    usort($upcoming, function($a, $b) {
                        return $a['due_date'] <=> $b['due_date'];
                    });
                    $upcoming = array_slice($upcoming, 0, 5);
                    
                    foreach($upcoming as $task): 
                    ?>
                    <li class="deadline-item">
                        <span class="task-title">
                            <?php echo htmlspecialchars($task['title']); ?>
                        </span>
                        <span class="task-date">
                            <?php echo date('M d, Y', strtotime($task['due_date'])); ?>
                        </span>
                    </li>
                    <?php endforeach; ?>
                </ul>
            </div>
        </div>
        
        <div class="quick-actions">
            <a href="../tasks/create.php" class="btn btn-primary">+ New Task</a>
            <a href="../tasks/index.php" class="btn btn-secondary">View All Tasks</a>
            <a href="../categories/index.php" class="btn btn-secondary">Manage Categories</a>
        </div>
    </div>
    
    <?php include '../templates/footer.php'; ?>
</body>
</html>

Security Implementation

Security Measures

Required Security Features

  • Password Hashing:Use password_hash() with PASSWORD_DEFAULT
  • SQL Injection Prevention:Always use prepared statements
  • XSS Protection:htmlspecialchars() for all output
  • CSRF Protection:Token validation for forms
  • Session Security:Regenerate session ID on login
  • Input Validation:Server-side validation for all inputs
  • Error Handling:Log errors, don't display to users

CSRF Token Implementation

<?php
// includes/csrf.php
function generateCSRFToken() {
    if (empty($_SESSION['csrf_token'])) {
        $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
    }
    return $_SESSION['csrf_token'];
}

function verifyCSRFToken($token) {
    if (!isset($_SESSION['csrf_token']) || $token !== $_SESSION['csrf_token']) {
        die('CSRF token validation failed');
    }
    return true;
}

// In forms
?>
<form method="POST" action="process.php">
    <input type="hidden" name="csrf_token" value="<?php echo generateCSRFToken(); ?>">
    <!-- other form fields -->
</form>

<?php
// In form processing
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    verifyCSRFToken($_POST['csrf_token']);
    // Process form
}
?>

Testing Requirements

Test Cases

Feature Test Case Expected Result
Registration Register with valid data Account created, auto-login
Registration Register with existing username Error message displayed
Login Valid credentials Redirect to dashboard
Login Invalid credentials Error message, stay on login
Task Creation Create task with all fields Task saved, appears in list
Task Update Edit existing task Changes saved successfully
Task Delete Delete task Task removed from database
Categories Create category Category available in dropdown
Filtering Filter by status Only matching tasks shown
Security Access without login Redirect to login page

Project Deliverables

Complete the Task Management System with the following requirements:

Minimum Requirements (70% grade)

  • ☐ Database setup with all required tables
  • ☐ User registration and login functionality
  • ☐ CRUD operations for tasks
  • ☐ Basic category management
  • ☐ Session management
  • ☐ Input validation
  • ☐ Prepared statements for all queries
  • ☐ Basic CSS styling

Standard Requirements (85% grade)

  • ☐ All minimum requirements
  • ☐ Dashboard with statistics
  • ☐ Task filtering and sorting
  • ☐ Priority levels
  • ☐ Due date tracking
  • ☐ Password reset functionality
  • ☐ CSRF protection
  • ☐ Responsive design

Advanced Requirements (100% grade)

  • ☐ All standard requirements
  • ☐ Activity logging
  • ☐ Remember me functionality
  • ☐ Task search
  • ☐ Export tasks to CSV/PDF
  • ☐ Email notifications for deadlines
  • ☐ Ajax for dynamic updates
  • ☐ Dark mode toggle
  • ☐ Unit tests for critical functions

Submission Requirements

  1. Source Code:Complete project folder in ZIP format
  2. Database:SQL export file with sample data
  3. Documentation:
    • README.md with installation instructions
    • Database schema diagram
    • User manual (PDF)
  4. Demo Video:5-minute screen recording showing all features
  5. Test Report:Document showing test cases and results

Sample Project Structure

Here's how a professional would organize this project:

# Project Organization

/task-manager
│
├── /src                 # Source code
│   ├── /classes        # PHP classes
│   ├── /controllers    # Request handlers
│   └── /models         # Data models
│
├── /public             # Publicly accessible files
│   ├── index.php      # Entry point
│   ├── /css           # Stylesheets
│   ├── /js            # JavaScript
│   └── /images        # Images
│
├── /templates          # View templates
│   ├── /layouts       # Page layouts
│   ├── /partials      # Reusable components
│   └── /pages         # Page templates
│
├── /config            # Configuration files
├── /database          # Database migrations and seeds
├── /tests             # Unit and integration tests
├── /docs              # Documentation
│
├── .gitignore         # Git ignore file
├── composer.json      # PHP dependencies
├── README.md          # Project documentation
└── LICENSE            # License file

Project Resources

🎯
Project Tips
  • Start with the database design - it's the foundation
  • Build and test one feature at a time
  • Use Git for version control from the beginning
  • Test with different user accounts
  • Focus on security from the start, not as an afterthought
  • Comment your code for future reference