Weave 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

Content History Bundle Laravel Package

integrated/content-history-bundle

Integrated Content History Bundle adds revision tracking to Symfony content entities, automatically recording changes and giving editors a clear history view to compare and restore previous versions. Simplify auditing, rollback, and editorial workflows with minimal setup.

View on GitHub
Deep Wiki
Context7

Getting Started

This bundle lives on Bitbucket, not Packagist, so the first move is a VCS repository entry rather than a plain composer require:

{
  "repositories": [
    { "type": "vcs", "url": "https://bitbucket.org/eactive/integrated-content-history-bundle.git" }
  ]
}
composer require integrated/content-history-bundle:dev-master

Pin the resolved commit in composer.lock immediately, and make sure CI has Bitbucket credentials (SSH deploy key or app password) — anonymous HTTPS clones fail quietly in pipelines.

Where to look first, in order:

  1. src/IntegratedContentHistoryBundle.php and src/DependencyInjection/Configuration.php — for an undocumented bundle, the config tree is the documentation. Every knob it accepts is declared here.
  2. src/Entity/ plus any Resources/config/doctrine/ or Migrations/ — this reveals the table shape it expects and whether you must author the schema yourself.
  3. src/EventSubscriber/ or src/EventListener/ — this is where "automatic" revision capture actually lives (if it exists). Read the listener instead of guessing which Doctrine events are hooked.
  4. README.md — treat as a hint to verify, not a contract.

Sanity-check the wiring before writing any feature code:

bin/console cache:clear
bin/console debug:config integrated_content_history
bin/console doctrine:schema:validate

First use case: pick a single existing content entity (Article, Page, or Block) and get exactly one history row written when it's edited. Resist wiring every entity up front — the capture mechanism (automatic listener vs. explicit service call) determines how invasive the whole integration is, and you want to learn that on one model, not five.

Framework reality check: despite any "Laravel/PHP" framing in the listing, the Bundle suffix and eactive hosting point to Symfony. If you are Laravel-only, budget this as a port, not a dependency. The steps below assume a Symfony host or an intentional port.

Implementation Patterns

Automatic capture via Doctrine events. If the bundle ships a listener, it typically hooks onFlush/postPersist/postUpdate and derives a history row from the entity change set. The integration work is then mostly configuration: telling it which entities to watch. Check Configuration.php for a classes, entities, or subjects node — that list is your allowlist.

Explicit capture at business boundaries. For content systems, "every column write" is usually the wrong granularity: you get noise from SEO fields, slugs, and timestamps. A more durable pattern is to invoke the history service yourself at meaningful moments:

public function publish(Article $article, User $actor): void
{
    $this->history->record($article, 'published', $actor, [
        'snapshot' => $this->normalizer->normalize($article, null, ['groups' => ['history']]),
    ]);
}

This survives refactors of the bundle's listener, is unit-testable, and lets you record intent (published, reverted, translation_added) instead of raw column deltas.

Snapshot vs. delta. Store a normalized snapshot for revert capability, and a diff for display. Do not rely on the bundle to serialize your entities — Doctrine proxies, lazy collections, and circular associations will bite you. Normalize through Symfony's Serializer with an explicit history group.

Version-on-publish, not on-save. A workflow that matches editorial reality:

Event History row? Payload
Draft autosave No (or coalesced) —
Publish / unpublish Yes Full snapshot + actor
Revert Yes New row referencing the restored revision
Bulk import No —

Revert flow. Implement revert as a new revision whose content copies the old one, not as a destructive write back in time. That keeps the audit trail append-only and makes "restore" itself reviewable. Wrap it in a transaction and re-dispatch any index/cache invalidation the original write would have triggered.

Consumer integration (Laravel app, Symfony history service). If the history capability lives in a Symfony service and a Laravel app needs to read it, expose a thin read API (GET /content/{type}/{id}/revisions) plus an internal event stream for writes. In Laravel, mirror the intent with Eloquent observers and a content_revisions table, so the two sides share a vocabulary even if they do not share code.

Retention and authorization. A revision list is a data-exfiltration surface. Gate it behind the same policy as edit access, and add a retention/archival job from day one — this table only grows.

Gotchas and Tips

  • It's a Symfony bundle. There is no service provider, no facade, no Laravel auto-discovery. In Laravel you either port the concept (observers + events + migrations) or isolate it behind a Symfony service — there is no drop-in path.
  • Not on Packagist, zero stars, zero dependents. Assume you own maintenance. Fork, pin, and assign a named owner. Treat upstream as read-only reference code.
  • Schema ownership is ambiguous. Verify whether the bundle ships migrations or expects you to create the table. doctrine:schema:validate will tell you fast; a silent mismatch shows up later as "history writes succeed but reads return nothing."
  • Check the Doctrine lifecycle event used. onFlush fires inside the transaction and is the usual place for subtle bugs (you can capture entities that are later rolled back, or double-count updates in a unit of work). Confirm behavior with a test that deliberately fails after flush.
  • Synchronous history writes hurt latency. If capture is inline, batch imports and bulk operations will crawl. Queue the write, or disable capture around bulk jobs, and log deliberately when you do.
  • PHP/Symfony/Doctrine version constraints are undocumented. Read composer.json first; deprecated Doctrine APIs (LifecycleEventArgs, preUpdate signatures) differ across majors and will fail at runtime, not at install.
  • Serialization traps. Doctrine proxies, DateTimeImmutable, and relations without cascade will either throw or produce gigantic nested snapshots. Use a dedicated normalizer group and assert snapshot size in a test.
  • Debug the capture path directly. bin/console debug:event-dispatcher will show whether the history subscriber is actually registered; debug:config integrated_content_history shows what the bundle thinks it was told. Between those two, most "nothing is being recorded" issues resolve.
  • Extension points are undocumented. The Configuration.php tree and the service definitions in Resources/config/ are the real extension surface — override or decorate services there (e.g. swap the recorder) rather than editing vendor code.
  • Authorization gaps around old revisions. Historical rows often preserve fields (draft text, PII) that were later removed from the live record. Make sure erasure/redaction requests reach the history table too, or you will fail a compliance review you thought you'd passed.
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.
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package