Download the PHP package jul6art/audit-bundle without Composer

On this page you can find all versions of the php package jul6art/audit-bundle. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.

FAQ

After the download, you have to make one include require_once('vendor/autoload.php');. After that you have to import the classes with use statements.

Example:
If you use only one package a project is not needed. But if you use more then one package, without a project it is not possible to import the classes with use statements.

In general, it is recommended to use always a project to download your libraries. In an application normally there is more than one library needed.
Some PHP packages are not free to download and because of that hosted in private repositories. In this case some credentials are needed to access such packages. Please use the auth.json textarea to insert credentials, if a package is coming from a private repository. You can look here for more information.

  • Some hosting areas are not accessible by a terminal or SSH. Then it is not possible to use Composer.
  • To use Composer is sometimes complicated. Especially for beginners.
  • Composer needs much resources. Sometimes they are not available on a simple webspace.
  • If you are using private repositories you don't need to share your credentials. You can set up everything on our site and then you provide a simple download link to your team member.
  • Simplify your Composer build process. Use our own command line tool to download the vendor folder as binary. This makes your build process faster and you don't need to expose your credentials for private repositories.
Please rate this library. Is it a good library?

Informations about the package audit-bundle

logo dev in the hood

Symfony audit bundle

License Version

Requirements

Installation

Setting it up

The bundle records who changed what, when, and on whose behalf — one row per write, in a table you own. Three steps, and the third is the one people forget.

1. Declare the entity that holds the trail

The bundle ships Entity\AuditLog as a mapped superclass, not an entity. That is deliberate: the table name, the indexes, and whatever you expose on top — an API resource, a retention policy, a role check — belong to your application, not to a bundle.

⚠️ Bring your own identifier. A mapped superclass may not declare the primary key of its children, so the concrete class does — IdTrait from jul6art/core-bundle, or a hand-written $id. Forgetting it fails at mapping time with a clear message, which is the good kind of failure.

Then generate the migration: bin/console doctrine:migrations:diff. The columns are organization_id, user_id, impersonator_id, action, target_type, target_id, payload, diff, ip_address, user_agent, created_at.

Add indexes for the way you read the trail. The two above are the minimum for a chronological screen filtered by action; add (target_type, target_id) for a per-object history, user_id for a per-actor listing, organization_id for a multi-tenant scope.

2. Point the bundle at it

This option has no default and the bundle refuses to boot without it. A mapped superclass has no table, so a missing or wrong log_class would leave every #[Auditable] silently inoperative — an audit trail that records nothing, and says nothing about it. Failing at container build is the lesser evil.

3. Annotate what you want audited

That is all. EventListener\AuditableListener is registered on Doctrine's postPersist, postUpdate and preRemove as soon as audit.enabled is true, and it is what actually reads the attribute.

What a row says

Column Filled with
action <entity>.<event> in lower case — invoice.created, user.updated
target_type / target_id the entity's short class name, and its id as a string (so a UUID fits)
organization_id getOrganization()->getId() or getOrganizationId(), when either exists
user_id the account the request runs as
impersonator_id the account that started an impersonation, null on a genuine login
payload {"diff": {"field": {"old": …, "new": …}}} on an update, your own array on a manual entry
ip_address / user_agent from the current request, null in a CLI command

Diff values are flattened so the row stays readable and serialisable: a date becomes ISO 8601, an enum its backing value, a related entity its id.

A soft delete reads as a deletion, not an update

A soft delete is technically an UPDATE of deletedAt, and logging it as updated produces a trail nobody can read. When the change set shows deletedAt going from null to a date, the row is recorded as deleted — and as restored on the way back.

⚠️ So do not log those actions by hand. An application that also calls $auditLogger->log('invoice.deleted') in its service ends up with two rows for one event.

Who is acting, and on whose behalf

The actor comes from the security token, through jul6art/core-bundle's TokenStorageAwareTrait. Two things follow:

Writing an entry yourself

The attribute covers entity writes. Everything else — an email sent, an export downloaded, a permission granted — is a call:

The listener writes inside your transaction, without a nested flush

Since 3.1 the automatic listener inserts its row through the connection (logWithinFlush()), inside the transaction the commit already holds: the row is rolled back with the write it records, and costs one INSERT — no SAVEPOINT pair. Before, it flushed the unit of work again from postPersist/postUpdate, which recomputed every pending change set: an entity updated later in the same flush was written twice, its PreUpdate callbacks run again.

Batching a fan-out

log() flushes immediately, which is right for one action and disastrous for a hundred: each flush re-runs Doctrine's listener cascade over everything accumulated so far, so a ~50-query request becomes 1500+.

Calls nest, and only the outermost endBatch() flushes.

⚠️ Always close a batch in a finally. An exception escaping an open batch leaves the logger buffering every subsequent row until something else flushes — the trail then looks incomplete rather than broken, which is harder to notice.

An open batch holds its rows and persists them when it closes. That is not an implementation detail: the automatic listener writes from postPersist/postUpdate, which fire inside UnitOfWork::commit(), and a commit ends by clearing entityInsertions wholesale. A row merely persist()ed from a listener is swept away with it and never reaches the database — silently.

So this is safe, and it is the shape a state machine or an importer actually needs:

Silencing the listener during a fan-out

Batching the writes is not the same as not writing them. When a flow already records its intent (feature.assigned) and the per-row trail underneath is noise:

⚠️ Coarse on purpose, and finally is not optional. A leaked depth silently disables auditing for the rest of the request. Use it when the caller records the intent itself — never to hide writes.

Retention

Rows accumulate forever unless you say otherwise. Put #[Purgeable] from jul6art/core-bundle on your concrete entity (see step 1) and core:purge applies it.

There is deliberately no audit.retention option in this bundle: the retention lives on your entity. To make it an operational setting, let the interval name a parameter — jul6art/core-bundle ≥ 3.2 resolves it when core:purge runs, so an environment variable changes the purge with no rebuild and no migration:

With an older core-bundle, the interval must be a literal.

The bundle listens to core-bundle's EntityPurgedEvent and writes one entity.purged row per purged line, naming the entity, its id, its organisation and the interval that condemned it. That matters more than it looks: retention deletes for good, and the trail is the only remaining evidence the data existed.

Configuration

audit.enabled is also exposed as a container parameter.

Quality assurance

License

The Audit Bundle is open-sourced software licensed under the MIT license.

© 2026 jul6art


All versions of audit-bundle with dependencies

PHP Build Version
Package Version
Requires php Version ^8.5
doctrine/doctrine-bundle Version ^2.13 || ^3.0
doctrine/orm Version ^3.3
jul6art/core-bundle Version ^2.9.1 || ^3.0
symfony/config Version ^7.4 || ^8.0
symfony/dependency-injection Version ^7.4 || ^8.0
symfony/http-kernel Version ^7.4 || ^8.0
symfony/yaml Version ^7.4 || ^8.0
Composer command for our command line client (download client) This client runs in each environment. You don't need a specific PHP version etc. The first 20 API calls are free. Standard composer command

The package jul6art/audit-bundle contains the following files

Loading the files please wait ...