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

Elasticsearch Audit Bundle Laravel Package

borsche/elasticsearch-audit-bundle

A Symfony bundle that records who changed what into Elasticsearch: Doctrine entities audited automatically, custom domain actions logged on demand, small changes coalesced into one record, async writes via Messenger, and a filterable read API for searchable audit history.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture fit. Despite being framed as a Laravel/PHP package, the substance of these notes is a Doctrine ORM auditing bundle with a transactional outbox built on Symfony Messenger (PhpSerializer, transports, AuditTransaction::run(), DBAL savepoints and nesting levels, Doctrine column/association mapping, elastic/transport encoding). That is a meaningfully different architecture from a Laravel package: Laravel's default persistence is Eloquent, and its queue abstraction is not Messenger. The package's value proposition — "capture every write to audited entities as a record, transactionally, and ship it via an outbox" — is coherent and well-suited to a Doctrine/Symfony service. It is not a drop-in for a standard Laravel app, and any assessment that treats it as one will overstate fit. Where the codebase does fit, it fits deeply: it hooks the unit of work at the flush, which is the only place an audit trail can be made truthful.

Integration feasibility. The capability surface is real and useful: audit:check now validates every audit declaration on the connection before anything is flushed, which turns a class of runtime data-loss bugs into a pre-merge CI failure. That is the single highest-value addition here and is straightforward to wire into a pipeline. The outbox-connection warning is a good defensive check. But integration is gated on several preconditions the team must satisfy deliberately: an audited Doctrine connection, an outbox table in the application's own database, Messenger transports configured, a redaction policy (redact.fields, max_depth, max_nodes), and an explicit choice of failure policy (log vs throw) per environment. Feasibility is high; effort is not trivial, and it is not a "composer require and go" package.

Technical risk. Three distinct risk classes, and they must not be conflated:

  1. Security — act now. The most serious item is the redaction defect: redaction read an object (via jsonSerialize() or public properties) but then handed the object itself to Messenger, whose PhpSerializer serialized every property including private ones, into a queue that — with the outbox — is a table in the application's own database. With no redact.fields configured, nothing read the record at all, so this occurred with no redactor in play. This has been present since 1.0.0. Any deployment handling PII, credentials, or health data must treat prior versions as a confirmed data-exposure incident and assess breach-notification obligations, not merely schedule an upgrade.
  2. Correctness — audit-trail integrity. AuditTransaction::run() could report a commit that had not happened, leaving the change and its history uncommitted and a transaction open for whatever ran next on the connection; its undo closed one level too few, leaving rows of a failed operation in an outer transaction. A commit or rollback issued by the operation itself — via the API or via SQL (COMMIT, BEGIN, ROLLBACK, ABORT) — went unseen. A failed rollback left a transaction open for the next request on a pooled connection. All dated from 1.1.0. For a component whose entire product is "the history is true," these are existential defects, and their longevity (multiple majors of exposure) is a governance signal about the maintainer's test coverage, not just about these lines of code.
  3. Release hygiene — the upgrade itself. A patch release (1.3.1) contains a behavioural breaking change: records now leave the writer as plain values (scalars, arrays, bundle-built stdClass) rather than application objects, so listeners doing instanceof MyDto or expecting DateTimeInterface on a Change side will break; there is also a UPGRADE.md. Additionally, the notes are internally inconsistent — the preamble reasons about 1.2.6, the final bullet is truncated mid-sentence, and several fixes are attributed to 1.3.0. Consumers cannot reliably reconstruct what changed from this document alone.

The residual, acknowledged limitation matters for framing: commits MySQL performs implicitly for DDL are not observed, and without the connection observer only the nesting level is checked. The package is therefore a strong audit trail, not a provable one. Do not position it as a compliance control without independent reconciliation.

Key questions.

  • Which of the three risk classes does the business actually care about? Security remediation is urgent regardless; audit-trail correctness has a different owner and tempo; the breaking change is a scheduling problem.
  • Is the truncated UPGRADE.md-referenced content recoverable from the repo, or is the changelog itself incomplete?
  • What is the maintained test suite's coverage of the transaction/outbox layer specifically, given defects introduced in 1.0.0, 1.1.0, and 1.3.0 all survived to 1.3.1?
  • Is there a stated support window / LTS policy, and does the project's release cadence (behavioural changes in patch releases) meet our dependency policy?
  • Does the app actually have a Doctrine connection to audit, or is this being evaluated against an Eloquent codebase where it cannot apply?

Integration Approach

Stack fit. Confirm the target service uses Doctrine ORM (not Eloquent) and can host Symfony Messenger. If the service is genuinely Laravel/Eloquent, this package is the wrong tool and the assessment should stop here — no amount of wrapping makes the flush-hook model meaningful without the unit of work. If the service is Symfony/Doctrine, fit is good. If it is Laravel-with-Doctrine (via a Doctrine bridge), fit is plausible but the Messenger transports and failure policies need explicit configuration and are outside Laravel's queue conventions; budget real design time, and expect two queue systems to coexist.

Migration path. Do not bundle these three risk classes into one change. Sequence: (1) a security-only upgrade on the fastest safe path — ideally to a build containing the redaction fix without the plain-values change; if the maintainer offers no such backport, plan the security upgrade and the breaking change together and accept the coupling, but scope the blast radius first by grepping for instanceof assertions and DateTimeInterface access on RecordCreatedEvent, CollectedRecord, and changes payloads. (2) Purge or quarantine the backlog: rows already written to the outbox table may contain serialized objects with private properties in the payload. Fixing the writer does not fix rows already at rest. This is a data-remediation task, not a code task, and is routinely missed. (3) Then adopt the correctness fixes and audit:check. For the connection-observer fixes, verify no application code relies on run()'s prior lenient behaviour — operations that previously "succeeded" while leaving a transaction open will now throw, which is correct but is a production-behaviour change.

Compatibility. Verify DBAL major version (the notes distinguish DBAL 4 savepoint behaviour from earlier), elastic/transport and client versions (8.11/8.19 vs 9.x are called out separately), Doctrine ORM version, and Symfony Messenger version. The immediately: true clarification is documentation-only but matters operationally — "visible after the index's next refresh," not immediately — and any SLA or UX copy that assumed immediate searchability must be corrected.

Sequencing. Contract: audit:check green in CI → redaction policy explicitly configured (redact.fields, plus the now-functional max_depth/max_nodes) → outbox connection covered by the observer → failure policy chosen per environment (log for availability, throw for integrity-critical paths) → plain-values consumer audit complete → production. Treat audit:check as a required status check, not an optional lint.

Operational Impact

Maintenance. Expect ongoing churn: this release alone touches transactions, encoding, serialization, redaction, and type handling. Pin exact versions, subscribe to releases, and budget recurring upgrade work rather than assuming a stable substrate. The volume of "code responsible dates from X" attributions across three minor versions suggests a project still stabilising its own core; plan for more of this.

Support. Upstream support posture is unclear from the notes. The honest self-documentation of limitations (MySQL DDL implicit commits, level-only checking without the observer) is a genuine positive — it is the mark of a maintainer who understands the failure surface — but it also means the package will not silently do the right thing. Someone on the team must own it by name.

Scaling. Two scaling axes. Write path: auditing every flush adds work inside the request's transaction, and AuditTransaction holds the transaction across record construction and history writes — longer transactions mean more lock contention under load. Test at target concurrency, not just functionally. Growth path: changes is described as not indexed, but the outbox is a table in the application's own database that grows with every audited write; without a retention/archive policy it becomes the largest table in the schema and a backup/restore liability. Define retention before, not after, go-live.

Failure modes. The set is now unusually well enumerated, which is useful: unrepresentable declaration (fails audit:check, then per-flush via failure policy); stream bound to an audited column (field change omitted, rest of record written under log; commit refused inside AuditTransaction; flush aborted under throw); nested or self-managed transaction (refused, all levels rolled back); failed rollback (connection closed, session voided); object payload (now impossible). Two gaps to plan around: MySQL DDL implicit commits are invisible, and without the connection observer a commit-and-rebegin is undetected. Both mean the trail can, in narrow cases, be incomplete — so any downstream system treating the audit log as authoritative needs a reconciliation job or must accept documented blind spots. Also note the availability trade-off: under log, writes succeed with a degraded record; under throw, audit failure becomes request failure. That choice is a product decision, not a technical one, and should be made explicitly per entity and per environment.

Ramp-up. Moderate. An engineer needs Doctrine's unit of work and flush semantics, DBAL transaction nesting, Messenger serialization, and the bundle's failure policy model before they can reason about this safely. The new audit:check is the best onboarding artifact available — it converts tribal knowledge of the audit rules into a command — and should be the first thing a new owner runs. Budget roughly a week to competent, more if they also own the outbox retention and reconciliation story. The UPGRADE.md reference is essential reading; the truncated release notes are not a substitute for it, and the first task for the adopting engineer should be to reconstruct the full changelog from the repository.

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.
gmostafa/php-graphql-client
xeviant/paystack
snowsoft/faker
meditationsteps/blogetc
animalyoung/laravel-influxdb
onix-solutions/sms-telera
lonely-lights/prosetta
oxygen/data
oxygen/core
jengo/base
xiloryx/log-collector
a21ns1g4ts/filament-stripe
la-php/laravel-admin
artisan-build/community-prompts
vleroy/smart
ayles-software/laravel-australian-public-holidays
cboxdk/laravel-id-spatie
cboxdk/laravel-id-billing
cboxdk/laravel-id-billing-bridge
simple-as-fuck/php-performance-log