Step-by-Step Implementation Guide
Phase 1: Database Setup (Laravel)
Step 1: Create Laravel Project and Database Schema
composer create-project laravel/laravel analytics-system
cd analytics-system
Create migrations:
php artisan make:migration create_visitors_tablephp artisan make:migration create_page_visits_tablephp artisan make:migration create_sessions_tablephp artisan make:migration create_categories_table
Step 2: Create Models // database/migrations/xxxx_create_visitors_table.php// database/migrations/xxxx_create_sessions_table.php// database/migrations/xxxx_create_categories_table.php// database/migrations/xxxx_create_page_visits_table.php
Step 3: Create Analytics ServiceStep 4: Create Analytics ControllersStep 5: Create API RoutesPhase 2: React Frontend Setup
Step 6: Create React Tracking ScriptStep 7: Create React Dashboard ComponentsNow let me create a comprehensive guide document and additional components:Perfect! Now let me create one final artifact with additional React components for the complete system:Perfect! I've created a complete visitor analytics system for you. Here's what I've built:
Complete System Components
Backend (Laravel)
1. Database Migrations - Complete schema with visitors, sessions, page visits, and categories2. Models - Eloquent models with relationshipsphp artisan make:model Post
3. Analytics Service - Core tracking logic with geo-location
4. Controllers - API endpoints for tracking and dashboard data
5. API Routes - Protected and public endpoints
Frontend (React)
1. Analytics Tracker - JavaScript tracking script with automatic time-spent monitoring
2. Main Dashboard Beautiful animated dashboard with:
Animated counter widgets
Daily/monthly traffic charts
Country-wise visitor map
Most visited pages
Exit pages analysis
3. Additional Widgets:
Real-time visitor counter widget
New vs Returning visitors chart
Category analytics (3-level support)
Live activity feed
Step-by-Step Implementation Guide
Database Migrations
php artisan make:migration create_visitors_table
php artisan make:migration create_page_visits_table
php artisan make:migration create_sessions_table
php artisan make:migration create_categories_table
Migrations
xxxx_create_sessions_table.php
xxxx_create_visitors_table.php
xxxx_create_categories_table.php
xxxx_create_page_visits_table.php
Create Models
Visitor.php
Session.php
Category.php
PageVisit.php
Services
app/Services/AnalyticsService.php
Controllers
app/Http/Controllers/AnalyticsTrackingController.php
app/Http/Controllers/AnalyticsDashboardController.php
Create API Routes
routes/api.php
React Frontend Setup
src/utils/analyticsTracker.jsx
Components/Dashboard.jsx (Analytics Dashboard)
Backend
2 Database Migrations
2.1 Create ‘visitors’ table
The visitors table stores unique visitor profiles based on IP addresses, capturing location (country, city, coordinates), device details (browser, platform, mobile/desktop), and visit history (first visit, last visit, total visits). It enables demographic analysis, geo-location mapping, device usage reports, and distinguishes unique visitors from total visits—the foundation for all analytics insights.
'ip_address' // Unique identifier for each visitor, example: "192.168.1.1"
Why: This is how we recognize the same visitor across multiple visits
'country_code' // ISO code like "US", "UK", "BD"
Why: For country-wise statistics and maps
'country_name' // Full name like "United States", "Bangladesh"
Why: Display-friendly country names in reports
'city' // City name like "Dhaka", "New York"
Why: More granular location analytics
'region' // State/Province like "California", "Dhaka Division"
Why: Regional analysis between country and city level
'latitude' & 'longitude' // Geographic coordinates
Why: Plot visitors on interactive maps
'user_agent' // Browser string like "Mozilla/5.0..."
// Why: Detect browser type, OS, device details
'browser' // Parsed: "Chrome", "Firefox", "Safari"
Why: Know which browsers to optimize for
'platform' // "Windows", "iOS", "Android", "Linux"
Why: Understand device/OS distribution
'is_mobile' // true/false
Why: Mobile vs Desktop traffic analysis
'first_visit_at' // Timestamp of first visit
Why: Calculate visitor acquisition over time
'last_visit_at' // Timestamp of most recent visit
Why: See when visitors last came back
'visit_count' // Total number of visits
Why: Identify loyal/returning visitors
2.2 create “page_visits” visits
The page_visits table records every individual page view with precise details: which page, how long visitors stayed, where they came from, and what content category they viewed. It reveals your most popular pages, identifies where visitors abandon your site, tracks navigation patterns, and measures content engagement—essential for understanding what works and what doesn't.
$table->id();
$table->foreignId('visitor_id')->constrained()->onDelete('cascade');
What: Foreign key linking to visitors table
Why: Know which visitor viewed this page
->onDelete('cascade'): Delete page visits if visitor deleted
Example: visitor_id = 10 means visitor #10 viewed this page
$table->foreignId('session_id')->constrained()->onDelete('cascade');
What: Foreign key linking to sessions table
Why: Group page views into sessions
->onDelete('cascade'): Delete page visits if session deleted
Example: session_id = 25 means this view was in session #25
$table->foreignId('category_id')->nullable()->constrained()->onDelete('set null');
What: Foreign key linking to categories table
Why: Categorize pages for content analytics
->nullable(): Optional (not all pages categorized)
->onDelete('set null'): Keep page visit but remove category if deleted
Example: category_id = 3 means page is in category #3
$table->string('url', 500);
What: Creates VARCHAR(500) for full page URL
Why: Exact page identification
Required: Must have value
Example: "https://example.com/products/laptop?id=123"
$table->string('page_title')->nullable();
What: Creates TEXT column for HTML page title
Why: Display-friendly page names in reports
->nullable(): Optional (might not be captured)
Example: "Premium Laptops - Tech Store"
$table->string('referrer', 500)->nullable();
What: Creates VARCHAR(500) for referring URL
Why: Track traffic sources (Google, social media, direct)
->nullable(): NULL for direct visits (no referrer)
Example: "https://google.com/search?q=laptops"
$table->integer('time_spent_seconds')->default(0);
What: Creates INTEGER for time spent on page
Why: Content engagement measurement
->default(0): Updated as user stays on page
Example: 45 (quick scan), 180 (engaged), 600 (very engaged)
$table->boolean('is_exit_page')->default(false);
What: Creates TINYINT(1) marking if user left site from here
Why: Exit page analysis (where site loses visitors)
->default(false): Marked true when session ends
Values: false (continued browsing), true (left site)
$table->boolean('is_bounce')->default(false);
What: Creates TINYINT(1) marking single-page sessions
Why: Bounce rate calculation (left after 1 page)
->default(false): True if only page in session
Values: false (viewed multiple pages), true (only page viewed)
$table->timestamp('visited_at');
What: Creates TIMESTAMP for exact view time
Why: Time-based analytics and ordering
Required: Must have value
Example: "2025-01-27 14:25:33"
$table->timestamps();
$table->index('url');
What: Creates index on url column
Why: Fast queries for "all visits to /products"
Performance: Speeds up page-specific analytics
$table->index('visited_at');
What: Creates index on visited_at timestamp
Why: Fast time-range queries like "views today"
Performance: Essential for time-based filtering
$table->index('session_id');
What: Creates index on session_id foreign key
Why: Fast lookup of all pages in a session
Performance: Speeds up session analysis queries
$table->index('category_id');
What: Creates index on category_id foreign key
Why: Fast category-based analytics
Performance: Speeds up "views per category" queries
2.3 create “sessions” table
The sessions table groups page views into complete visits, measuring how long users stay and how many pages they explore. It distinguishes new versus returning visitors, identifies entry and exit points, calculates bounce rates, and tracks multiple visits per day transforming scattered page views into meaningful visitor journeys for actionable insights.
$table->id()
$table->foreignId('visitor_id')
session_id
$table->timestamp('started_at');
$table->timestamp('ended_at')->nullable();
What: Creates TIMESTAMP for session end time
Why: Calculate visit duration (ended_at - started_at)
->nullable(): NULL means session still active
Example: "2025-01-27 10:15:30" or NULL (ongoing)
$table->integer('duration_seconds')->default(0);
What: Creates INTEGER storing total session time in seconds
Why: Pre-calculated duration for fast queries (avoid calculating each time)
->default(0): Starts at 0, updated when session ends
Example: 900 (15 minutes), 3600 (1 hour)
$table->integer('page_views')->default(0);
What: Creates INTEGER counting pages viewed in session
Why: Measure engagement (more pages = more engaged)
->default(0): Increments with each page view
Example: 1 (bounce), 5 (engaged), 15 (highly engaged)
$table->string('entry_page')->nullable();
What: Creates TEXT column for first page URL
Why: Identify how visitors enter your site (landing pages)
->nullable(): Optional field
Example: "/blog/seo-tips", "/home", "/products/laptop"
$table->string('exit_page')->nullable();
What: Creates TEXT column for last page URL before leaving
Why: Identify where visitors abandon your site (exit analysis)
->nullable(): NULL if session still active
Example: "/cart" (abandoned), "/checkout" (left), "/contact" (success)
$table->boolean('is_returning')->default(false);
What: Creates TINYINT(1) for new vs returning flag
Why: Distinguish first-time vs repeat visitors
->default(false): New visitor by default
Values: false (new), true (returning)
$table->timestamps();
$table->index('started_at');
What: Creates index on started_at column
Why: Fast date queries like "sessions today"
Performance: Essential for time-based filtering
$table->index('visitor_id');
What: Creates index on visitor_id foreign key
Why: Fast lookup of all sessions for a visitor
Performance: Speeds up JOIN queries with visitors table
2.4 create “Categories” table
The categories table organizes pages into hierarchical topics (Category → Subcategory → Sub-subcategory), enabling content-based analytics. It reveals which topics attract most visitors, measures engagement by content type, identifies high-performing categories, and helps optimize content strategy. Without it, you'd only see individual page performance—not broader content themes that drive traffic and engagement.
$table->id();
$table->string('name', 100);
What: Creates VARCHAR(100) for category name
Why: Human-readable category label
Required: Must have value
Example: "Electronics", "Mobile Phones", "Samsung"
$table->string('slug',100)->unique();
What: Creates VARCHAR(100) for URL-friendly identifier
Why: Use in URLs and unique lookups
->unique(): No duplicate slugs allowed
Example: "electronics", "mobile-phones", "samsung"
$table->foreignId('parent_id')->nullable()->constrained('categories')->onDelete('cascade';
What: Creates self-referencing foreign key
Why: Build hierarchy (subcategory points to parent category)
->nullable(): NULL for top-level categories
->constrained('categories'): Links to categories.id (same table)
->onDelete('cascade'): Deleting parent deletes all children
Example: parent_id = NULL (top level), parent_id = 5 (child of category 5)
$table->integer('level')->default(1); // 1=category, 2=subcategory, 3=sub-subcategory
What: Creates INTEGER for hierarchy depth
Why: Quick identification of category level without recursive queries
->default(1): Top-level categories
Values: 1 (category), 2 (subcategory), 3 (sub-subcategory)
$table->timestamps();
$table->index('slug');
What: Creates index on slug column
Why: Fast lookups like "WHERE slug = 'electronics'"
Performance: Speeds up category searches
$table->index('parent_id');
What: Creates index on parent_id foreign key
Why: Fast queries for "find all children of category X"
Performance: Essential for hierarchical queries
3. Create Models
3.1 Visitor.php
This Visitor model(app/Models/Visitor.php) in your Laravel project is used to store and track information about every visitor to your website. It keeps track of “who visited, from where, when, how many times, and on what device.
fillable
The $fillable allows mass assignment for visitor properties like IP, location, device, and visit timestamps.
protected $fillable = [
'ip_address', 'country_code', 'country_name', 'city', 'region',
'latitude', 'longitude', 'user_agent', 'browser', 'platform',
'is_mobile', 'first_visit_at', 'last_visit_at', 'visit_count'
];
casts
The $casts ensures proper data types, converting is_mobile to boolean, first_visit_at and last_visit_at to datetime, and latitude/longitude to decimals.
protected $casts = [
'is_mobile' => 'boolean',
'first_visit_at' => 'datetime',
'last_visit_at' => 'datetime',
'latitude' => 'decimal:7',
'longitude' => 'decimal:7',
];
sessions
The sessions() method defines a one-to-many relationship with the Session model, tracking all sessions per visitor.
public function sessions(): HasMany
{
return $this->hasMany(Session::class);
}
pageVisits
Similarly, pageVisits() links the visitor to multiple PageVisit records, recording every page viewed.
public function pageVisits(): HasMany
{
return $this->hasMany(PageVisit::class);
}
3.2 Session.php
This Session(app/Models/Session.php) model in Laravel is used to track individual sessions of a visitor, basically, a period of activity a visitor spends on your website in one go.
Tracks each visitor's session separately. It stores session-related information—duration,page views, entry/exit pages. It links with Visitor and PageVisits.
It can be used for analytics, such as:
Average session duration
Popular entry/exit pages
Identifying returning visitors
protected $fillable
The $fillable array defines which fields can be mass-assigned. When using Session::create($data),
only these fields will be inserted into the database. This protects against mass assignment vulnerabilities.
Here, the fields are visitor_id, session_id, started_at, ended_at, duration_seconds, page_views, entry_page, exit_page, and is_returning.
protected $fillable = [
'visitor_id', 'session_id', 'started_at', 'ended_at',
'duration_seconds', 'page_views', 'entry_page', 'exit_page', 'is_returning'
];
protected $casts
The $casts array automatically converts the data type of attributes. started_at and ended_at are cast to datetime,
while is_returning is cast to boolean. This makes it easy to work with timestamps and logical values directly without manually parsing or formatting them.
visitor() function
The visitor() function defines a BelongsTo relationship. This indicates that a session belongs to a specific visitor. You can access the related visitor from a session using $session->visitor. It establishes the link between a session and the Visitor model.
pageVisits() function
The pageVisits() function defines a HasMany relationship. This means a session can have multiple page visits. Using $session->pageVisits, you can retrieve all page visit records associated with that session, allowing tracking of user activity during that session.
4. Services
// app/Services/AnalyticsService.php
use App\Models\Visitor;
This imports the Session model into your file.
use App\Models\Session;
It allows you to create, read, update, and delete session data from the sessions database table.
use App\Models\PageVisit;
This imports the PageVisit model. It allows you to store each page a visitor opens.
use Illuminate\Support\Facades\Http;
This enables Laravel’s HTTP Client. It allows you to make API requests to external services.
use Illuminate\Support\Str;
This imports Laravel’s String Helper class.
use Jenssegers\Agent\Agent;
Detect device, OS & browser
protected agent;
protected $agent;
public function __construct()
{
$this->agent = new Agent();
}
This is a class property. It stores an object of the Agent class, which is used to detect the visitor’s browser, device, and operating system.
public function trackVisit($data)
public function trackVisit($data)
{
$ipAddress = $data['ip'] ?? request()->ip();
// Get or create visitor
$visitor = $this->getOrCreateVisitor($ipAddress, $data);
// Get or create session
$session = $this->getOrCreateSession($visitor, $data);
// Record page visit
$pageVisit = $this->recordPageVisit($visitor, $session, $data);
return [
'visitor' => $visitor,
'session' => $session,
'page_visit' => $pageVisit
];
}
This function tracks a website visit by detecting the IP, creating or finding a visitor, managing the session, recording the visited page, and returning visitor, session, and page details.
getOrCreateVisitor
protected function getOrCreateVisitor($ipAddress, $data)
{
$visitor = Visitor::where('ip_address', $ipAddress)->first();
if (!$visitor) {
$geoData = $this->getGeoLocation($ipAddress);
$this->agent->setUserAgent($data['user_agent'] ?? request()->userAgent());
$visitor = Visitor::create([
'ip_address' => $ipAddress,
'country_code' => $geoData['country_code'] ?? null,
'country_name' => $geoData['country_name'] ?? null,
'city' => $geoData['city'] ?? null,
'region' => $geoData['region'] ?? null,
'latitude' => $geoData['latitude'] ?? null,
'longitude' => $geoData['longitude'] ?? null,
'user_agent' => $data['user_agent'] ?? request()->userAgent(),
'browser' => $this->agent->browser(),
'platform' => $this->agent->platform(),
'is_mobile' => $this->agent->isMobile(),
'first_visit_at' => now(),
'last_visit_at' => now(),
'visit_count' => 1
]);
} else {
$visitor->update([
'last_visit_at' => now(),
'visit_count' => $visitor->visit_count + 1
]);
}
return $visitor;
}
Checks if the visitor exists in the database using IP.
If not exists:
Get geo-location data.
Detects browser/platform using Agent.
Create a new Visitor record with all relevant info.
If exists:
Update last_visit_at and increment visit_count.
Goal: Track unique visitors and their info.
getOrCreateSession
protected function getOrCreateSession($visitor, $data)
{
$sessionId = $data['session_id'] ?? session()->getId();
$session = Session::where('session_id', $sessionId)->first();
$isReturning = $visitor->visit_count > 1;
if (!$session) {
$session = Session::create([
'visitor_id' => $visitor->id,
'session_id' => $sessionId,
'started_at' => now(),
'entry_page' => $data['url'] ?? request()->url(),
'is_returning' => $isReturning,
'page_views' => 1
]);
} else {
$session->update([
'ended_at' => now(),
'duration_seconds' => now()->diffInSeconds($session->started_at),
'exit_page' => $data['url'] ?? request()->url(),
'page_views' => $session->page_views + 1
]);
}
return $session;
}
Purpose: Track a user's session on the site.
$sessionId → Use existing session ID or create one using Laravel’s session.
If session doesn’t exist: Create a new session with started_at, entry_page, and page view count = 1.
If session exists: Update the session with ended_at, duration_seconds, exit_page, and increment page views.
$isReturning → Checks if the visitor is returning (visit_count > 1).
recordPageVisit
protected function recordPageVisit($visitor, $session, $data)
{
return PageVisit::create([
'visitor_id' => $visitor->id,
'session_id' => $session->id,
'category_id' => $data['category_id'] ?? null,
'url' => $data['url'] ?? request()->url(),
'page_title' => $data['page_title'] ?? null,
'referrer' => $data['referrer'] ?? request()->header('referer'),
'time_spent_seconds' => $data['time_spent'] ?? 0,
'visited_at' => now()
]);
}
Logs each page the user visits.
Stores:
Which visitor
Which session
Page URL & title
Referrer (previous page)
Time spent
Timestamp
getGeoLocation
protected function getGeoLocation($ipAddress)
{
// Skip for local IPs
if ($ipAddress === '127.0.0.1' || $ipAddress === '::1') {
return [];
}
try {
// Using ipapi.co (free tier: 1000 requests/day)
$response = Http::timeout(3)->get("https://ipapi.co/{$ipAddress}/json/");
if ($response->successful()) {
$data = $response->json();
return [
'country_code' => $data['country_code'] ?? null,
'country_name' => $data['country_name'] ?? null,
'city' => $data['city'] ?? null,
'region' => $data['region'] ?? null,
'latitude' => $data['latitude'] ?? null,
'longitude' => $data['longitude'] ?? null,
];
}
} catch (\Exception $e) {
\Log::error('Geo-location failed: ' . $e->getMessage());
}
return [];
}
Uses ipapi.co to get geo-location of the visitor’s IP.
Skips local IPs (127.0.0.1).
Returns an array with country, city, region, latitude, longitude.
Logs errors if API fails.
updateTimeSpent
public function updateTimeSpent($pageVisitId, $timeSpent)
{
$pageVisit = PageVisit::find($pageVisitId);
if ($pageVisit) {
$pageVisit->update(['time_spent_seconds' => $timeSpent]);
}
}
Updates the time spent on a page for a specific PageVisit.
Can be used later to track user engagement per page.
5. Controllers
5.1 AnalyticsTrackingController
Track And track
protected $analytics;
public function __construct(AnalyticsService $analytics)
{
$this->analytics = $analytics;
}
public function track(Request $request)
{
$validated = $request->validate([
'url' => 'required|string',
'page_title' => 'nullable|string',
'referrer' => 'nullable|string',
'category_id' => 'nullable|integer',
'session_id' => 'nullable|string',
]);
$result = $this->analytics->trackVisit([
'url' => $validated['url'],
'page_title' => $validated['page_title'] ?? null,
'referrer' => $validated['referrer'] ?? null,
'category_id' => $validated['category_id'] ?? null,
'session_id' => $validated['session_id'] ?? session()->getId(),
'ip' => $request->ip(),
'user_agent' => $request->userAgent(),
]);
return response()->json([
'success' => true,
'page_visit_id' => $result['page_visit']->id
]);
}
When a user opens a page, the track() function collects information such as the page URL, page title, referrer link, and category. First, it checks whether the required data is valid (for example, the URL must be provided). Then, it sends this information to the AnalyticsService, which saves the visit details in the database. At the same time, it automatically records the user’s IP address and their browser or device details. If no session ID is sent, Laravel creates one automatically. Finally, the system sends back a response confirming that the data was saved successfully and returns the ID of the stored page visit record.
updateTimeSpent
public function updateTimeSpent(Request $request)
{
$validated = $request->validate([
'page_visit_id' => 'required|integer',
'time_spent' => 'required|integer',
]);
$this->analytics->updateTimeSpent(
$validated['page_visit_id'],
$validated['time_spent']
);
return response()->json(['success' => true]);
}
This function is used to update the amount of time a user spent on a page.
When a user leaves a page, the updateTimeSpent() method receives two pieces of data: page_visit_id (which identifies the specific page visit record) and time_spent (the duration the user stayed on the page, in seconds). First, it validates the input to ensure both values are provided and are integers. Then, it sends this data to the updateTimeSpent() method in the AnalyticsService, where the time spent is updated in the database for that page visit. Finally, the function returns a JSON response confirming that the update was successful.
5.2 AnalyticsDashboardController.php
overview
public function overview(Request $request)
{
$period = $request->get('period', 'today'); // today, week, month, year
$startDate = $this->getStartDate($period);
return response()->json([
'total_visitors' => Visitor::count(),
'unique_visitors' => Visitor::where('first_visit_at', '>=', $startDate)->count(),
'total_visits' => PageVisit::where('visited_at', '>=', $startDate)->count(),
'total_sessions' => Session::where('started_at', '>=', $startDate)->count(),
'new_visitors' => Visitor::where('first_visit_at', '>=', $startDate)->count(),
'returning_visitors' => Session::where('started_at', '>=', $startDate)
->where('is_returning', true)->distinct('visitor_id')->count(),
'avg_time_spent' => Session::where('started_at', '>=', $startDate)
->avg('duration_seconds'),
'avg_pages_per_session' => Session::where('started_at', '>=', $startDate)
->avg('page_views'),
]);
}
This function provides an analytics overview for a website over a specified time period. It first checks the period parameter from the request, which can be today, week, month, or year, and defaults to today if none is provided. Using this period, it calculates the startDate to determine the beginning of the selected timeframe. The function then gathers key metrics from the database and returns them as a JSON response. These metrics include the total number of visitors, the number of unique visitors within the period, total page visits, total sessions, new visitors, and returning visitors. Additionally, it calculates the average time spent per session and the average number of pages viewed per session. Overall, this method provides a clear summary of website traffic and user engagement, making it useful for dashboards and reporting.
dailyTraffic
public function dailyTraffic(Request $request)
{
$days = $request->get('days', 30);
$startDate = Carbon::now()->subDays($days);
$traffic = PageVisit::select(
DB::raw('DATE(visited_at) as date'),
DB::raw('COUNT(*) as visits'),
DB::raw('COUNT(DISTINCT visitor_id) as unique_visitors')
)
->where('visited_at', '>=', $startDate)
->groupBy('date')
->orderBy('date')
->get();
return response()->json($traffic);
}
This function provides a daily traffic report for the website over a specified number of days. It first retrieves the days parameter from the request, defaulting to 30 if no value is provided. Using this, it calculates the startDate by subtracting the number of days from the current date. The method then queries the PageVisit table to gather traffic data, grouping it by each date. For each day, it calculates the total number of page visits and the number of unique visitors. The results are ordered by date to show the traffic in chronological order. Finally, the function returns the collected data as a JSON response, which can be used for charts or analytics dashboards to visualize daily website activity.
monthlyTraffic
public function monthlyTraffic(Request $request)
{
$months = $request->get('months', 12);
$startDate = Carbon::now()->subMonths($months);
$traffic = PageVisit::select(
DB::raw('DATE_FORMAT(visited_at, "%Y-%m") as month'),
DB::raw('COUNT(*) as visits'),
DB::raw('COUNT(DISTINCT visitor_id) as unique_visitors')
)
->where('visited_at', '>=', $startDate)
->groupBy('month')
->orderBy('month')
->get();
return response()->json($traffic);
}
This function provides a monthly traffic report for the website over a specified number of months. It first retrieves the months parameter from the request, defaulting to 12 months if none is provided. Using this value, it calculates the startDate by subtracting the number of months from the current date. The method then queries the PageVisit table to collect traffic data, grouping the results by month. For each month, it calculates the total number of page visits and the number of unique visitors. The data is ordered chronologically by month and returned as a JSON response. This output can be used to generate charts or dashboards to visualize monthly website traffic trends.
countryStats
public function countryStats()
{
$stats = Visitor::select(
'country_name',
'country_code',
DB::raw('COUNT(*) as visitor_count'),
DB::raw('AVG(visit_count) as avg_visits')
)
->whereNotNull('country_name')
->groupBy('country_name', 'country_code')
->orderByDesc('visitor_count')
->get();
return response()->json($stats);
}
This function provides a summary of visitor statistics by country. It queries the Visitor table to collect data for each country where country_name is not null. For each country, it counts the total number of visitors and calculates the average number of visits per visitor. The results are grouped by country_name and country_code and are ordered in descending order based on the total number of visitors, so the countries with the highest traffic appear first. Finally, the function returns this data as a JSON response, which can be used for analytics dashboards, reports, or visualizations of visitor distribution by country.
mostVisitedPages
public function mostVisitedPages(Request $request)
{
$limit = $request->get('limit', 10);
$startDate = $this->getStartDate($request->get('period', 'month'));
$pages = PageVisit::select(
'url',
'page_title',
DB::raw('COUNT(*) as visit_count'),
DB::raw('AVG(time_spent_seconds) as avg_time_spent')
)
->where('visited_at', '>=', $startDate)
->groupBy('url', 'page_title')
->orderByDesc('visit_count')
->limit($limit)
->get();
return response()->json($pages);
}
This function provides a report of the most visited pages on the website over a specified period. It first retrieves the limit parameter from the request, which defines how many top pages to return, defaulting to 10. It also determines the startDate based on the period parameter (such as today, week, month, or year) using the getStartDate() helper. The method then queries the PageVisit table to collect data for each page visited since the start date. For every page, it calculates the total number of visits and the average time users spent on that page. The results are grouped by page URL and title, ordered in descending order by visit count, and limited to the requested number of top pages. Finally, the function returns this information as a JSON response, which can be used to analyze user engagement and popular content on the site.
exitPages
public function exitPages(Request $request)
{
$limit = $request->get('limit', 10);
$startDate = $this->getStartDate($request->get('period', 'month'));
$exitPages = Session::select(
'exit_page',
DB::raw('COUNT(*) as exit_count')
)
->whereNotNull('exit_page')
->where('started_at', '>=', $startDate)
->groupBy('exit_page')
->orderByDesc('exit_count')
->limit($limit)
->get();
return response()->json($exitPages);
}
This function provides a report of the most common exit pages on the website over a specified period. It first retrieves the limit parameter from the request, which defines how many top exit pages to return, defaulting to 10. It also calculates the startDate using the period parameter (such as today, week, month, or year) through the getStartDate() helper. The method then queries the Session table to collect data for all sessions that started on or after the start date and have a non-null exit_page. For each exit page, it counts how many sessions ended on that page. The results are grouped by exit_page, ordered in descending order by the number of exits, and limited to the requested number of top pages. Finally, the function returns this data as a JSON response, which can be used to identify pages where users most frequently leave the site.
categoryStats
public function categoryStats(Request $request)
{
$level = $request->get('level', 1); // 1, 2, or 3
$startDate = $this->getStartDate($request->get('period', 'month'));
$stats = Category::select(
'categories.id',
'categories.name',
'categories.level',
DB::raw('COUNT(page_visits.id) as visit_count'),
DB::raw('AVG(page_visits.time_spent_seconds) as avg_time_spent')
)
->leftJoin('page_visits', 'categories.id', '=', 'page_visits.category_id')
->where('categories.level', $level)
->where('page_visits.visited_at', '>=', $startDate)
->groupBy('categories.id', 'categories.name', 'categories.level')
->orderByDesc('visit_count')
->get();
return response()->json($stats);
}
This function provides analytics for website categories based on a specified level and period. It first retrieves the level parameter from the request, which can be 1, 2, or 3, representing different category levels, and defaults to 1. It also calculates the startDate using the period parameter (like today, week, month, or year) via the getStartDate() helper. The method then queries the Category table and performs a left join with the page_visits table to gather data about pages under each category. For every category, it counts the total page visits and calculates the average time spent on pages in that category since the start date. The results are grouped by category ID, name, and level, ordered by descending visit count, and returned as a JSON response. This provides insights into which categories are the most visited and how engaged users are with each category on the website.
visitorMap
public function visitorMap()
{
$visitors = Visitor::select(
'country_name',
'city',
'latitude',
'longitude',
DB::raw('COUNT(*) as count')
)
->whereNotNull('latitude')
->whereNotNull('longitude')
->groupBy('country_name', 'city', 'latitude', 'longitude')
->get();
return response()->json($visitors);
}
This function generates data for a visitor map, showing where users are located geographically. It queries the Visitor table to collect information about each visitor’s country_name, city, latitude, and longitude. Only records with non-null latitude and longitude are considered, ensuring that location data is valid. The query groups the results by country, city, and coordinates, and counts how many visitors are from each location. Finally, the function returns this data as a JSON response, which can be used to create a map visualization of user distribution across different cities and countries.
getStartDate
protected function getStartDate($period)
{
return match($period) {
'today' => Carbon::today(),
'week' => Carbon::now()->subWeek(),
'month' => Carbon::now()->subMonth(),
'year' => Carbon::now()->subYear(),
default => Carbon::now()->subMonth(),
};
}
This protected function determines the starting date for analytics queries based on a given time period. It takes a period parameter, which can be 'today', 'week', 'month', or 'year'. Using PHP’s match expression, it returns a Carbon date object representing the beginning of that period. For example, if the period is 'today', it returns the start of the current day; if 'week', it returns the date one week ago; 'month' returns one month ago; and 'year' returns one year ago. If the period value is not recognized, it defaults to one month ago. This function is used throughout the analytics system to filter data based on the selected timeframe.
6. Create API Routes
<?php
// routes/api.php
use App\Http\Controllers\AnalyticsTrackingController;
use App\Http\Controllers\AnalyticsDashboardController;
use Illuminate\Support\Facades\Route;
// Public tracking endpoints (CORS enabled)
analytics/track
Registers a route that listens for POST requests to /api/analytics/track.
When a request arrives, Laravel calls the track method on AnalyticsTrackingController.
Typical use: client sends pageview / event data (path, title, referrer, timestamp, user id/anon id). Because it’s POST, payload is in the request body (JSON or form data).
Route::post('/analytics/track', [AnalyticsTrackingController::class, 'track']);
analytics/time-spent
Calls updateTimeSpent on AnalyticsTrackingController.
Typical use: the client reports session duration / time-on-page or periodic heartbeats. Also a POST because you’re sending data to store/update.
Registers POST /api/analytics/time-spent.
Route::post('/analytics/time-spent', [AnalyticsTrackingController::class, 'updateTimeSpent']);
// Protected dashboard endpoints (requires authentication)
Route::middleware(['auth:sanctum'])->prefix('analytics')->group(function () {
Overview
Route::get('/overview', [AnalyticsDashboardController::class, 'overview']); is a Laravel route definition that connects an HTTP GET request to a specific controller method. When a client or admin panel makes a request to the URL /api/analytics/overview, Laravel automatically triggers the overview() method inside the AnalyticsDashboardController. This method usually gathers high-level analytics data such as total visitors, pageviews, unique users, device statistics, or any summarized metrics needed for the dashboard’s overview section. Because this route is placed inside an auth:sanctum middleware group, it can only be accessed by authenticated users, ensuring that sensitive analytics information is protected and visible only to authorized administrators. This route essentially acts as the entry point for loading the main summary of your analytics dashboard.
Route::get('/overview', [AnalyticsDashboardController::class, 'overview']);
daily-traffic
The route Route::get('/daily-traffic', [AnalyticsDashboardController::class, 'dailyTraffic']); defines an API endpoint that responds to GET requests made to /api/analytics/daily-traffic. When this URL is accessed, Laravel automatically calls the dailyTraffic() method inside the AnalyticsDashboardController. This method is typically responsible for generating and returning daily traffic analytics—such as the number of visitors, pageviews, or sessions recorded on each day. Because this route is placed inside an authentication-protected group using auth:sanctum, only authenticated admin users can access this data. This ensures that your daily traffic statistics remain secure and are only visible within the analytics dashboard.
Route::get('/daily-traffic', [AnalyticsDashboardController::class, 'dailyTraffic']);
Monthly-traffic
This route handles GET requests made to /api/analytics/monthly-traffic. When called, Laravel executes the monthlyTraffic() method from the AnalyticsDashboardController. This endpoint provides aggregated traffic statistics by month—showing patterns such as monthly growth, total visits per month, or long-term trends. This data is especially useful for analytics dashboards that visualize month-to-month performance.
Route::get('/monthly-traffic', [AnalyticsDashboardController::class, 'monthlyTraffic']);
Country-stats
This route registers a GET endpoint at /api/analytics/country-stats. When someone accesses this endpoint, Laravel calls the countryStats() method inside the AnalyticsDashboardController. That method usually compiles analytics data grouped by country—such as how many visitors came from each region—helping visualize global traffic distribution. Because this route sits behind Sanctum authentication, only logged-in and authorized users can access these geographic statistics.
Route::get('/country-stats', [AnalyticsDashboardController::class, 'countryStats']);
Most-visited-pages
Route::get('/most-visited-pages', [AnalyticsDashboardController::class, 'mostVisitedPages']); defines an API endpoint that returns the list of pages with the highest number of visits. When a client sends a GET request to /api/analytics/most-visited-pages, Laravel automatically triggers the mostVisitedPages() method inside the AnalyticsDashboardController. That method usually fetches analytics data—such as which pages users view the most, how many visits each page received, and possibly ranking or percentages. Because this route is inside the authenticated middleware group, only logged-in or authorized users (such as admin panel users) can access this data. This ensures the analytics information remains secure and visible only to authorized people.
Route::get('/most-visited-pages', [AnalyticsDashboardController::class, 'mostVisitedPages']);
exit-pages
This route responds to a GET request at /api/analytics/exit-pages and invokes the exitPages() method inside the controller. It provides data about which pages users most frequently leave the site from. These insights are useful for identifying potential issues with pages that may cause users to drop off.
Route::get('/exit-pages', [AnalyticsDashboardController::class, 'exitPages']);
Category-stats
Route::get('/category-stats', [AnalyticsDashboardController::class, 'categoryStats']);
This route returns metrics grouped by content category. When /api/analytics/category-stats is requested, categoryStats() compiles statistics like visits, conversions, or average engagement for each category (e.g., blog, product, support), enabling category-level comparison and strategic content decisio
Route::get('/category-stats', [AnalyticsDashboardController::class, 'categoryStats']);
Visitor-map
The route Route::get('/visitor-map', [AnalyticsDashboardController::class, 'visitorMap']); defines a GET endpoint in your Laravel API that is responsible for providing data used to visualize the geographical distribution of visitors on your site. When an authenticated request is made to /api/analytics/visitor-map, Laravel calls the visitorMap() method of the AnalyticsDashboardController. This method typically gathers location-based data such as visitor counts by country, region, or city, and returns it in a structured JSON format suitable for rendering a map or heatmap on the analytics dashboard. Since this route is protected by the auth:sanctum middleware, only authorized users can access it, ensuring that sensitive visitor location data is kept secure.
Route::get('/visitor-map', [AnalyticsDashboardController::class, 'visitorMap']);
});
Frontend

0 Comments