PHP
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Functions
-
Section 4: Object-Oriented PHP
-
Section 5: Working with Data
-
Section 6: Modern PHP (PHP 8)
-
Section 7: Working with Files and Networking
-
Section 8: Common PHP Frameworks Overview
-
Section 9: Tooling and Ecosystem
-
Section 10: Practical Projects
-
Section 11: Interview Practice
-
Section 12: More Standard Library
-
Section 13: Data Structures and Algorithms in PHP
-
Section 14: More Practice Exercises
-
Section 15: More Security and Best Practices
-
Section 16: More Testing and Tooling
-
Section 17: WordPress-Style CMS Concepts
-
Section 18: Advanced OOP Practice
-
Section 19: More Web Fundamentals
-
Section 20: Database Practice
-
Section 21: PHP Manual: Array Functions
-
Section 22: PHP Manual: Date and Calendar Functions
-
Section 23: PHP Manual: Filesystem and Directory Functions
-
Section 24: PHP Manual: Filter and Var Handling
-
Section 25: PHP Manual: Math Functions
-
Section 26: PHP Manual: JSON and XML
-
Section 27: PHP Manual: Network and Stream Functions
-
Section 28: PHP Manual: Error and Exception Handling
-
Section 29: PHP Manual: Output Control and Misc
-
Section 30: PHP Manual: FTP, Zip, and Mail
-
Section 31: Modern PHP Frameworks Deep Dive
-
Section 32: PHP Design Patterns
-
Section 33: More Practice Exercises
-
Section 34: PHP Performance and Deployment
-
Section 35: More Interview Practice
-
Section 36: More PHP Standard Library
-
Section 37: PHP Concurrency and Async
-
Section 38: More Web Development Practice
-
Section 39: PHP Testing Deep Dive
-
Section 40: Composer and Package Development
-
Section 41: PHP Security Deep Dive
-
Section 42: More Practical Projects
-
Section 43: Legacy PHP Maintenance
-
Section 44: More Algorithm Practice
-
Section 45: Final Practice and Review
-
Section 46: PHP for E-Commerce Patterns
-
Section 47: PHP API Design Deep Dive
-
Section 48: PHP Caching Strategies
-
Section 49: PHP Queue and Background Jobs
-
Section 50: PHP Multi-Tenancy Patterns
-
Section 51: PHP Real-Time Features
-
Section 52: PHP CMS and Content Modeling
-
Section 53: PHP Internationalization
-
Section 54: More Framework-Specific Practice
-
Section 55: PHP Legacy Code Refactoring
-
Section 56: More Practice Projects Round 2
-
Section 57: PHP Command-Line Applications
-
Section 58: PHP and Microservices
-
Section 59: More Interview and Review Round 2
237: Building a Simple Analytics Dashboard Backend
When people talk about "analytics," they usually imagine complex machine learning models or massive data warehouses. But at its core, a backend for an analytics dashboard is just about two things: efficiently recording events and aggregating those events into something a human can actually understand.
For this lesson, we're going to build a simple page-view tracker. I want a system that records which page a user visited and then allows us to query the "Top 5 Most Visited Pages" for the current day. It's a classic pattern you'll see in almost every internal admin tool.
Capturing the Page Hits
First, we need a way to log the event. I'm going to keep the database schema lean: just a page_views table with a page_path (string) and a viewed_at (timestamp). We don't need a primary key for the logic, but we'll keep one for database health.
// logger.php
$pdo = new PDO('mysql:host=localhost;dbname=analytics_db', 'user', 'pass');
$page = $_SERVER['REQUEST_URI'];
$stmt = $pdo->prepare("INSERT INTO page_views (page_path, viewed_at) VALUES (?, NOW())");
$stmt->execute([$page]);
I usually put this snippet in a global header or a middleware layer. It's lightweight, but remember: in a high-traffic environment, hitting the database on every single page load is a recipe for a crash. In a real-world production app, I'd probably push these events to a queue like Redis first, but for our dashboard, direct inserts are fine.
Aggregating the Traffic
Now for the actual dashboard logic. We don't want to see every single row; we want the totals. This is where GROUP BY becomes your best friend in SQL.
// dashboard_data.php
$pdo = new PDO('mysql:host=localhost;dbname=analytics_db', 'user', 'pass');
$query = "SELECT page_path, COUNT(*) as views
FROM page_views
GROUP BY page_path
ORDER BY views DESC
LIMIT 5";
$stmt = $pdo->query($query);
$topPages = $stmt->fetchAll(PDO::FETCH_ASSOC);
I ran this locally and it worked perfectly. But then I noticed something. As I added more test data from yesterday and the day before, the "Top Pages" were reflecting the total history of the site. That's not a dashboard; that's an archive. A real dashboard needs a time window—usually "today" or "last 24 hours."
I messed up by forgetting the WHERE clause. Let's fix that so we're only looking at today's traffic:
// The corrected query
$query = "SELECT page_path, COUNT(*) as views
FROM page_views
WHERE viewed_at >= CURDATE()
GROUP BY page_path
ORDER BY views DESC
LIMIT 5";
Adding WHERE viewed_at >= CURDATE() ensures that the moment the clock strikes midnight, our dashboard resets. It keeps the data relevant and, more importantly, keeps the query fast as the table grows.
Packaging the Data for the Frontend
Since the frontend will likely be a separate JS component or a template, I'm not going to echo HTML directly here. Instead, I'll structure the response as a JSON object. This makes the backend reusable if you ever decide to switch from a PHP template to a Vue or React frontend.
header('Content-Type: application/json');
$stats = [
'generated_at' => date('Y-m-d H:i:s'),
'top_pages' => $topPages,
'total_views_today' => $pdo->query("SELECT COUNT(*) FROM page_views WHERE viewed_at >= CURDATE()")->fetchColumn()
];
echo json_encode($stats);
I added a total_views_today key because it's the first thing any stakeholder asks for when they open a dashboard. It's a simple scalar value that gives immediate context to the "Top Pages" list.
📋 Practical Task
Implementing a Page-Specific View Counter
Currently, our dashboard shows the top 5 pages across the whole site. Your task is to create a new PHP script called page_stats.php that accepts a path via a GET request (e.g., page_stats.php?path=/about) and returns the total number of views for only that specific page for all time.
Your script should:
- Validate that the
pathparameter exists in the$_GETarray. - Use a prepared statement to prevent SQL injection.
- Return a JSON response containing the path and the view count.
There are no comments for now.