Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Doctrine Footprint Extension Laravel Package

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.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup for Laravel + Doctrine ORM

  1. Installation

    composer require adrianglazer/doctrine-footprint-extension
    

    (Note: Requires Doctrine ORM integration in Laravel, typically via doctrine/orm or beberlei/doctrineextensions.)

  2. Configure Doctrine Add to config/packages/doctrine.yaml:

    doctrine:
        orm:
            filters:
                footprint:
                    class: Glazer\DoctrineFootprintExtension\Filter\FootprintFilter
                    enabled: true
    
  3. 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']
    
  4. Use the Trait Extend your entity with the trait (e.g., App\Entity\Post):

    use Glazer\DoctrineFootprintExtension\Traits\FootprintTrait;
    
    class Post
    {
        use FootprintTrait;
        // ...
    }
    
  5. 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).

Implementation Patterns

Workflow Integration

  1. Entity Design

    • Standard Fields: Always include created_at, updated_at, deleted_at (if soft deletes are needed).
    • User Fields: Use created_by, updated_by, deleted_by as string (store usernames) or integer (store user IDs).
    • Example:
      /**
       * @ORM\Column(type="datetime")
       */
      protected $createdAt;
      
      /**
       * @ORM\Column(type="string", length=255)
       */
      protected $createdBy;
      
  2. Soft Deletes

    • Enable via the trait (default) or disable by creating a custom trait without deletedAt/deletedBy.
    • Override soft-delete logic in remove() if needed:
      public function remove()
      {
          $this->deletedAt = new \DateTime();
          $this->deletedBy = $this->getCurrentUser();
      }
      
  3. User Resolution

    • The listener uses Symfony’s TokenStorage to fetch the current user.
    • Laravel Note: If using Laravel’s auth, inject Auth facade or resolve the user manually in the listener:
      $user = auth()->user(); // Replace TokenStorage logic if needed.
      
  4. Bulk Operations

    • The listener triggers per-entity, so bulk operations (e.g., EntityManager::flush()) will update all tracked fields.
    • Performance Tip: Disable footprints for bulk updates via a temporary filter:
      $em->getFilters()->disable('footprint');
      // Bulk operations...
      $em->getFilters()->enable('footprint');
      
  5. Custom Logic

    • Extend the trait to add pre/post hooks:
      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.
              }
          }
      }
      

Gotchas and Tips

Pitfalls

  1. Symfony Dependency

    • The package expects Symfony’s TokenStorage (for user resolution). In Laravel, you may need to:
      • Mock TokenStorage or wrap Laravel’s Auth in a Symfony-compatible service.
      • Workaround: Replace the listener’s TokenStorage argument with a custom service that resolves Laravel’s auth()->user().
  2. Soft Deletes Conflicts

    • If using Gedmo\SoftDeleteable, disable it or merge logic to avoid duplicate deleted_at fields.
    • Fix: Remove Gedmo\SoftDeleteable and rely solely on this package’s trait.
  3. Filter Not Triggering

    • Ensure the filter is enabled in doctrine.yaml and the listener is autowired in services.yaml.
    • Debug: Check Doctrine events with:
      $em->getEventManager()->addEventListener(
          array('prePersist', 'preUpdate', 'preRemove'),
          function ($event) { dump($event->getEntity()); }
      );
      
  4. User Resolution Failures

    • If created_by/updated_by is null, the listener couldn’t resolve the user.
    • Debug: Override the listener’s getCurrentUser() method or ensure TokenStorage has a user.
  5. Timezone Issues

    • Timestamps may use the server’s timezone. Force UTC in the trait:
      $this->createdAt = new \DateTime('now', new \DateTimeZone('UTC'));
      

Tips

  1. Laravel-Specific Setup

    • Register the listener in Laravel’s service provider:
      $this->app->bind('Glazer\DoctrineFootprintExtension\Listener\FootprintListener',
          function ($app) {
              return new \Glazer\DoctrineFootprintExtension\Listener\FootprintListener(
                  $app['auth']->guard()->user() // Simplified for Laravel.
              );
          }
      );
      
  2. Testing

    • Mock the listener in tests:
      $listener = $this->createMock(FootprintListener::class);
      $listener->method('getCurrentUser')->willReturn($user);
      $em->getEventManager()->addEventSubscriber($listener);
      
  3. Partial Updates

    • Skip footprints for specific updates by checking dirty fields:
      if (!$entity->isDirty('criticalField')) {
          $entity->updatedAt = null;
      }
      
  4. Database Indexes

    • Add indexes to 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
      
  5. Legacy Systems

    • For existing databases, backfill timestamps/user fields via a migration:
      $entities = $em->getRepository(Post::class)->findAll();
      foreach ($entities as $entity) {
          $entity->createdAt = new \DateTime('2020-01-01');
          $entity->createdBy = 'admin';
          $em->flush();
      }
      
  6. Extension Points

    • Override the trait’s setCurrentUser() to customize user resolution:
      protected function setCurrentUser($user)
      {
          $this->createdBy = $user->getId(); // Store ID instead of username.
      }
      
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
codifyo/ts-generator-bundle
andydefer/laravel-cluster
testo/fiber
mintobit/jobqueue
a4sex/maintenance-bundle
a4sex/entity-date-update
a4sex/client-identifier
a4sex/base-utilites
a4sex/key-value-storage
a4sex/micro-status
chilldev/dependency-injection-extra
datinglibre/datinglibre-app-api
biberltd/corebundle
bricre/symfony-bundle-test
biberltd/logbundle
dominium/http-adapter-bundle
dominium/google-analytics
a4sex/auto-clean-entity
christhompsontldr/laravel-inky
spatie/mailcoach-vapor