Skip to content

Commit 0266cdb

Browse files
committed
Added base
1 parent 5c4c293 commit 0266cdb

File tree

7 files changed

+250
-1
lines changed

7 files changed

+250
-1
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ composer.phar
44
# Commit your application's lock file https://getcomposer.org/doc/01-basic-usage.md#commit-your-composer-lock-file-to-version-control
55
# You may choose to ignore a library lock file http://getcomposer.org/doc/02-libraries.md#lock-file
66
# composer.lock
7+
composer.lock

README.md

Lines changed: 110 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,111 @@
11
# laravel-pageview-counter
2-
A stupid simple analytics tool to keep track of page views
2+
3+
A stupid simple analytics tool to keep track of page views in Laravel projects. It logs requests to a database table and you can access the data with an Eloquent model.
4+
5+
## Installation
6+
7+
```bash
8+
composer require voidgraphics/laravel-pageview-counter
9+
```
10+
11+
You need to publish the migration that will add the pageviews table:
12+
13+
```bash
14+
php artisan vendor:publish --tag=pageview-counter-migrations
15+
```
16+
17+
Then run the migrations
18+
19+
```bash
20+
php artisan migrate
21+
```
22+
23+
After this, register the `LogRequest` middleware. You can do it globally or on specific routes. Here's how to install it globally in Laravel 11
24+
25+
```php
26+
// bootstrap/app.php
27+
28+
<?php
29+
30+
use Illuminate\Foundation\Application;
31+
use Illuminate\Foundation\Configuration\Exceptions;
32+
use Illuminate\Foundation\Configuration\Middleware;
33+
use PageviewCounter\Middleware\LogRequest;
34+
35+
return Application::configure(basePath: dirname(__DIR__))
36+
// ...
37+
->withMiddleware(function (Middleware $middleware) {
38+
$middleware->append(LogRequest::class);
39+
})
40+
// ...
41+
->create();
42+
```
43+
44+
## Accessing the data
45+
46+
Every request hitting those routes will now be logged. You can access that data using the `PageviewCounter\Models\Pageview` model. It has 3 scopes to help you prepare the data for display.
47+
48+
```php
49+
Pageview::daily()->get(); // Returns the number of views grouped by date and path.
50+
51+
/*
52+
Illuminate\Database\Eloquent\Collection {
53+
all: [
54+
PageviewCounter\Models\Pageview {
55+
views: 156,
56+
path: "/",
57+
date: "2024-07-18",
58+
},
59+
PageviewCounter\Models\Pageview {
60+
views: 68,
61+
path: "about",
62+
date: "2024-07-18",
63+
}
64+
PageviewCounter\Models\Pageview {
65+
views: 289,
66+
path: "/",
67+
date: "2024-07-17",
68+
},
69+
PageviewCounter\Models\Pageview {
70+
views: 32,
71+
path: "about",
72+
date: "2024-07-17",
73+
}
74+
],
75+
}
76+
*/
77+
```
78+
79+
```php
80+
Pageview::withoutBots()->get(); // Exclude most popular bots by user-agent
81+
82+
/*
83+
Shorthand for:
84+
85+
$query->where('useragent', 'not like', '%bot%')
86+
->where('useragent', 'not like', '%python-requests%')
87+
->where('useragent', 'not like', '%http%')
88+
->where('useragent', 'not like', '%node-fetch%')
89+
->where('useragent', 'not like', '%postman%')
90+
->where('useragent', 'not like', '%curl%')
91+
*/
92+
```
93+
94+
```php
95+
Pageview::uniqueVisitors()->get(); // Returns the count of unique visitors, grouped by date
96+
97+
/*
98+
Illuminate\Database\Eloquent\Collection {
99+
all: [
100+
PageviewCounter\Models\Pageview {
101+
unique_visitors: 176,
102+
date: "2024-07-18",
103+
},
104+
PageviewCounter\Models\Pageview {
105+
unique_visitors: 302,
106+
date: "2024-07-17",
107+
}
108+
],
109+
}
110+
*/
111+
```

composer.json

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
"name": "voidgraphics/laravel-pageview-counter",
3+
"description": "A stupid simple analytics tool to keep track of page views",
4+
"autoload": {
5+
"psr-4": {
6+
"PageviewCounter\\": "src/"
7+
}
8+
},
9+
"authors": [
10+
{
11+
"name": "Adrien Leloup",
12+
"email": "[email protected]"
13+
}
14+
],
15+
"require": {
16+
"laravel/framework": "^11.0"
17+
},
18+
"extra": {
19+
"laravel": {
20+
"providers": [
21+
"PageviewCounter\\ServiceProvider"
22+
]
23+
}
24+
}
25+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
use Illuminate\Database\Migrations\Migration;
4+
use Illuminate\Database\Schema\Blueprint;
5+
use Illuminate\Support\Facades\Schema;
6+
7+
return new class extends Migration
8+
{
9+
/**
10+
* Run the migrations.
11+
*/
12+
public function up(): void
13+
{
14+
Schema::create('pageviews', function (Blueprint $table) {
15+
$table->id();
16+
$table->string('path');
17+
$table->string('method', 12);
18+
$table->text('useragent');
19+
$table->string('visitorid');
20+
$table->timestamps();
21+
});
22+
}
23+
24+
/**
25+
* Reverse the migrations.
26+
*/
27+
public function down(): void
28+
{
29+
Schema::dropIfExists('pageviews');
30+
}
31+
};

src/Middleware/LogRequest.php

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
namespace PageviewCounter\Middleware;
4+
5+
use PageviewCounter\Models\Pageview;
6+
use Illuminate\Http\Request;
7+
use Closure;
8+
9+
class LogRequest
10+
{
11+
public function handle(Request $request, Closure $next)
12+
{
13+
// Make it an after middleware
14+
$response = $next($request);
15+
16+
try {
17+
Pageview::create([
18+
'path' => $request->path(),
19+
'method' => $request->method(),
20+
'useragent' => $request->userAgent(),
21+
'visitorid' => crypt($request->ip(), config('app.key'))
22+
]);
23+
24+
return $response;
25+
} catch (Error $e) {
26+
report($e);
27+
return $response;
28+
}
29+
}
30+
}

src/Models/Pageview.php

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php
2+
3+
namespace PageviewCounter\Models;
4+
5+
use Illuminate\Database\Eloquent\Builder;
6+
use Illuminate\Database\Eloquent\Model;
7+
8+
class Pageview extends Model
9+
{
10+
protected $guarded = [];
11+
12+
public function scopeDaily(Builder $query): Builder
13+
{
14+
return $query->selectRaw('COUNT(*) views, path, DATE(created_at) date')
15+
->groupBy(['date', 'path'])
16+
->orderBy('date', 'desc');
17+
}
18+
19+
public function scopeWithoutBots(Builder $query): Builder
20+
{
21+
return $query->where('useragent', 'not like', '%bot%')
22+
->where('useragent', 'not like', '%python-requests%')
23+
->where('useragent', 'not like', '%http%')
24+
->where('useragent', 'not like', '%node-fetch%')
25+
->where('useragent', 'not like', '%postman%')
26+
->where('useragent', 'not like', '%curl%');
27+
}
28+
29+
public function scopeUniqueVisitors(Builder $query): Builder
30+
{
31+
return $query->selectRaw('COUNT(DISTINCT visitorid) unique_visitors, DATE(created_at) date')
32+
->groupBy(['date'])
33+
->orderBy('date', 'desc');
34+
}
35+
}

src/ServiceProvider.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
namespace PageviewCounter;
4+
5+
use Illuminate\Support\ServiceProvider as LaravelServiceProvider;
6+
7+
class ServiceProvider extends LaravelServiceProvider
8+
{
9+
/**
10+
* Bootstrap any package services.
11+
*/
12+
public function boot(): void
13+
{
14+
$this->publishesMigrations([
15+
__DIR__.'/../database/migrations' => database_path('migrations'),
16+
], 'pageview-counter-migrations');
17+
}
18+
}

0 commit comments

Comments
 (0)