Installation
composer require damienharper/auditor-bundle
Add to config/bundles.php:
return [
// ...
DamienHarper\AuditorBundle\AuditorBundle::class => ['all' => true],
];
Basic Configuration
Create config/packages/dh_auditor.yaml:
dh_auditor:
audit_log:
table_name: 'audit_log'
connection: 'default'
audit_log_entry:
table_name: 'audit_log_entry'
connection: 'default'
Enable Auditing for an Entity
Use the Auditable trait and annotations:
use DH\AuditorBundle\Annotation as ORMAudit;
#[ORM\Entity]
#[ORMAudit\Auditable]
class User
{
// ...
}
First Audit Log
Run migrations (php bin/console doctrine:migrations:diff + php bin/console doctrine:migrations:migrate) and interact with your entity. Check the audit_log table for records.
View Audit Logs
php bin/console dh:auditor:view
Access the built-in viewer at /auditor (enabled by default).
Filter by Entity/Action Use the viewer’s UI or query directly:
$auditLogRepository = $entityManager->getRepository(AuditLog::class);
$logs = $auditLogRepository->findBy(['entityClass' => User::class]);
Automatic Tracking
The bundle listens to Doctrine events (prePersist, preUpdate, preRemove) and logs changes to audit_log/audit_log_entry tables.
#[ORMAudit\AuditField] to track specific properties:
#[ORMAudit\AuditField]
private ?string $email;
Bulk Operations
For batch updates (e.g., EntityManager::createQueryBuilder()->update()), manually trigger audits:
$auditor = $this->container->get('auditor');
$auditor->audit($entity, 'update');
use DH\Auditor\Provider\ProviderInterface;
class ApiAuditProvider implements ProviderInterface
{
public function audit($entity, $action, array $changes = []): void
{
// Custom logic (e.g., log to an external service)
}
}
Register in services.yaml:
services:
DH\Auditor\Provider\ProviderInterface: '@api_audit_provider'
DoctrineProvider with kernel.reset (since v7.2.0) to handle Messenger workers correctly. No config needed for basic use.$auditLogRepository = $entityManager->getRepository(AuditLog::class);
$entries = $auditLogRepository->findByEntity(User::class, 1); // User with ID 1
audit_log_entry for detailed changes:
$qb = $entityManager->createQueryBuilder();
$qb->select('al', 'ale')
->from(AuditLog::class, 'al')
->leftJoin('al.entries', 'ale')
->where('al.entityClass = :class')
->setParameter('class', User::class);
Service Registration
In config/services.php, bind the auditor:
'auditor' => \Illuminate\Support\Facades\App::make('auditor'),
Doctrine Event Listeners Replace Symfony’s event system with Laravel’s:
use Doctrine\ORM\Event\LifecycleEventArgs;
use DH\AuditorBundle\Annotation\Auditable;
class AuditableListener
{
public function preUpdate(LifecycleEventArgs $args): void
{
$entity = $args->getObject();
if ($entity instanceof Auditable) {
$auditor = app('auditor');
$auditor->audit($entity, 'update');
}
}
}
Register in AppServiceProvider@boot():
$em->getEventManager()->addEventListener(
\Doctrine\ORM\Events::preUpdate,
new AuditableListener()
);
Migrations Use Laravel Migrations to create audit tables:
Schema::create('audit_log', function (Blueprint $table) {
$table->id();
$table->string('entity_class');
$table->json('metadata')->nullable();
$table->timestamps();
});
Route Configuration
Add the auditor routes in routes/web.php:
Route::prefix('auditor')->group(function () {
\Illuminate\Support\Facades\Route::get('/', [\DamienHarper\AuditorBundle\Controller\AuditController::class, 'index']);
// Add other routes as needed
});
Schema Updates
php bin/console doctrine:schema:update --force may drop audit tables if not configured properly.# config/packages/doctrine.yaml
doctrine:
orm:
schema_filter: '~^(?!audit_).*~'
Circular References
User ↔ Post) may cause infinite loops.#[ORMAudit\AuditIgnore] on problematic properties or limit depth:
dh_auditor:
audit_log_entry:
max_depth: 3
Performance
#[ORMAudit\Auditable(disabled: true)]
class LogEntry {}
Symfony Messenger Workers
kernel.reset is enabled (auto-handled since v7.2.0). For older versions, manually tag the service:
services:
DH\Auditor\Provider\Doctrine\DoctrineProvider:
tags: ['kernel.reset']
Enable Debug Logging
Add to config/packages/monolog.yaml:
handlers:
dh_auditor:
type: stream
path: "%kernel.logs_dir%/dh_auditor.log"
level: debug
channels: ["auditor"]
Then configure the bundle:
dh_auditor:
debug: true
Check Audit Logs
doctrine:
dbal:
logging: true
profiling: true
$auditor = app('auditor');
$auditor->audit($entity, 'update', ['field' => ['old' => 'old', 'new' => 'new']]);
Common Errors
table_name in config.#[ORMAudit\Auditable].audit_log_entry table exists and is linked correctly.Partial Auditing
Audit only specific actions (e.g., update but not delete):
#[ORMAudit\Auditable(actions: ['update'])]
class Product {}
Custom Metadata Attach extra data to audit logs:
$auditor->audit($entity, 'update', [], [
'source' => 'admin_panel',
'user_id' => auth()->id(),
]);
Audit Viewer Customization
templates/auditor/ from the bundle to your project and modifyHow can I help you explore Laravel packages today?