zairakai / laravel-activity
Enhanced activity logging helpers for Spatie Laravel Activity Log with pivot table support
Installs: 0
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Forks: 0
pkg:composer/zairakai/laravel-activity
Requires
- php: ^8.3
- illuminate/contracts: ^11.0 || ^12.0
- illuminate/database: ^11.0 || ^12.0
- illuminate/support: ^11.0 || ^12.0
- spatie/laravel-activitylog: ^4.8
Requires (Dev)
- driftingly/rector-laravel: ^2.1
- ergebnis/composer-normalize: ^2.49
- larastan/larastan: ^3.9
- laravel/pint: ^1.27
- nunomaduro/phpinsights: ^2.13
- orchestra/testbench: ^9.0 || ^10.0
- phpmetrics/phpmetrics: ^2.9
- phpstan/phpstan: ^2.1
- phpunit/phpunit: ^10.0 || ^11.0
- rector/rector: ^2.3
- zairakai/laravel-dev-tools: ^3.0
Suggests
- ergebnis/composer-normalize: Automated composer.json normalization
README
Zero-friction pivot activity logging for Spatie Laravel Activity Log. Track attach/detach/sync operations on many-to-many relationships with a fluent API and automatic before/after state snapshots.
$user->roles()
->activity()
->by(auth()->user())
->withMessage('Assigned admin roles')
->sync([1, 2, 3]);
// Activity logged with complete before/after state
What it does
Extends BelongsToMany with an ->activity() method that wraps every pivot operation and logs the change to Spatie's activity log automatically.
attach,detach,sync,syncWithoutDetaching,toggle,updateExistingPivot— all covered- Only logs actual changes — no-ops are silently skipped
- Records a complete before/after diff in the activity
properties - Fluent causer attribution and custom messages
- Optional
TraceActivitiestrait to query activities on any model
Requirements
- PHP 8.3 or 8.4
- Laravel 11 or 12
- spatie/laravel-activitylog ^4.8
Installation
composer require zairakai/laravel-activity
Spatie's activity log must be installed and migrated first:
composer require spatie/laravel-activitylog
php artisan vendor:publish --provider="Spatie\Activitylog\ActivitylogServiceProvider" --tag="activitylog-migrations"
php artisan migrate
Quick Start
Pivot logging
Call ->activity() on any BelongsToMany relationship:
// Attach
$user->roles()->activity()->attach([1, 2, 3]);
// Detach
$user->roles()->activity()->detach([2]);
// Sync (replaces the full pivot set)
$user->roles()->activity()->sync([1, 5, 7]);
// Sync without detaching (additive only)
$user->roles()->activity()->syncWithoutDetaching([4, 5]);
// Toggle (attach missing, detach existing)
$user->roles()->activity()->toggle([1, 2, 3]);
// Update pivot columns
$user->roles()->activity()->updateExistingPivot($role, ['granted_at' => now()]);
Query activities
Add the TraceActivities trait to any model:
use Zairakai\LaravelActivity\Traits\TraceActivities;
class User extends Model
{
use TraceActivities;
}
Then query:
// Activities caused BY this user
$user->activities()->by()->get();
// Activities performed ON this user
$user->activities()->on()->get();
// Both directions
$user->activities()->all()->get();
API Reference
Pivot methods
| Method | Description |
|---|---|
activity() | Enter fluent logging mode on a BelongsToMany |
attach($ids, $attrs) | Attach IDs not already present |
detach($ids) | Detach specified IDs (or all if null) |
sync($ids) | Replace pivot set, logs changes only |
syncWithoutDetaching($ids) | Additive sync, logs new additions only |
toggle($ids) | Attach missing, detach existing |
updateExistingPivot($id, $attrs) | Update extra pivot columns |
by($model) | Set causer (defaults to Auth::user() if called with no argument) |
byAnonymous() | Mark as anonymous — no causer |
withMessage($text) | Override the auto-generated log message |
Activity properties
Every logged activity stores these properties:
{
"operation": "attached|detached|synced|synced_without_detaching|toggled|updated_pivot",
"relation": "roles",
"attributes": [1, 2, 3],
"old": [1, 2]
}
For updateExistingPivot, attributes and old contain pivot column maps keyed by model ID:
{
"operation": "updated_pivot",
"relation": "roles",
"attributes": { "5": { "granted_at": "2024-01-15" } },
"old": { "5": { "granted_at": "2023-06-01" } }
}
TraceActivities query methods
| Method | Returns |
|---|---|
activities()->by() | Activities where this model is the causer |
activities()->on() | Activities where this model is the subject |
activities()->all() | Union of both |
All return an Eloquent Builder<Activity> — chain any query method on top.
Causer resolution
Priority order (highest first):
->byAnonymous()— no causer recorded->by($model)— explicit model passed as argument->by()with no argument — resolvesAuth::user()- No
by()call at all — falls back to the parent model itself
withMessage() is reset automatically after each logged operation. The causer set via by() persists for the lifetime of the fluent instance.
Advanced usage
Reuse the same instance across operations
$pivot = $user->roles()
->activity()
->by(auth()->user())
->withMessage('Role reorganization');
$pivot->detach([5, 6]);
$pivot->attach([1, 2]);
withMessageapplies to the next operation only and is cleared after logging. The causer set viaby()persists across all operations on the same instance.
Models and Collections as IDs
$user->roles()->activity()->attach($adminRole);
$user->roles()->activity()->sync(Role::whereIn('id', [1, 2, 3])->get());
Filtering activity queries
$user->activities()
->on()
->where('properties->operation', 'attached')
->where('properties->relation', 'roles')
->latest()
->paginate(20);
Internal classes
Not part of the public API — documented for contributors:
| Class | Role |
|---|---|
PivotChangeSet | Immutable value object — computes added/removed IDs from before/after arrays |
PivotIdNormalizer | Normalizes int, string, Model, array, or Collection to array<int\|string> |
PivotSyncSummary | Formats a sync() result into a human-readable string |
Running tests and quality checks
make quality # all quality checks (pint + phpstan + rector + insights + markdownlint)
make test # full test suite
make test-coverage # tests with coverage report
Or via Composer scripts directly:
composer test # full test suite
composer test:unit # unit suite only
composer test:coverage # with coverage report
composer cs # code style check
composer analyse # static analysis
composer quality # all quality checks
Documentation
- Contributing — Commit format, branch workflow, MR checklist
- Security — Vulnerability reporting and security policy
- Code of Conduct
Getting Help
Built with ❤️ by the Zairakai team for Laravel developers