Enable Contrib Support Run in your project root:
composer config extra.symfony.allow-contrib true
composer require codyas/audit-bundle
Register the Bundle
Ensure Codyas\Audit\AuditBundle::class is added to config/bundles.php:
return [
// ...
Codyas\Audit\AuditBundle::class => ['all' => true],
];
Configure a Master Entity
Annotate a Doctrine entity (e.g., User) with @Audit\Master to track its changes:
use Codyas\Audit\Annotation\Master;
/**
* @Master()
*/
#[ORM\Entity]
class User { ... }
Trigger an Audit
Use the AuditManager service to manually trigger an audit (or rely on Doctrine lifecycle events):
$auditManager = $this->container->get('codyas_audit.manager');
$auditManager->audit($user);
View Revisions
Fetch revisions for an entity via the AuditRepository:
$revisions = $this->container->get('codyas_audit.repository')->findRevisions($user);
Annotate Master Entities
Mark entities with @Master to enable auditing. Example:
/**
* @Master(
* ignoreFields = {"password", "tempToken"},
* serializeGroups = {"api"}
* )
*/
class User { ... }
Customize Serialization Use Symfony Serializer groups or custom serializers for complex fields:
# config/packages/codyas_audit.yaml
codyas_audit:
serializers:
- 'App\Serializer\UserSerializer'
Automatic vs. Manual Auditing
preUpdate, prePersist) trigger audits.$auditManager->audit($entity) in business logic (e.g., after bulk updates).Querying Revisions Filter revisions by date, user, or changes:
$revisions = $auditRepository->findRevisions($user, [
'limit' => 5,
'orderBy' => ['createdAt' => 'DESC'],
]);
Integrate with API Responses Attach revisions to API responses for transparency:
return $this->json([
'data' => $user,
'revisions' => $auditRepository->findRevisions($user),
]);
Symfony Events: Listen to kernel.response to batch audit operations post-request:
$eventDispatcher->addListener(KernelEvents::RESPONSE, function (RequestEvent $event) {
$auditManager = $this->container->get('codyas_audit.manager');
$auditManager->flushPendingAudits();
});
Doctrine Events: Override default behavior by subscribing to codyas_audit.pre_audit:
$eventDispatcher->addListener('codyas_audit.pre_audit', function (AuditEvent $event) {
if (!$event->getEntity()->isActive()) {
$event->stopPropagation();
}
});
Testing: Mock AuditManager to avoid DB writes in unit tests:
$this->mock(AuditManager::class)
->shouldReceive('audit')
->once();
Performance Overhead
ignoreFields or use serializeGroups.codyas_audit.compression_enabled: true) for text-heavy revisions.Circular References
User ↔ Address) cause serialization errors.NormalizerInterface or use @Groups to break cycles:
#[Groups(['api'])]
#[Assert\NotBlank]
private string $street;
Race Conditions
flushPendingAudits() in a post-request event to batch writes.Configuration Overrides
config/packages/codyas_audit.yaml.imports:
- { resource: "@CodyasAuditBundle/Resources/config/config.yaml" }
codyas_audit:
<<: *default_config
custom_setting: value
Enable Logging
Set debug: true in config to log audit events:
codyas_audit:
debug: true
Check Event Propagation
Use stopPropagation() in listeners to debug skipped audits:
$event->stopPropagation(); // Prevents audit for this entity
Verify Doctrine Events
Ensure codyas_audit.subscriber is registered in doctrine.event_subscribers:
doctrine:
orm:
event_subscribers:
- Codyas\Audit\Doctrine\AuditSubscriber
Custom Audit Storage
Override the default AuditRepository to store revisions in a NoSQL database:
class CustomAuditRepository implements AuditRepositoryInterface { ... }
Register it as a service:
services:
Codyas\Audit\AuditRepositoryInterface: '@App\Repository\CustomAuditRepository'
Dynamic Master Entities
Skip annotations by implementing MasterEntityResolverInterface:
class DynamicMasterResolver implements MasterEntityResolverInterface {
public function isMaster(object $entity): bool {
return $entity instanceof User && $entity->isAdmin();
}
}
Webhook Notifications Dispatch events after audits to trigger external actions (e.g., Slack alerts):
$eventDispatcher->addListener('codyas_audit.post_audit', function (AuditEvent $event) {
$this->slackService->sendAlert($event->getChanges());
});
How can I help you explore Laravel packages today?