a2zwebltd/auditable-relations
Automatic auditing for Eloquent relationship changes (attach, detach, sync) using Laravel Auditing
2,664
4
| Install | |
|---|---|
composer require a2zwebltd/auditable-relations |
|
| Latest Version: | v1.1.0 |
| PHP: | ^8.2 |
| License: | MIT |
| Last Updated: | Mar 28, 2026 |
| Links: | GitHub · Packagist |
Maintainer: dawid-makowski
Auditable Relations
Automatic auditing for Eloquent relationship changes (attach, detach, sync) using Laravel Auditing.
Features
- 🔍 Automatic Tracking: Captures before/after state of relationship changes
- 📝 Detailed Logs: Stores complete related model data, not just IDs
- 🎯 Event-Based: Uses
owen-it/laravel-auditingfor consistent audit logs - ⚡ Zero Configuration: Works out of the box after trait inclusion
- 🔧 Flexible: Supports BelongsToMany and MorphToMany relationships
- 📦 Lightweight: Minimal overhead, maximum value
Installation
composer require a2zwebltd/auditable-relations
Requirements
- PHP 8.2+
- Laravel 10, 11, 12, or 13
- owen-it/laravel-auditing 13/14
Quick Start
1. Implement Auditable on Your Model
use Illuminate\Database\Eloquent\Model;
use OwenIt\Auditing\Auditable as AuditableTrait;
use OwenIt\Auditing\Contracts\Auditable;
class Post extends Model implements Auditable
{
use AuditableTrait;
}
2. Add the Trait and Wrap Your Relationships
use A2ZWeb\AuditableRelations\Traits\AuditsRelationships;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
class Post extends Model implements Auditable
{
use AuditableTrait;
use AuditsRelationships;
public function tags(): BelongsToMany
{
return $this->auditableRelation(
$this->belongsToMany(Tag::class)
);
}
}
That's it! Now all changes to the tags relationship will be automatically audited.
Usage Examples
Basic Usage
$post = Post::find(1);
// These operations are automatically audited
$post->tags()->attach([1, 2, 3]);
$post->tags()->detach([2]);
$post->tags()->sync([1, 3, 4]);
What Gets Logged
Each operation creates an audit log entry like:
[
'event' => 'synced', // or 'attached', 'detached'
'auditable_type' => 'App\Models\Post',
'auditable_id' => 1,
'old_values' => [
'tags' => [
['id' => 1, 'name' => 'Laravel', 'created_at' => '...'],
['id' => 2, 'name' => 'PHP', 'created_at' => '...'],
]
],
'new_values' => [
'tags' => [
['id' => 1, 'name' => 'Laravel', 'created_at' => '...'],
['id' => 3, 'name' => 'Vue', 'created_at' => '...'],
['id' => 4, 'name' => 'Tailwind', 'created_at' => '...'],
]
],
'user_id' => 123,
'user_type' => 'App\Models\User',
]
Multiple Relationships
You can audit multiple relationships on the same model:
class Post extends Model implements Auditable
{
use AuditableTrait;
use AuditsRelationships;
public function tags(): BelongsToMany
{
return $this->auditableRelation(
$this->belongsToMany(Tag::class)
);
}
public function categories(): BelongsToMany
{
return $this->auditableRelation(
$this->belongsToMany(Category::class)
);
}
public function attachments(): MorphToMany
{
return $this->auditableRelation(
$this->morphToMany(File::class, 'attachable')
);
}
}
Polymorphic Relationships
Works seamlessly with polymorphic relationships:
class Post extends Model implements Auditable
{
use AuditableTrait;
use AuditsRelationships;
public function images(): MorphToMany
{
return $this->auditableRelation(
$this->morphToMany(Image::class, 'imageable')
);
}
}
Supported Relationships
- ✅
BelongsToMany - ✅
MorphToMany - ⏳ Other relationship types (planned)
Configuration
The package respects Laravel Auditing's global configuration:
// config/audit.php
return [
'enabled' => true, // Disable to stop all auditing
'console' => false, // Audit console commands
// ... other audit config
];
Advanced Usage
Conditional Auditing
You can control auditing at runtime:
// Temporarily disable auditing
config(['audit.enabled' => false]);
$post->tags()->sync([1, 2, 3]); // Not audited
config(['audit.enabled' => true]);
Custom Event Names
The package uses standard event names:
attached- When models are attacheddetached- When models are detachedsynced- When models are synced
Accessing Audit Logs
use OwenIt\Auditing\Models\Audit;
// Get all audits for a model
$audits = Audit::where('auditable_type', Post::class)
->where('auditable_id', 1)
->get();
// Get relationship change audits
$relationshipAudits = Audit::where('auditable_type', Post::class)
->whereIn('event', ['attached', 'detached', 'synced'])
->get();
How It Works
Architecture
- Trait Application:
AuditsRelationshipstrait wraps relationship definitions - Relationship Proxy: Creates auditable versions of
BelongsToManyandMorphToMany - Operation Interception: Intercepts
attach(),detach(), andsync()calls - State Capture: Records relationship state before and after the operation
- Event Dispatch: Fires
AuditCustomevent with the captured data - Audit Creation: Laravel Auditing processes the event and creates the audit log
Performance
- Minimal overhead: Only one additional query per operation (to capture current state)
- Efficient: Uses existing Laravel Auditing infrastructure
- Asynchronous-ready: Compatible with queued audit processing
Comparison with Alternatives
Unlike other solutions:
- ✅ Complete Data: Stores full related model data, not just IDs
- ✅ Native Integration: Uses Laravel Auditing's standard audit model
- ✅ Zero Config: No additional tables or setup required
- ✅ Framework-Native: Uses Laravel's event system
Troubleshooting
Audits Not Appearing
- Verify auditing is enabled:
config('audit.enabled'); // Should be true
- Check model implements
Auditable:
class Post extends Model implements \OwenIt\Auditing\Contracts\Auditable
{
use \OwenIt\Auditing\Auditable;
}
- Ensure relationship is wrapped:
public function tags(): BelongsToMany
{
return $this->auditableRelation( // Don't forget this!
$this->belongsToMany(Tag::class)
);
}
Console Commands Not Audited
Enable console auditing in config/audit.php:
'console' => true,
Testing
composer test
Contributing
Contributions are welcome! Please see CONTRIBUTING.md for details.
Security
If you discover a security vulnerability, please email contact@a2zweb.co.
Credits
License
The MIT License (MIT). Please see License File for more information.