feat: add Doctrine DBAL and in-memory storage adapters, selected via new storage config option - #45
Merged
Conversation
Adds two official storage backends on top of the 2.0 storage abstraction, selected with the new 'storage' config option (default 'elasticsearch', BC): - Bridge\Doctrine: stores activity logs and current data trackers in two shared database tables with JSON columns, using the DBAL schema API so it works on any supported database. Timestamps are normalized to UTC. - Storage\InMemory: keeps logs in the PHP process so test suites can run the full logging flow without any external service. When a non-Elasticsearch backend is selected, no Elasticsearch services are registered.
…cates Saving a tracker for an already tracked object now updates the existing entry in the Doctrine and in-memory adapters. Plain inserts collided with the unique (object_class, object_id) index on Messenger retries and when re-running the populate command over existing data. Also: - add (object_class, logged_at) index so sorted activity log reads don't filesort on large tables - scope tracker updates by object_class in addition to the id - fall back to '[]' instead of '' for null tracker data, which would throw a JsonException on json_decode - run the CI test suite on both DBAL 3 and 4 to keep the ^3.8 || ^4.0 constraint honest
paullla
force-pushed
the
feature/doctrine-storage-bridge
branch
from
July 7, 2026 12:52
658d322 to
b3ed8d4
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Since 2.0 the core talks to three storage-agnostic interfaces, but Elasticsearch is still the only implementation the bundle ships. Requiring an Elasticsearch cluster just for activity logs is the biggest adoption blocker: most Symfony apps already run Postgres or MySQL and already depend on Doctrine. Custom backends are possible via alias overrides, but that means every team writes and maintains its own adapter. Test suites (ours and users') also had no way to run the full logging flow without a real backend.
Changes
Locastic\Loggastic\Bridge\Doctrine\Storage(DoctrineActivityLogStorage,DoctrineCurrentDataTrackerStorage,DoctrineStorageInitializer). All loggable classes share two tables (loggastic_activity_log,loggastic_current_data_tracker) discriminated by an indexedobject_classcolumn, with JSON columns for changes/tracker data. The schema is created through the DBAL schema API and all values go through DBAL type conversion, so the adapter works on PostgreSQL, MySQL, SQLite and anything else DBAL supports. Timestamps are normalized to UTC on write. Table names are constructor arguments with defaults, overridable via service configurationLocastic\Loggastic\Storage\InMemorymirroring the same sorting, pagination and class-discrimination semantics, so test suites can run the full logging flow with zero external servicesstorageconfig option (elasticsearch|doctrine|in_memory, defaultelasticsearch, so existing apps are unaffected). Picking a backend is now one line of config instead of three service aliases. When a non-Elasticsearch backend is selected, no Elasticsearch service is registered at all: the bundle boots without the ES client or anyelastic_*configuration. Alias overrides keep working for fully custom backendsdoctrine/dbal(^3.8 || ^4.0) added as an explicit dependency (previously only transitive via ORM); composer description updated to reflect pluggable storageVerification
storage: in_memoryand runs the full create/no-op-update/edit-diff/delete flow throughActivityLogger; a wiring test boots withstorage: doctrineand asserts the interfaces resolve to the Doctrine bridge with no Elasticsearch services registeredstorage: doctrine: full create/child-edit-diff/delete flow verified, and all 13 backend-parity edge-case checks pass (no-op updates, forced logs, orphan-removal diffs, cross-class isolation, pagination, tracker recreate/populate/rebuild)composer validate --strictpasses