Skip to content

feat: add Doctrine DBAL and in-memory storage adapters, selected via new storage config option - #45

Merged
paullla merged 2 commits into
Locastic:masterfrom
paullla:feature/doctrine-storage-bridge
Jul 7, 2026
Merged

feat: add Doctrine DBAL and in-memory storage adapters, selected via new storage config option#45
paullla merged 2 commits into
Locastic:masterfrom
paullla:feature/doctrine-storage-bridge

Conversation

@paullla

@paullla paullla commented Jul 3, 2026

Copy link
Copy Markdown
Member
Q A
Bug fix? no
New feature? yes
BC breaks? no
Deprecations? no
Issues -
License MIT

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

  • New Doctrine DBAL storage adapter in Locastic\Loggastic\Bridge\Doctrine\Storage (DoctrineActivityLogStorage, DoctrineCurrentDataTrackerStorage, DoctrineStorageInitializer). All loggable classes share two tables (loggastic_activity_log, loggastic_current_data_tracker) discriminated by an indexed object_class column, 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 configuration
  • New in-memory storage adapter in Locastic\Loggastic\Storage\InMemory mirroring the same sorting, pagination and class-discrimination semantics, so test suites can run the full logging flow with zero external services
  • New storage config option (elasticsearch | doctrine | in_memory, default elasticsearch, 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 any elastic_* configuration. Alias overrides keep working for fully custom backends
  • doctrine/dbal (^3.8 || ^4.0) added as an explicit dependency (previously only transitive via ORM); composer description updated to reflect pluggable storage
  • README: new "Choose your storage" section up top, storage key documented in the configuration reference; CHANGELOG updated

Verification

  • Full PHPUnit suite passes (47 tests, 144 assertions): 10 new integration tests exercise the Doctrine adapter on an in-memory SQLite connection (round-trips including nullable fields, UTC normalization, sorting, pagination, multi-class discrimination, per-class recreate, tracker update/bulk-save); 8 new unit tests cover the in-memory adapters; a new functional test boots the fixture app with storage: in_memory and runs the full create/no-op-update/edit-diff/delete flow through ActivityLogger; a wiring test boots with storage: doctrine and asserts the interfaces resolve to the Doctrine bridge with no Elasticsearch services registered
  • Both DBAL majors exercised: the bundle test suite runs on DBAL 4.4, and the demo application validation below ran on DBAL 3.10
  • Demo application (Symfony 6.4 + Postgres 15) switched from its app-level adapter to storage: 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)
  • PHPStan reports no errors (no new baseline entries); PHP-CS-Fixer reports 0 fixable files; composer validate --strict passes

paullla added 2 commits July 3, 2026 15:15
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
paullla force-pushed the feature/doctrine-storage-bridge branch from 658d322 to b3ed8d4 Compare July 7, 2026 12:52
@paullla
paullla merged commit e9ebea0 into Locastic:master Jul 7, 2026
13 checks passed
@paullla
paullla deleted the feature/doctrine-storage-bridge branch July 7, 2026 13:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant