Lesson 2
Applying Data Filtering and Aggregation in a User Management System Using PHP
Introduction

Welcome to today's lesson on applying data filtering and aggregation in a real-world scenario using a user management system. We'll start by building a foundational structure that can handle basic user operations. Then, we'll expand it by introducing more advanced functionalities that allow filtering and aggregating user data.

Starter Task Methods

In our starter task, we will implement a class that manages basic operations on a collection of user data, specifically handling adding new users, retrieving user profiles, and updating user profiles.

Here are the starter task methods rewritten for PHP:

  • addUser($user_id, $age, $country, $subscribed) - Adds a new user with the specified attributes and returns true if the user was added successfully, or false if a user with the same $user_id already exists.

  • getUser($user_id) - Returns the user's profile if the user exists; otherwise, returns null.

  • updateUser($user_id, $age = null, $country = null, $subscribed = null) - Updates the user's profile based on non-null parameters. Returns true if the user exists and was updated, false otherwise.

To store the user data, we will use associative arrays.

Starter Task Implementation

Here is the implementation of our starter task in PHP:

php
1<?php 2 3class UserManager { 4 private $users = []; 5 6 public function addUser($user_id, $age, $country, $subscribed) { 7 if (isset($this->users[$user_id])) { 8 return false; 9 } 10 $this->users[$user_id] = [ 11 'age' => $age, 12 'country' => $country, 13 'subscribed' => $subscribed 14 ]; 15 return true; 16 } 17 18 public function getUser($user_id) { 19 return $this->users[$user_id] ?? null; 20 } 21 22 public function updateUser($user_id, $age = null, $country = null, $subscribed = null) { 23 if (!isset($this->users[$user_id])) { 24 return false; 25 } 26 if ($age !== null) { 27 $this->users[$user_id]['age'] = $age; 28 } 29 if ($country !== null) { 30 $this->users[$user_id]['country'] = $country; 31 } 32 if ($subscribed !== null) { 33 $this->users[$user_id]['subscribed'] = $subscribed; 34 } 35 return true; 36 } 37} 38 39// Example usage 40$um = new UserManager(); 41echo $um->addUser("u1", 25, "USA", true) ? "true\n" : "false\n"; // true 42echo $um->addUser("u2", 30, "Canada", false) ? "true\n" : "false\n"; // true 43echo $um->addUser("u1", 22, "Mexico", true) ? "true\n" : "false\n"; // false 44 45$user = $um->getUser("u1"); 46if ($user !== null) { 47 echo $user['age'] . "\n"; // 25 48} 49 50echo $um->updateUser("u1", 26) ? "true\n" : "false\n"; // true 51echo $um->updateUser("u3", 19, "UK", false) ? "true\n" : "false\n"; // false 52?>

This implementation covers all our starter methods. Let's move forward and introduce more complex functionalities.

Introducing New Methods for Data Filtering and Aggregation

With our foundational structure in place, it's time to add functionalities for filtering user data and aggregating statistics.

Here are the new methods to implement:

  • filterUsers($min_age = null, $max_age = null, $country = null, $subscribed = null):
    • Returns the list of user IDs that match the specified criteria. Criteria can be null, meaning that the criterion should not be applied during filtering.
  • aggregateStats() - Returns statistics in the form of an associative array:
    • total_users: Total number of users.
    • average_age: Average age of all users (rounded down to the nearest integer).
    • subscribed_ratio: Ratio of subscribed users to total users (rounded to two decimal places).
Step 1: Adding `filterUsers` Method

This method filters users based on the criteria provided. Let's see how it works in PHP:

php
1<?php 2 3class UserManager { 4 // Existing methods... 5 6 public function filterUsers($min_age = null, $max_age = null, $country = null, $subscribed = null) { 7 $filtered_users = []; 8 foreach ($this->users as $user_id => $profile) { 9 if ($min_age !== null && $profile['age'] < $min_age) { 10 continue; 11 } 12 if ($max_age !== null && $profile['age'] > $max_age) { 13 continue; 14 } 15 if ($country !== null && $profile['country'] !== $country) { 16 continue; 17 } 18 if ($subscribed !== null && $profile['subscribed'] !== $subscribed) { 19 continue; 20 } 21 $filtered_users[] = $user_id; 22 } 23 return $filtered_users; 24 } 25 26} 27 28// Example usage of the new method 29$um = new UserManager(); 30$um->addUser("u1", 25, "USA", true); 31$um->addUser("u2", 30, "Canada", false); 32$um->addUser("u3", 22, "USA", true); 33 34$result1 = $um->filterUsers(20, 30, "USA", true); 35echo implode(" ", $result1) . "\n"; // u1 u3 36 37$result2 = $um->filterUsers(null, 28, null, null); 38echo implode(" ", $result2) . "\n"; // u1 u3 39 40$result3 = $um->filterUsers(null, null, "Canada", false); 41echo implode(" ", $result3) . "\n"; // u2 42 43?>

The filterUsers method filters users based on min_age, max_age, country, and subscribed status. It checks each user's profile against the provided criteria and adds matching users to the filtered_users array, which is then returned.

Step 2: Adding `aggregateStats` Method

This method aggregates statistics from the user profiles. Let's implement it in PHP:

php
1<?php 2 3class UserManager { 4 // Existing methods... 5 6 public function aggregateStats() { 7 $total_users = count($this->users); 8 if ($total_users === 0) { 9 return [ 10 "total_users" => 0, 11 "average_age" => 0, 12 "subscribed_ratio" => 0.00 13 ]; 14 } 15 16 $total_age = array_reduce($this->users, function ($sum, $profile) { 17 return $sum + $profile['age']; 18 }, 0); 19 20 $subscribed_users = count(array_filter($this->users, function ($profile) { 21 return $profile['subscribed']; 22 })); 23 24 $average_age = floor($total_age / $total_users); 25 $subscribed_ratio = round($subscribed_users / $total_users, 2); 26 27 return [ 28 "total_users" => $total_users, 29 "average_age" => $average_age, 30 "subscribed_ratio" => $subscribed_ratio 31 ]; 32 } 33} 34 35// Using `um` from the previous section 36$um = new UserManager(); 37$um->addUser("u1", 25, "USA", true); 38$um->addUser("u2", 30, "Canada", false); 39$um->addUser("u3", 22, "USA", true); 40 41$stats = $um->aggregateStats(); 42echo "Total users: " . $stats["total_users"] . "\n"; // 3 43echo "Average age: " . $stats["average_age"] . "\n"; // 25 44echo "Subscribed ratio: " . $stats["subscribed_ratio"] . "\n"; // 0.67 45 46?>

The aggregate_stats method calculates and returns aggregate statistics about the users, including total_users, average_age, and subscribed_ratio.

The Final Solution

Here's the complete UserManager class with all methods, including the new ones for filtering and aggregation:

php
1<?php 2 3class UserManager { 4 private $users = []; 5 6 public function addUser($user_id, $age, $country, $subscribed) { 7 if (isset($this->users[$user_id])) { 8 return false; 9 } 10 $this->users[$user_id] = [ 11 'age' => $age, 12 'country' => $country, 13 'subscribed' => $subscribed 14 ]; 15 return true; 16 } 17 18 public function getUser($user_id) { 19 return $this->users[$user_id] ?? null; 20 } 21 22 public function updateUser($user_id, $age = null, $country = null, $subscribed = null) { 23 if (!isset($this->users[$user_id])) { 24 return false; 25 } 26 if ($age !== null) { 27 $this->users[$user_id]['age'] = $age; 28 } 29 if ($country !== null) { 30 $this->users[$user_id]['country'] = $country; 31 } 32 if ($subscribed !== null) { 33 $this->users[$user_id]['subscribed'] = $subscribed; 34 } 35 return true; 36 } 37 38 public function filterUsers($min_age = null, $max_age = null, $country = null, $subscribed = null) { 39 $filtered_users = []; 40 foreach ($this->users as $user_id => $profile) { 41 if ($min_age !== null && $profile['age'] < $min_age) { 42 continue; 43 } 44 if ($max_age !== null && $profile['age'] > $max_age) { 45 continue; 46 } 47 if ($country !== null && $profile['country'] !== $country) { 48 continue; 49 } 50 if ($subscribed !== null && $profile['subscribed'] !== $subscribed) { 51 continue; 52 } 53 $filtered_users[] = $user_id; 54 } 55 return $filtered_users; 56 } 57 58 public function aggregateStats() { 59 $total_users = count($this->users); 60 if ($total_users === 0) { 61 return [ 62 "total_users" => 0, 63 "average_age" => 0, 64 "subscribed_ratio" => 0.00 65 ]; 66 } 67 68 $total_age = array_reduce($this->users, function ($sum, $profile) { 69 return $sum + $profile['age']; 70 }, 0); 71 72 $subscribed_users = count(array_filter($this->users, function ($profile) { 73 return $profile['subscribed']; 74 })); 75 76 $average_age = floor($total_age / $total_users); 77 $subscribed_ratio = round($subscribed_users / $total_users, 2); 78 79 return [ 80 "total_users" => $total_users, 81 "average_age" => $average_age, 82 "subscribed_ratio" => $subscribed_ratio 83 ]; 84 } 85} 86 87// Example usage 88$um = new UserManager(); 89$um->addUser("u1", 25, "USA", true); 90$um->addUser("u2", 30, "Canada", false); 91$um->addUser("u3", 22, "USA", true); 92 93$result1 = $um->filterUsers(20, 30, "USA", true); 94echo implode(" ", $result1) . "\n"; // u1 u3 95 96$result2 = $um->filterUsers(null, 28, null, null); 97echo implode(" ", $result2) . "\n"; // u1 u3 98 99$result3 = $um->filterUsers(null, null, "Canada", false); 100echo implode(" ", $result3) . "\n"; // u2 101 102$stats = $um->aggregateStats(); 103echo "Total users: " . $stats["total_users"] . "\n"; // 3 104echo "Average age: " . $stats["average_age"] . "\n"; // 25 105echo "Subscribed ratio: " . $stats["subscribed_ratio"] . "\n"; // 0.67 106 107?>
Lesson Summary

Great job! Today, you've learned how to effectively handle user data by implementing advanced functionalities like filtering and aggregation on top of a basic system. This is a critical skill in real-life software development, where you often need to extend existing systems to meet new requirements.

I encourage you to practice solving similar challenges to solidify your understanding of data filtering and aggregation. Happy coding, and see you in the next lesson!

Enjoy this lesson? Now it's time to practice with Cosmo!
Practice is how you turn knowledge into actual skills.