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

Symfony Doctrine Audit Laravel Package

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.

View on GitHub
Deep Wiki
Context7

Product Decisions This Supports

  • Whether "full audit history" is in scope at all. The package records field-level changes for Doctrine entities, including collection/join-table membership changes, into either a second relational database or an append-only JSONL file. That makes "do we ship an audit trail?" a buy-vs-build decision rather than a quarter of engineering work.
  • Build vs. buy. The realistic alternative is a hand-rolled onFlush/postFlush listener plus a bespoke purge job. This release is largely a catalogue of exactly the bugs that hand-rolled listeners ship with — see below — so the comparison should be "our own listener, two iterations in" not "our own listener, day one."
  • Retention and purge policy. The purge command with --before is the retention lever; you can commit to a stated retention window ("24 months hot, then removable") and back it with a tool that refuses to proceed on a corrupt file rather than silently doing the wrong thing.
  • Data-integrity commitments you can make to compliance. Two fixes materially change what you can promise:
    • A non-scalar identifier now fails in onFlush, before the transaction opens, instead of in postFlush after commit. Previously the database kept a change whose audit row was lost — i.e. silent gaps in the audit log. That is the single most important line in these notes.
    • A failed purge no longer leaves a partial <file>.purge kept set behind, and a failed JSONL write is truncated under lock. Both previously turned one bad run into a file that every later read and purge rejected.
  • Schema and data-model decisions. Entities keyed by an association (foreign key as primary key) used to crash the auditor on every flush. Composite and association-keyed legacy tables are now auditable, so "we can't audit that table, it has an awkward key" stops being an architecture constraint.
  • Identifier conventions. --identity='code="007"' is the escape hatch for zero-padded codes, references and string-that-looks-numeric keys. If your product identifiers are human-readable codes, this decides whether the CLI can target a single entity at all.
  • Storage topology. Relational audit table (queryable, joins to your own reporting) vs. JSONL file (shippable to a warehouse, no DB load). The example app demonstrates both against MySQL/MariaDB.
  • Upgrade sequencing. If you are on any 4.x prior to this, upgrading is a data-loss remediation, not a feature upgrade. Treat it as such on the roadmap.

When to Consider This Package

Consider it when:

  • Your domain model is Doctrine ORM entities. In a Laravel codebase that means the app has gone Doctrine-native, or has a Doctrine-managed bounded context alongside Eloquent. If everything is Eloquent, this is not the tool and you should look at owen-it/laravel-auditing or spatie/laravel-activitylog instead.
  • You need history of collection membership, not just scalar columns — the "who was on this list, and when did that change" question. This is the differentiator; most audit packages only cover the owning row.
  • The domain is regulated or dispute-prone: pricing, entitlements, product nomenclature, permissions, contract terms.
  • You want no new infrastructure: an existing database plus a file is enough. No broker, no CDC pipeline, no SaaS vendor in the data path.
  • You need a defensible retention/purge story with recovery semantics rather than a DELETE FROM audit WHERE created_at < ?.
  • You are on MySQL or MariaDB, which is what the example application and its CI job actually exercise.

Look elsewhere when:

  • Your primary need is per-request actor attribution (who clicked the button) — verify this explicitly in the docs before committing; the release notes describe what changed, not who changed it.
  • You need event streaming / sub-second downstream consumers. A JSONL file and a second DB are batch-shaped.
  • You have very high write volume. Every flush now does extra reads during onFlush for collection removals — correctness bought with queries.
  • Your doctrine/dbal is pinned below ^4.2. This release raises the floor because Types::ENUM and Column::getValues() don't exist in 4.0/4.1 — the previously declared floor could not actually install. If you're stuck on dbal 4.0/4.1, this version is not available to you and the enum-column stability it provides is not either.
  • You are on Postgres/SQL Server and unwilling to be the first. The guarantees here are tested on MySQL/MariaDB.

How to Pitch It (Stakeholders)

To executives (one paragraph): We can offer customers a complete, tamper-evident change history — including who was added to or removed from collections — without buying an audit SaaS product or standing up new infrastructure. This release is mostly a correctness release: it closes three ways an audit log can silently develop holes (changes committed without their audit row, collection history lost on owner deletion, a corrupt file that blocks all later reads) and makes the retention/purge tool refuse to act rather than act on bad data. That is the difference between "we log changes" and "we can defend the log." Cost is contained: an existing database plus a file, no vendor.

To engineering (the short version):

  • Non-scalar identifiers now fail in onFlush instead of postFlush — the previous behaviour committed the entity change and lost the audit row. Audit completeness is now enforced pre-transaction wherever the identifier is readable at that point.
  • Association-keyed entities no longer TypeError on every flush; getOriginalEntityData() returns the referenced column value, which was being handed to isInIdentityMap().
  • Collection removals are now read from the database during onFlush, because Doctrine's Collection::clear() snapshots after emptying and owner deletion bypasses collection scheduling entirely. Both previously produced empty removed lists.
  • Purge: an incomplete kept set is discarded and the real cause surfaced; a complete kept set is still left for recovery, which is the designed behaviour. Don't "fix" that.
  • FileStorage truncates a partially-written record under the same exclusive lock, so one bad write no longer poisons every subsequent read and purge.
  • Audit cursors that don't round-trip exactly (trailing newline, beyond PHP_INT_MAX) are now rejected instead of being read as end-of-data.

Two things to plan for, not discover:

  1. doctrine/dbal floor is now ^4.2. Audit this against every service before scheduling the bump.
  2. JSONL date is now ATOM with offset. A --before boundary was previously honoured up to fourteen hours off when the purge ran in a different timezone. Old records still read, in the reader's timezone as before — so a mixed-format file is readable but not uniformly interpretable. If you retain long windows, know which side of the change a given file was written on.

Positioning: adopt at v4.1 or later, not earlier. Everything before it is a version whose failure mode was missing audit rows.

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.
mehedimi/laravel-amp-mail
happones/laravel-vue-i18n-generator
jwerd/inertia-table
siberfx/flysystem-wasabi
hollyit/token-replace
ippanelcom/laravel-sdk
adachsoft/changelog-linter
adachsoft/collection
payerurl/telegrambot-crypto-payment-instant-payout
dskripchenko/laravel-hybrid-search
riorizkyrainey/lara-ced
mehedimi/static-vel
zharp/zharp
nonsapiens/bigquery-model-sync
lgu-bayambang-ict-office/lgu-laravel-development-standards
andydefer/laravel-support
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel