adrianglazer/doctrine-footprint-extension
Doctrine extension to auto-track entity create/update/delete with timestamps and usernames. Adds created_at/by, updated_at/by, deleted_at/by via a single trait + event subscriber, plus a Doctrine filter for soft deletes.
Installation
composer require adrianglazer/doctrine-footprint-extension
(Note: Requires Doctrine ORM integration in Laravel, typically via doctrine/orm or beberlei/doctrineextensions.)
Configure Doctrine
Add to config/packages/doctrine.yaml:
doctrine:
orm:
filters:
footprint:
class: Glazer\DoctrineFootprintExtension\Filter\FootprintFilter
enabled: true
Register Listener
Add to config/services.yaml:
Glazer\DoctrineFootprintExtension\Listener\FootprintListener:
class: Glazer\DoctrineFootprintExtension\Listener\FootprintListener
autowire: true
tags:
- { name: doctrine.event_subscriber }
arguments: ['@security.token_storage']
Use the Trait
Extend your entity with the trait (e.g., App\Entity\Post):
use Glazer\DoctrineFootprintExtension\Traits\FootprintTrait;
class Post
{
use FootprintTrait;
// ...
}
First Use Case After setup, Doctrine will auto-populate:
created_at, created_by on persist()updated_at, updated_by on flush()deleted_at, deleted_by on remove() (if soft deletes are enabled).Entity Design
created_at, updated_at, deleted_at (if soft deletes are needed).created_by, updated_by, deleted_by as string (store usernames) or integer (store user IDs)./**
* @ORM\Column(type="datetime")
*/
protected $createdAt;
/**
* @ORM\Column(type="string", length=255)
*/
protected $createdBy;
Soft Deletes
deletedAt/deletedBy.remove() if needed:
public function remove()
{
$this->deletedAt = new \DateTime();
$this->deletedBy = $this->getCurrentUser();
}
User Resolution
TokenStorage to fetch the current user.Auth facade or resolve the user manually in the listener:
$user = auth()->user(); // Replace TokenStorage logic if needed.
Bulk Operations
EntityManager::flush()) will update all tracked fields.$em->getFilters()->disable('footprint');
// Bulk operations...
$em->getFilters()->enable('footprint');
Custom Logic
use Glazer\DoctrineFootprintExtension\Traits\FootprintTrait;
class Post
{
use FootprintTrait;
public function preUpdate()
{
if (!$this->isDirty('title')) {
$this->updatedBy = null; // Skip update if only non-title fields change.
}
}
}
Symfony Dependency
TokenStorage (for user resolution). In Laravel, you may need to:
TokenStorage or wrap Laravel’s Auth in a Symfony-compatible service.TokenStorage argument with a custom service that resolves Laravel’s auth()->user().Soft Deletes Conflicts
Gedmo\SoftDeleteable, disable it or merge logic to avoid duplicate deleted_at fields.Gedmo\SoftDeleteable and rely solely on this package’s trait.Filter Not Triggering
doctrine.yaml and the listener is autowired in services.yaml.$em->getEventManager()->addEventListener(
array('prePersist', 'preUpdate', 'preRemove'),
function ($event) { dump($event->getEntity()); }
);
User Resolution Failures
created_by/updated_by is null, the listener couldn’t resolve the user.getCurrentUser() method or ensure TokenStorage has a user.Timezone Issues
$this->createdAt = new \DateTime('now', new \DateTimeZone('UTC'));
Laravel-Specific Setup
$this->app->bind('Glazer\DoctrineFootprintExtension\Listener\FootprintListener',
function ($app) {
return new \Glazer\DoctrineFootprintExtension\Listener\FootprintListener(
$app['auth']->guard()->user() // Simplified for Laravel.
);
}
);
Testing
$listener = $this->createMock(FootprintListener::class);
$listener->method('getCurrentUser')->willReturn($user);
$em->getEventManager()->addEventSubscriber($listener);
Partial Updates
if (!$entity->isDirty('criticalField')) {
$entity->updatedAt = null;
}
Database Indexes
created_by, updated_by for query performance:
# config/packages/doctrine.yaml
orm:
mappings:
App:
type: annotation
dir: "%kernel.project_dir%/src/Entity"
prefix: "App\Entity"
use_simple_annotation_reader: false
filters:
footprint:
class: Glazer\DoctrineFootprintExtension\Filter\FootprintFilter
enabled: true
Legacy Systems
$entities = $em->getRepository(Post::class)->findAll();
foreach ($entities as $entity) {
$entity->createdAt = new \DateTime('2020-01-01');
$entity->createdBy = 'admin';
$em->flush();
}
Extension Points
setCurrentUser() to customize user resolution:
protected function setCurrentUser($user)
{
$this->createdBy = $user->getId(); // Store ID instead of username.
}
How can I help you explore Laravel packages today?