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

Entity History Bundle Laravel Package

bobv/entity-history-bundle

View on GitHub
Deep Wiki
Context7
## Getting Started

### Minimal Setup
1. **Installation**:
   ```bash
   composer require bobvandevijver/entity-history-bundle:^4.3.1

For Laravel (via DoctrineBundle):

composer require fruitcake/laravel-doctrine
  1. Configuration: Add to config/packages/doctrine.yaml (Symfony) or config/doctrine.php (Laravel):

    doctrine:
        orm:
            entity_listeners:
                Bobv\EntityHistoryBundle\Listener\HistoryListener: ~
    
  2. Annotate an Entity:

    use Bobv\EntityHistoryBundle\Annotation\History;
    
    /**
     * @History
     */
    #[ORM\Entity]
    class User
    {
        // ...
    }
    
  3. Run Migrations:

    php bin/console doctrine:migrations:diff
    php bin/console doctrine:migrations:migrate
    

    Note: If using a new database API (e.g., Doctrine DBAL 3.5+), ensure the history table's primary key constraint is created correctly. The fix in v4.3.1 addresses potential issues with this.

  4. First Use Case: Test by creating/updating a User and verify the history table records changes:

    $user = new User();
    $user->setName('John Doe');
    $entityManager->persist($user);
    $entityManager->flush();
    
    // Check history
    $history = $entityManager->getRepository(\Bobv\EntityHistoryBundle\Entity\History::class)
        ->findBy(['entityId' => $user->getId()]);
    

Implementation Patterns

Core Workflows

1. Tracking Entity Changes

  • Automatic: Annotate entities with @History or configure via YAML/XML.
  • Custom Fields: Exclude fields from history by adding @History\Ignore:
    /**
     * @History\Ignore
     */
    private $apiToken;
    

2. Querying History

  • Basic Fetch:
    $history = $entityManager->getRepository(\Bobv\EntityHistoryBundle\Entity\History::class)
        ->findBy(['entityId' => $entity->getId()]);
    
  • Filtered by Field/Revision:
    $history = $entityManager->getRepository(\Bobv\EntityHistoryBundle\Entity\History::class)
        ->createQueryBuilder('h')
        ->where('h.entityId = :id')
        ->andWhere('h.revision = :rev')
        ->setParameter('id', $entity->getId())
        ->setParameter('rev', 1)
        ->getQuery()
        ->getResult();
    

3. Integrating with Laravel

  • Service Provider Setup (Laravel):
    public function register()
    {
        $this->app->register(\Fruitcake\Doctrine\DoctrineServiceProvider::class);
        $this->app->make(\Fruitcake\Doctrine\DoctrineServiceProvider::class)
            ->registerEntityListeners();
    }
    
  • Event Listeners: Extend the bundle’s listener to add metadata (e.g., user ID):
    use Bobv\EntityHistoryBundle\Event\HistoryEvent;
    
    $entityManager->getEventManager()->addEventListener(
        ['preUpdate', 'prePersist'],
        function (HistoryEvent $event) {
            $event->setMetadata(['user_id' => auth()->id()]);
        }
    );
    

4. Soft Deletes

  • Pair with Gedmo/SoftDeleteable:
    use Gedmo\Mapping\Annotation as Gedmo;
    
    /**
     * @Gedmo\SoftDeleteable(fieldName="deletedAt")
     * @History
     */
    class User { ... }
    
    History will capture the deletedAt field change.

5. Bulk Operations

  • Disable history for bulk inserts/updates:
    $entityManager->getConnection()->getConfiguration()->setSQLLogger(null);
    // Bulk operations here
    $entityManager->flush();
    

Advanced Patterns

Custom History Storage

  • Extend the History entity to add columns (e.g., user_id):
    /**
     * @ORM\Entity(repositoryClass="App\Repository\CustomHistoryRepository")
     */
    class CustomHistory extends \Bobv\EntityHistoryBundle\Entity\History
    {
        /**
         * @ORM\Column(type="integer", nullable=true)
         */
        private ?int $userId;
    }
    
    Update the listener to populate userId.

Asynchronous History

  • Use Laravel Queues to defer history writes:
    $entityManager->getEventManager()->addEventListener(
        ['postUpdate', 'postPersist'],
        function (HistoryEvent $event) {
            dispatch(new RecordHistoryJob($event->getEntity(), $event->getMetadata()));
        }
    );
    

Diff Views

  • Create a custom repository method to compare revisions:
    public function getDiff(User $user, int $oldRevision, int $newRevision)
    {
        $oldHistory = $this->findOneBy(['entityId' => $user->getId(), 'revision' => $oldRevision]);
        $newHistory = $this->findOneBy(['entityId' => $user->getId(), 'revision' => $newRevision]);
        return $this->diffEntities($oldHistory->getData(), $newHistory->getData());
    }
    

API Endpoints

  • Expose history via Laravel routes:
    Route::get('/entities/{entity}/history', function (EntityManagerInterface $em, $entity) {
        $history = $em->getRepository(\Bobv\EntityHistoryBundle\Entity\History::class)
            ->findBy(['entityId' => $entity]);
        return response()->json($history);
    });
    

Gotchas and Tips

Pitfalls

  1. Primary Key Constraint Issues (v4.3.1 Fix)

    • Issue: If using a new database API (e.g., Doctrine DBAL 3.5+), the history table's primary key constraint might fail to create.
    • Fix: Update to ^4.3.1 and rerun migrations:
      php bin/console doctrine:migrations:migrate
      
  2. Missing Annotations:

    • Issue: History not recording for an entity.
    • Fix: Ensure @History is applied to the entity class (not just methods/fields).
  3. Direct SQL Bypasses ORM:

    • Issue: Changes made via raw SQL (e.g., DB::update()) won’t trigger history.
    • Fix: Use Doctrine’s EntityManager for all entity operations or implement a database trigger.
  4. Performance Bottlenecks:

    • Issue: History queries are slow due to missing indexes.
    • Fix: Add indexes to history.entity_id, history.changed_at, and history.revision:
      CREATE INDEX idx_history_entity_revision ON history(entity_id, revision);
      
  5. Circular References:

    • Issue: History table grows uncontrollably due to nested entity relationships.
    • Fix: Use @History\Ignore for non-critical fields or limit depth with a custom listener.
  6. Transaction Rollbacks:

    • Issue: History records persist even if the original operation rolls back.
    • Fix: Wrap history recording in the same transaction or use postFlush events.
  7. Doctrine Event Conflicts:

    • Issue: Other listeners override history data.
    • Fix: Set listener priority in config/packages/doctrine.yaml:
      services:
          Bobv\EntityHistoryBundle\Listener\HistoryListener:
              tags:
                  - { name: doctrine.event_subscriber, priority: 255 } # Highest priority
      
  8. Large Binary Data:

    • Issue: History bloats with large fields (e.g., text, json).
    • Fix: Exclude binary fields from history or store hashes instead.

Debugging Tips

  1. Enable Doctrine Logging:

    $entityManager->getConnection()->getConfiguration()->setSQLLogger(new \Doctrine\DBAL\Logging\EchoSQLLogger());
    
    • Check for missing INSERT queries to the history table.
  2. Inspect Events:

    • Dump event payloads to verify data:
      $entityManager->getEventManager()->addEventListener(
          ['preUpdate'],
          function (LifecycleEventArgs $args) {
              dump($args->getEntity(), $args->getObject()->getChanges());
          }
      );
      
  3. Check History Table:

    • Verify records exist:
      SELECT * FROM history WHERE entity_id = 123 ORDER BY revision DESC;
      
  4. Validate Annotations:

    • Use doctrine:schema:validate to ensure annotations are parsed:
      php bin/console
      
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.
terminal42/code-quality-tools
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