precision-soft/symfony-doctrine-audit
Symfony Doctrine Audit provides automatic audit trails for Doctrine entities in Symfony apps. It tracks create, update, and delete operations, records changed fields and values, and stores a history for compliance, debugging, and accountability, with configurable listeners and mappings.
Architecture fit. v4.1.0 is a Doctrine ORM bundle, not a Laravel-native package. Everything in the release — onFlush/postFlush hooks, UnitOfWork::isInIdentityMap(), getOriginalEntityData(), Collection::clear() — is Doctrine event-listener surface. That means it fits a Laravel application only where the audited models are Doctrine entities, which is a minority pattern in Laravel codebases. Eloquent-based models get nothing from this release. A TPM should treat this as a Doctrine-infrastructure dependency that happens to be installed via Composer into a Laravel app, not as a Laravel package.
Integration feasibility. Composer-installable and the changes are additive plus bug-fix oriented, so the migration surface is small. The one hard gate is the changed floor: doctrine/dbal now requires ^4.2 because the audit schema uses Types::ENUM and Column::getValues(). Applications on dbal 4.0/4.1 cannot install this release, and applications pinned to 3.x cannot either. Any Laravel app that also runs Doctrine migrations is likely to have a DBAL pin in composer.lock, so the first integration question is not "does the feature work" but "can we resolve the dependency graph."
Technical risk. Three items carry real weight:
onFlush before the transaction opens instead of in postFlush after commit. This is a correctness improvement — previously the database kept a committed change with no audit row — but it converts a silent audit gap into a hard write failure. Entities whose primary key includes a foreign key are now the most likely thing to break on upgrade, and they will break in production write paths, not in tests, unless those paths are exercised.--before on a purge run now means different things depending on when the record was written. Any retention policy or export pipeline consuming these files needs to know which era a record is from.removed list and lost collection history; a failed purge could leave a partial <file>.purge that the next run told the operator to restore over a good audit file; FileStorage could leave a partial JSONL line that then broke every subsequent read. These are reassuring to have fixed, but the fact that they existed means historical audit files produced by earlier releases may contain gaps that the upgrade does not retroactively repair.Key questions for the maintainers.
onFlush refusal for association-keyed identifiers a hard throw, and does it abort the whole unit of work or just that entity?dbal 3.x users — is 4.1.x the last line supporting 3.x?.example/ micro-kernel intended as a supported reference or as a test fixture?Stack fit. Confirm first whether the audit target is Doctrine entities. If the application is Eloquent-only, this package is out of scope and the assessment ends there. If there is a Doctrine island — a reporting module, a legacy bounded context — the bundle is a reasonable fit for that island and nothing else.
Migration path. The upgrade is a composer require bump with three pre-flight checks:
composer why-not doctrine/dbal ^4.2 before touching the bundle. If the app is pinned below 4.2 by a migration tool, a profiler, or a Laravel major that constrains DBAL, either bump those in the same change or defer the bundle upgrade. Do not attempt to satisfy the constraint by ignoring it — the release notes state the declared floor was previously uninstallable, meaning earlier versions may have installed against a DBAL that lacks the enum APIs the schema needs.Compatibility. The bundle remains backward compatible at the API level; there are no removals in this release. The compatibility risks are all behavioural (error timing) and environmental (DBAL floor, mixed timestamp corpus). Semver-wise a DBAL floor bump in a minor is aggressive but defensible given the previous floor could not install.
Sequencing. (1) Bump dbal and any dependents; (2) upgrade the bundle; (3) update JSONL consumers for ATOM offsets; (4) run the .example/ validation harness — ./.dev/validate/all.sh --example — against MySQL and MariaDB to confirm the micro-kernel scenarios pass on your engine and version; (5) deploy with the identifier and collection flush tests as the gate.
Maintenance. The .example/ application plus its dedicated CI job is a meaningful maintenance asset: it gives an adopter a runnable oracle for expected behaviour across every capability, on two database engines. It also raises the maintenance bar for the maintainer, since the example is now a second thing to keep green. From a consumer standpoint, the addition is low-cost and high-value.
Support. Expect an uptick in two support categories after upgrade: applications whose entities are keyed by associations and now throw at flush, and operators reading a .purge file. The purge behaviour changed in a good direction — an incomplete kept set is now deleted and the real cause reported, rather than being left as a trap — but operators familiar with the old behaviour need to be told that a missing .purge file now means "the purge failed, read the error" rather than "recovery is impossible."
Scaling. The correctness fixes for collection auditing read join rows from the database during onFlush, while they are still present. That is the right fix and there is no other place to do it, but it adds query volume to the flush path proportional to the number of audited owning collections being cleared. On bulk operations — mass deletes, wide collection clears — this is a new per-flush cost in the write path, and it lands precisely where write latency is most sensitive. Benchmark the batch-delete path before and after.
Failure modes to instrument. (a) Flush-time exceptions from association-keyed identifiers aborting ordinary writes; (b) partial .purge removal with the underlying cause only in logs; (c) FileStorage truncation relying on an exclusive lock — correct on a single host, but a shared filesystem or multi-container deployment writing the same JSONL path is outside what a PHP file lock reliably guarantees; (d) cursor rejection, which now correctly refuses an undecodable cursor rather than silently reporting end-of-data — good, but it will surface previously invisible corruption as an error; (e) timezone drift on --before boundaries for records written before this release.
Ramp-up. Low for a developer who has worked with Doctrine events; moderate for a Laravel-only team, because the debugging model is different — failures arrive as onFlush exceptions from a listener rather than as Eloquent model events. Budget half a day for a Doctrine-familiar engineer to read the example app and run the validation script, and add a day if the team must first untangle a DBAL version conflict. The --identity='code="007"' quoting escape hatch is a small usability win worth documenting internally, since the unquoted form silently casts to integer and matches nothing — a class of bug that is hard to spot in a CLI invocation.
How can I help you explore Laravel packages today?