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

Historique Bundle Laravel Package

atournayre/historique-bundle

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Install the Bundle

    composer require atournayre/historique-bundle
    
  2. Register the Bundle Add to config/bundles.php (Symfony 5+):

    return [
        // ...
        Atournayre\Bundle\HistoriqueBundle\HistoriqueBundle::class => ['all' => true],
    ];
    
  3. Configure Doctrine and Bundle Update config/packages/doctrine.yaml:

    doctrine:
        orm:
            resolve_target_entities:
                Atournayre\Bundle\HistoriqueBundle\Interfaces\History: App\Entity\History
                Symfony\Component\Security\Core\User\UserInterface: App\Entity\User
    

    Update config/packages/atournayre_historique.yaml:

    atournayre_historique:
        history_class: App\Entity\History
    
  4. Create the History Entity

    // src/Entity/History.php
    namespace App\Entity;
    
    use Atournayre\Bundle\HistoriqueBundle\Entity\History as BaseHistory;
    use Atournayre\Bundle\HistoriqueBundle\Interfaces\History;
    use Doctrine\ORM\Mapping as ORM;
    
    #[ORM\Entity]
    class History extends BaseHistory implements History
    {
        // No need to extend further
    }
    
  5. Enable History for an Entity

    // src/Entity/YourEntity.php
    use Atournayre\Bundle\HistoriqueBundle\Traits\HistorycableTrait;
    use Atournayre\Bundle\HistoriqueBundle\Traits\HistorycableInterface;
    
    class YourEntity implements HistorycableInterface
    {
        use HistorycableTrait;
    }
    

First Use Case

Log changes to a Product entity:

// src/Factory/ProductHistoryFactory.php
namespace App\Factory;

use App\Entity\Product;
use Atournayre\Bundle\HistoriqueBundle\DTO\HistoryDTO;
use Atournayre\Bundle\HistoriqueBundle\Factory\AbstractFactory;
use Atournayre\Bundle\HistoriqueBundle\Interfaces\History;

class ProductHistoryFactory extends AbstractFactory
{
    public function create(array $changeSet): ?History
    {
        $this->name($changeSet);
        $this->price($changeSet);
        return parent::createHistory();
    }

    private function name(array $changeSet): void
    {
        $this->changeSet->set('name', HistoryDTO::createFromChangeSet(
            'Product Name',
            $changeSet['name'] ?? null,
            fn(Product $product) => $product->getName()
        ));
    }

    private function price(array $changeSet): void
    {
        $this->changeSet->set('price', HistoryDTO::createFromChangeSet(
            'Product Price',
            $changeSet['price'] ?? null,
            fn(Product $product) => $product->getPrice()
        ));
    }
}

Update config/packages/atournayre_historique.yaml:

atournayre_historique:
    mappings:
        App\Entity\Product: App\Factory\ProductHistoryFactory

Implementation Patterns

Workflow: Tracking Entity Changes

  1. Implement HistorycableInterface Add the trait to any entity you want to track:

    use Atournayre\Bundle\HistoriqueBundle\Traits\HistorycableTrait;
    
    class User implements HistorycableInterface
    {
        use HistorycableTrait;
    }
    
  2. Create a Factory Define how changes are logged for each field:

    class UserHistoryFactory extends AbstractFactory
    {
        public function create(array $changeSet): ?History
        {
            $this->email($changeSet);
            $this->role($changeSet);
            return parent::createHistory();
        }
    
        private function email(array $changeSet): void
        {
            $this->changeSet->set('email', HistoryDTO::createFromChangeSet(
                'User Email',
                $changeSet['email'] ?? null,
                fn(User $user) => $user->getEmail()
            ));
        }
    }
    
  3. Map the Factory

    atournayre_historique:
        mappings:
            App\Entity\User: App\Factory\UserHistoryFactory
    
  4. Retrieve History

    $user = $entityManager->getRepository(User::class)->find(1);
    $history = $user->getEntityChangeSet(); // Collection of History entities
    $historyArray = $user->getEntityChangeSetAsArray(); // Array of changes
    

Integration Tips

  • Symfony Forms: Use HistoryDTO to pre-populate forms with previous values.
  • APIs: Expose history via a custom API endpoint:
    #[Route('/entities/{id}/history', name: 'get_entity_history')]
    public function getHistory(EntityManagerInterface $em, int $id): JsonResponse
    {
        $entity = $em->getRepository(YourEntity::class)->find($id);
        return new JsonResponse($entity->getEntityChangeSetAsArray());
    }
    
  • Doctrine Events: Extend functionality with onFlush events:
    $eventManager->addEventListener(
        [OnFlushEvent::class],
        [$this, 'logAdditionalData']
    );
    

Gotchas and Tips

Pitfalls

  1. Empty Change Sets

    • If $changeSet is empty (e.g., no changes), the factory must return null or handle it gracefully:
      public function create(array $changeSet): ?History
      {
          if (empty($changeSet)) {
              return null;
          }
          // ...
      }
      
  2. Circular References

    • Avoid circular references in HistoryDTO (e.g., logging a User that references the same User entity). Use HistoryDTOFactory::createFromChangeSet() with care.
  3. Performance

    • Logging every field change can bloat your database. Selectively log critical fields (e.g., price, status).
    • Use Criteria to paginate history:
      $criteria = Criteria::create()
          ->orderBy(['createdAt' => 'DESC'])
          ->setLimit(10);
      $history = $entity->getEntityChangeSet()->matching($criteria);
      
  4. User Tracking

    • The bundle automatically logs the user who made the change (via SecurityContext). Ensure your User entity implements UserInterface and is properly configured in resolve_target_entities.
  5. Doctrine Events

    • The bundle throws exceptions on onFlush if validation fails. Catch these in your event subscriber:
      public function onFlush(OnFlushEventArgs $args): void
      {
          try {
              // ...
          } catch (HistoriqueException $e) {
              $this->logger->error($e->getMessage());
          }
      }
      

Debugging Tips

  1. Verify Factory Mapping Check config/packages/atournayre_historique.yaml for correct mappings entries. A missing or misconfigured mapping will silently ignore changes.

  2. Inspect Change Sets Dump $changeSet in your factory to debug what’s being passed:

    public function create(array $changeSet): ?History
    {
        dump($changeSet); // Debug the input
        // ...
    }
    
  3. History Entity Not Persisting Ensure:

    • The History entity is annotated with @ORM\Entity.
    • The HistoryEventSubscriber is listed in EntityListeners:
      #[ORM\EntityListeners([HistoryEventSubscriber::class])]
      class History { ... }
      
  4. Symfony Security Context If the logged user is null, verify:

    • The User entity is properly resolved in doctrine.yaml.
    • The Security component is configured to load the user (e.g., via FIREWALL in security.yaml).

Extension Points

  1. Custom History Fields Extend the History entity to add metadata (e.g., ipAddress, device):

    #[ORM\Column(type: 'string', nullable: true)]
    private ?string $ipAddress = null;
    
    // Set in factory:
    $this->changeSet->set('ipAddress', HistoryDTO::createFromChangeSet(
        'IP Address',
        $_SERVER['REMOTE_ADDR'] ?? null
    ));
    
  2. Bulk Operations Disable history for bulk updates (e.g., batch imports) by temporarily removing the HistorycableTrait or using a decorator pattern.

  3. Soft Deletes Integrate with gedmo/doctrine-extensions to log soft-deleted entities:

    #[ORM\EntityListeners([HistoryEventSubscriber::class, SoftDeleteableListener::class])]
    class Product { ... }
    
  4. Custom DTO Factories Override HistoryDTOFactory to handle complex types (e.g

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.
nasirkhan/laravel-sharekit
directorytree/privacy-filter-classifier
directorytree/privacy-filter
datacore/hub-sdk
develia/commons
cuci/prototurk-sdk
cuci/prototurk-sdk-symfony
develia/geo-bundle
dreamzy/livewire-charts
touchestate-sdk/php-sdk
22h/doctrine-garbage-collection-bundle
agtp/agtp-php
agtp/mod-php
splash/sonata-admin
splash/metadata
splash/openapi
splash/scopes
splash/toolkit
testo/output-teamcity
testo/bridge-symfony