Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,12 @@
- [Views](#visit-monitoring-views)
- [Ajax Requests](#ajax-requests)
- [Visit Monitoring Guest Mode](#visit-monitoring-guest-mode)
- [Visit Monitoring Custom Conditions](#visit-monitoring-custom-conditions)
- [Action Monitoring](#action-monitoring)
- [Views](#action-monitoring-views)
- [Reverse Proxy Config](#action-monitoring-reverse-proxy-config)
- [Action Monitoring Guest Mode](#action-monitoring-guest-mode)
- [Action Monitoring Custom Conditions](#action-monitoring-custom-conditions)
- [Authentication Monitoring](#authentication-monitoring)
- [Views](#authentication-monitoring-views)
- [How to use in big projects](#how-to-use-in-big-projects)
Expand Down Expand Up @@ -366,6 +368,34 @@ When set to `false`, only authenticated user visits will be recorded.
],
```

<a name="visit-monitoring-custom-conditions"></a>
### Visit Monitoring Custom Conditions

The `Laravel User Monitoring` package allows you to define custom conditions for visit monitoring.
Conditions give you full control over when a visit should be logged.

#### 🔧 How It Works

- Conditions are checked before a visit is stored.
- If any condition returns false, the visit will be skipped.
- You can define conditions as closures or as class-based rules.

```php
'visit_monitoring' => [
'conditions' => [
// Class-based condition (must implement MonitoringCondition interface)
\App\Monitoring\YourCustomCondition::class,

// Closure-based condition (receives the Request and authenticated User)
function (Illuminate\Http\Request $request) {
$user = $request->user();

return $user && $user->isAdmin();
},
],
],
```

<a name="action-monitoring"></a>
## Action Monitoring

Expand Down Expand Up @@ -457,6 +487,34 @@ When set to `false`, only authenticated user visits will be recorded.
],
```

<a name="action-monitoring-custom-conditions"></a>
### Action Monitoring Custom Conditions

The `Laravel User Monitoring` package lets you define custom conditions for action monitoring (create, update, delete events on models).
Conditions give you control over when model actions should be logged.

#### 🔧 How It Works

- Conditions are checked before an action is stored.
- If any condition returns false, the action will be skipped.
- You can define conditions as closures or class-based rules.

```php
'action_monitoring' => [
'conditions' => [
// Class-based condition (must implement MonitoringCondition interface)
\App\Monitoring\YourCustomCondition::class,

// Closure-based condition (receives the Request and authenticated User)
function (Illuminate\Http\Request $request) {
$user = $request->user();

return $user && $user->isAdmin();
},
],
],
```

<a name="authentication-monitoring"></a>
## Authentication Monitoring

Expand Down
17 changes: 17 additions & 0 deletions config/user-monitoring.php
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,16 @@
* Determines whether to store `visits` even when the user is not logged in.
*/
'guest_mode' => true,

/*
| Here you can define one or more conditions that determine whether a visit
| should be logged. Each condition must return a boolean (true = log visit,
| false = skip logging).
|
| All conditions are evaluated before monitoring. If any condition returns
| false, the visit will NOT be recorded.
*/
'conditions' => [],
],

/*
Expand Down Expand Up @@ -136,6 +146,13 @@
* Determines whether to store `actions` even when the user is not logged in.
*/
'guest_mode' => true,

/*
* Here you can define one or more conditions that determine whether an action
* should be logged. Each condition must return a boolean (true = log action,
* false = skip logging).
*/
'conditions' => [],
],

/*
Expand Down
10 changes: 10 additions & 0 deletions src/Contracts/MonitoringCondition.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace Binafy\LaravelUserMonitoring\Contracts;

use Illuminate\Http\Request;

interface MonitoringCondition
{
public function shouldMonitor(Request $request): bool;
}
34 changes: 33 additions & 1 deletion src/Middlewares/VisitMonitoringMiddleware.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Binafy\LaravelUserMonitoring\Middlewares;

use Binafy\LaravelUserMonitoring\Contracts\MonitoringCondition;
use Binafy\LaravelUserMonitoring\Utills\Detector;
use Binafy\LaravelUserMonitoring\Utills\UserUtils;
use Closure;
Expand All @@ -25,7 +26,12 @@ public function handle(Request $request, Closure $next): mixed
return $next($request);
}

$detector = new Detector();
// Custom conditions from config
if (! $this->shouldMonitor($request)) {
return $next($request);
}

$detector = new Detector;
$exceptPages = config('user-monitoring.visit_monitoring.except_pages', []);

if (empty($exceptPages) || !$this->checkIsExceptPages($request->path(), $exceptPages)) {
Expand Down Expand Up @@ -53,4 +59,30 @@ protected function checkIsExceptPages(string $page, array $exceptPages): bool
{
return collect($exceptPages)->contains($page);
}

/**
* Determine if monitoring should be performed for the given request and user.
*/
protected function shouldMonitor(Request $request): bool
{
$config = config('user-monitoring.visit_monitoring.conditions', []);

foreach ($config as $condition) {
if (is_callable($condition)) {
if (! $condition($request)) {
return false;
}
} elseif (is_string($condition)) {
$instance = new $condition;
if (! $instance instanceof MonitoringCondition) {
continue;
}
if (! $instance->shouldMonitor($request)) {
return false;
}
}
}

return true;
}
}
33 changes: 33 additions & 0 deletions src/Traits/Actionable.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@

namespace Binafy\LaravelUserMonitoring\Traits;

use Binafy\LaravelUserMonitoring\Contracts\MonitoringCondition;
use Binafy\LaravelUserMonitoring\Utills\ActionType;
use Binafy\LaravelUserMonitoring\Utills\Detector;
use Binafy\LaravelUserMonitoring\Utills\UserUtils;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;

trait Actionable
Expand All @@ -20,6 +22,11 @@ protected static function boot(): void
return;
}

// Custom conditions from config
if (! static::shouldMonitor(request())) {
return;
}

if (config('user-monitoring.action_monitoring.on_store', false)) {
static::created(function (mixed $model) {
static::insertActionMonitoring($model, ActionType::ACTION_STORE);
Expand Down Expand Up @@ -94,4 +101,30 @@ private static function getRealIP(): string
? request()->header(config('user-monitoring.real_ip_header'))
: request()->ip();
}

/**
* Determine if monitoring should be performed for the given request and user.
*/
protected static function shouldMonitor(Request $request): bool
{
$config = config('user-monitoring.action_monitoring.conditions', []);

foreach ($config as $condition) {
if (is_callable($condition)) {
if (! $condition($request)) {
return false;
}
} elseif (is_string($condition)) {
$instance = new $condition;
if (! $instance instanceof MonitoringCondition) {
continue;
}
if (! $instance->shouldMonitor($request)) {
return false;
}
}
}

return true;
}
}
33 changes: 33 additions & 0 deletions tests/Feature/ActionMonitoringTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
use Binafy\LaravelUserMonitoring\Utills\ActionType;
use Binafy\LaravelUserMonitoring\Utills\UserUtils;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Tests\SetUp\Models\Product;
use Tests\SetUp\Models\ProductSoftDelete;
Expand Down Expand Up @@ -361,3 +362,35 @@
// DB Assertions
assertDatabaseCount(config('user-monitoring.action_monitoring.table'), 2);
});

test('action is stored when config conditions are true', function () {
config()->set('user-monitoring.action_monitoring.conditions', [
function (Request $request) {
return true;
},
]);

Product::query()->create([
'title' => 'milwad',
'description' => 'WE ARE HELPING TO OPEN-SOURCE WORLD'
]);

// DB Assertions
assertDatabaseCount(config('user-monitoring.action_monitoring.table'), 1);
});

test('action is not stored when config conditions are false', function () {
config()->set('user-monitoring.action_monitoring.conditions', [
function (Request $request) {
return false;
},
]);

Product::query()->create([
'title' => 'milwad',
'description' => 'WE ARE HELPING TO OPEN-SOURCE WORLD'
]);

// DB Assertions
assertDatabaseCount(config('user-monitoring.action_monitoring.table'), 0);
});
29 changes: 29 additions & 0 deletions tests/Feature/VisitMonitoringTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

use Binafy\LaravelUserMonitoring\Models\VisitMonitoring;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Http\Request;
use Tests\SetUp\Models\User;
use function Pest\Laravel\{actingAs, get};
use function Pest\Laravel\{assertDatabaseCount, assertDatabaseHas, assertDatabaseMissing};
Expand Down Expand Up @@ -122,6 +123,34 @@
assertDatabaseCount(config('user-monitoring.visit_monitoring.table'), 1);
});

test('visit monitoring is store when config conditions are true', function () {
config()->set('user-monitoring.visit_monitoring.conditions', [
function (Request $request) {
return true;
},
]);

$response = get('/');
$response->assertContent('milwad');

// DB Assertions
assertDatabaseCount(config('user-monitoring.visit_monitoring.table'), 1);
});

test('visit monitoring is not store when config conditions are false', function () {
config()->set('user-monitoring.visit_monitoring.conditions', [
function (Request $request) {
return false;
},
]);

$response = get('/');
$response->assertContent('milwad');

// DB Assertions
assertDatabaseCount(config('user-monitoring.visit_monitoring.table'), 0);
});

/**
* Create user and return it.
*/
Expand Down