PHPackages                             ss-ipg/laravel-auditable - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Database &amp; ORM](/categories/database)
4. /
5. ss-ipg/laravel-auditable

ActiveLibrary[Database &amp; ORM](/categories/database)

ss-ipg/laravel-auditable
========================

Declarative model audit logging for Laravel using PHP attributes

v1.0.4(3mo ago)02.4k↓45%[1 PRs](https://github.com/ss-ipg/laravel-auditable/pulls)MITPHPPHP ^8.3CI passing

Since Jan 14Pushed 3mo agoCompare

[ Source](https://github.com/ss-ipg/laravel-auditable)[ Packagist](https://packagist.org/packages/ss-ipg/laravel-auditable)[ RSS](/packages/ss-ipg-laravel-auditable/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (5)Dependencies (9)Versions (7)Used By (0)

Laravel Auditable
=================

[](#laravel-auditable)

Declarative model audit logging for Laravel using PHP attributes.

Overview
--------

[](#overview)

Laravel Auditable provides a simple, attribute-based approach to audit logging for Eloquent models. Mark any model with the `#[Auditable]` attribute and all create, update, and delete events are automatically logged with detailed change tracking.

### Features

[](#features)

- Declarative `#[Auditable]` attribute on models
- Tracks `created`, `updated`, `deleted`, `soft_deleted`, and `restored` events
- Old/new value tracking for updates
- Column filtering (include, exclude, redact)
- Per-model event filtering
- Soft delete detection
- Boolean cast normalization
- JSON output for log aggregation (Datadog, Splunk, etc.)
- Extensible context providers for custom metadata
- Configurable formatters

Requirements
------------

[](#requirements)

- PHP 8.3+
- Laravel 11+

Installation
------------

[](#installation)

```
composer require ss-ipg/laravel-auditable
```

Publish the configuration file:

```
php artisan vendor:publish --tag=auditable-config
```

Quick Start
-----------

[](#quick-start)

### 1. Configure a log channel

[](#1-configure-a-log-channel)

Add an `audit` channel to `config/logging.php`:

```
'channels' => [
    // ...

    'audit' => [
        'driver' => 'daily',
        'path' => storage_path('logs/audit.log'),
        'level' => 'info',
        'days' => 14,
    ],
],
```

### 2. Add the attribute to a model

[](#2-add-the-attribute-to-a-model)

```
use SSIPG\Auditable\Attributes\Auditable;

#[Auditable]
class User extends Model
{
    // ...
}
```

### 3. Enable audit logging

[](#3-enable-audit-logging)

In your `.env` file:

```
AUDITABLE_ENABLED=true

```

That's it! All changes to the model will now be logged.

Configuration
-------------

[](#configuration)

```
// config/auditable.php

return [
    // Enable/disable audit logging at runtime
    'enabled' => env('AUDITABLE_ENABLED', false),

    // Environments where logging is permitted
    // Use ['*'] to allow all environments
    'environments' => ['local', 'production', 'staging'],

    // Log channel name (must exist in config/logging.php)
    'channel' => 'audit',

    // Context providers for adding custom metadata
    'context_providers' => [
        // App\Audit\CustomContextProvider::class,
    ],

    // Formatter class for serializing log entries
    'formatter' => SSIPG\Auditable\Formatters\JsonFormatter::class,
];
```

> **Note**: Set `environments` to `['*']` to enable audit logging in all environments regardless of `APP_ENV`.

Attribute Options
-----------------

[](#attribute-options)

The `#[Auditable]` attribute accepts several options:

OptionTypeDefaultDescription`columns``?array``null`Only audit these columns. `null` = all columns.`exclude``array``[]`Exclude these columns from auditing.`redact``array``[]`Log that column changed, but show `[REDACTED]` instead of values.`events``array`All eventsWhich events to audit.`withOriginal``bool``true`Include original values in update logs.### Examples

[](#examples)

```
use SSIPG\Auditable\Attributes\Auditable;
use SSIPG\Auditable\Enums\AuditAction;

// Audit everything (default)
#[Auditable]
class User extends Model {}

// Only audit specific columns
#[Auditable(columns: ['email', 'status'])]
class User extends Model {}

// Audit all except certain columns
#[Auditable(exclude: ['cached_data', 'last_seen_at'])]
class User extends Model {}

// Redact sensitive values
#[Auditable(redact: ['password', 'api_key'])]
class User extends Model {}

// Only audit deletions (compliance mode)
#[Auditable(events: [AuditAction::Deleted, AuditAction::SoftDeleted])]
class HighVolumeModel extends Model {}

// Don't track original values on updates (smaller logs)
#[Auditable(withOriginal: false)]
class User extends Model {}

// Combined options
#[Auditable(
    columns: ['email', 'password', 'role'],
    redact: ['password'],
    events: [AuditAction::Updated, AuditAction::Deleted],
    withOriginal: false,
)]
class User extends Model {}
```

Log Output
----------

[](#log-output)

Each audit entry is a JSON object with the following structure:

```
{
  "action": "updated",
  "context": "web",
  "model": "App\\Models\\User",
  "model_id": 123,
  "user_id": 456,
  "ip": "192.168.1.1",
  "timestamp": "2026-01-07T15:30:00+00:00",
  "changes": {
    "email": {
      "old": "old@example.com",
      "new": "new@example.com"
    }
  }
}
```

### Default Fields

[](#default-fields)

These fields are automatically included in every audit entry:

FieldSource`action`The event type (`created`, `updated`, `deleted`, etc.)`context``"web"` or `"cli"` based on how the application is running`model`The fully-qualified model class name`model_id`The model's primary key value`user_id``auth()->id()` — the authenticated user, or `null` if unauthenticated`ip``request()->ip()` — the client IP address`timestamp`ISO 8601 formatted timestamp### Changes Structure

[](#changes-structure)

The `changes` field varies by event type:

- **`created`**: All tracked attribute values
- **`updated`**: Only changed attributes with `old` and `new` values (or just new values if `withOriginal: false`)
- **`deleted`**, **`soft_deleted`**, **`restored`**: Only the model ID (`{"id": 123}`)

### Soft Delete Detection

[](#soft-delete-detection)

When a model uses Laravel's `SoftDeletes` trait, the package automatically distinguishes between soft deletes and hard deletes:

- **`soft_deleted`**: Logged when `delete()` is called on a soft-deletable model
- **`deleted`**: Logged when `forceDelete()` is called, or when deleting a model without `SoftDeletes`
- **`restored`**: Logged when `restore()` is called on a soft-deleted model

No configuration is needed—the package detects the `SoftDeletes` trait automatically.

### Cast Normalization

[](#cast-normalization)

The package respects your model's `$casts` to prevent false-positive change detection. For example:

```
// In your model
protected $casts = ['is_active' => 'boolean'];

// These are considered equivalent (no update logged):
$model->is_active = true;
$model->is_active = 1;    // Cast to true
$model->is_active = '1';  // Cast to true
```

This applies to `boolean`, `integer`, `float`, `string`, `array`, and `json` casts.

Context Providers
-----------------

[](#context-providers)

Context providers allow you to add custom metadata to every audit log entry. Create a class that implements `AuditContextProvider`:

```
namespace App\Audit;

use Illuminate\Database\Eloquent\Model;
use SSIPG\Auditable\Contracts\AuditContextProvider;
use SSIPG\Auditable\Enums\AuditAction;

class CustomContextProvider implements AuditContextProvider
{
    public function getContext(Model $model, AuditAction $action): array
    {
        return [
            'custom_id' => $model->custom_id,
        ];
    }
}
```

Register the provider in `config/auditable.php`:

```
'context_providers' => [
    App\Audit\CustomContextProvider::class,
],
```

Custom Formatters
-----------------

[](#custom-formatters)

To customize the log output format, create a class that implements `AuditFormatter`:

```
namespace App\Audit;

use SSIPG\Auditable\Contracts\AuditFormatter;

class CustomFormatter implements AuditFormatter
{
    public function format(array $payload): string
    {
        // Return your formatted string
        return json_encode($payload, JSON_PRETTY_PRINT);
    }
}
```

Register it in `config/auditable.php`:

```
'formatter' => App\Audit\CustomFormatter::class,
```

Auditing Pivot Tables
---------------------

[](#auditing-pivot-tables)

Standard `attach()`, `detach()`, and `sync()` operations bypass Eloquent model events. To audit pivot table changes, use a custom Pivot model:

```
use Illuminate\Database\Eloquent\Relations\Pivot;
use SSIPG\Auditable\Attributes\Auditable;

#[Auditable]
class ProjectUserPivot extends Pivot
{
    protected $table = 'project_user';
}
```

Then reference it in your relationships:

```
// app/Models/Project.php
public function users(): BelongsToMany
{
    return $this->belongsToMany(User::class)->using(ProjectUserPivot::class);
}

// app/Models/User.php
public function projects(): BelongsToMany
{
    return $this->belongsToMany(Project::class)->using(ProjectUserPivot::class);
}
```

Testing
-------

[](#testing)

The package provides `Audit::fake()` for testing auditable models without writing to actual log files.

### Basic Usage

[](#basic-usage)

```
use SSIPG\Auditable\Facades\Audit;
use SSIPG\Auditable\Enums\AuditAction;

public function test_user_creation_is_audited(): void
{
    Audit::fake();

    User::create(['name' => 'John', 'email' => 'john@example.com']);

    Audit::assertLogged(AuditAction::Created);
}
```

### Available Assertions

[](#available-assertions)

```
// Assert an action was logged
Audit::assertLogged(AuditAction::Created);
Audit::assertLogged(AuditAction::Updated);
Audit::assertLogged(AuditAction::Deleted);
Audit::assertLogged(AuditAction::SoftDeleted);
Audit::assertLogged(AuditAction::Restored);

// Assert with callback for detailed matching
Audit::assertLogged(
    action: AuditAction::Created,
    callback: fn (array $entry) => $entry['model'] === User::class
        && $entry['changes']['email'] === 'john@example.com'
);

// Assert an action was NOT logged
Audit::assertNotLogged(AuditAction::Updated);

// Assert nothing was logged
Audit::assertNothingLogged();

// Assert exact count of entries
Audit::assertLoggedCount(2);

// Get all logged entries for inspection
$entries = Audit::logged();
$createdEntries = Audit::logged(AuditAction::Created);
```

### Entry Structure

[](#entry-structure)

Each captured entry contains:

```
[
    'action'   => AuditAction::Created,  // The action enum
    'model'    => 'App\\Models\\User',   // Model class name
    'model_id' => 123,                   // Model primary key
    'changes'  => [                      // Changed attributes
        'name' => 'John',
        'email' => 'john@example.com',
    ],
]
```

Known Limitations
-----------------

[](#known-limitations)

- **Mass operations bypass events**: `Model::insert()`, `Model::update()` (query builder), and similar mass operations do not fire Eloquent events and will not be audited. Use model instances for auditable operations.
- **Timestamp columns excluded**: `created_at`, `updated_at`, and `deleted_at` are always excluded from change tracking (the audit log has its own timestamp).

License
-------

[](#license)

MIT License. See [LICENSE](LICENSE) for details.

###  Health Score

44

—

FairBetter than 92% of packages

Maintenance78

Regular maintenance activity

Popularity23

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity53

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 90.9% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~1 days

Total

5

Last Release

118d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/225706?v=4)[jmonday](/maintainers/jmonday)[@jmonday](https://github.com/jmonday)

---

Top Contributors

[![jmonday](https://avatars.githubusercontent.com/u/225706?v=4)](https://github.com/jmonday "jmonday (10 commits)")[![dependabot[bot]](https://avatars.githubusercontent.com/in/29110?v=4)](https://github.com/dependabot[bot] "dependabot[bot] (1 commits)")

---

Tags

audit-logeloquent-modelslaravel-packagelaravelloggingmodeleloquentAudit

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/ss-ipg-laravel-auditable/health.svg)

```
[![Health](https://phpackages.com/badges/ss-ipg-laravel-auditable/health.svg)](https://phpackages.com/packages/ss-ipg-laravel-auditable)
```

###  Alternatives

[owen-it/laravel-auditing

Audit changes of your Eloquent models in Laravel

3.4k33.0M95](/packages/owen-it-laravel-auditing)[mongodb/laravel-mongodb

A MongoDB based Eloquent model and Query builder for Laravel

7.1k7.2M71](/packages/mongodb-laravel-mongodb)[dyrynda/laravel-model-uuid

This package allows you to easily work with UUIDs in your Laravel models.

4802.8M8](/packages/dyrynda-laravel-model-uuid)[tucker-eric/eloquentfilter

An Eloquent way to filter Eloquent Models

1.8k4.8M26](/packages/tucker-eric-eloquentfilter)[watson/validating

Eloquent model validating trait.

9723.3M47](/packages/watson-validating)[spiritix/lada-cache

A Redis based, automated and scalable database caching layer for Laravel

591444.8k2](/packages/spiritix-lada-cache)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
