Skip to content

Commit

Permalink
Add auditing for model state changes (#51)
Browse files Browse the repository at this point in the history
The commit introduces tracking for which user sets the approval state for a model. This is accomplished by adding an 'audited_by' column to the 'approvals' table, and updating the approval setter to include auditing data. Migration and publishing steps for upgrading to this version are documented in UPGRADE.md file.
  • Loading branch information
cjmellor authored Feb 17, 2024
1 parent ff34845 commit b716e4e
Show file tree
Hide file tree
Showing 6 changed files with 91 additions and 4 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,10 @@ Here is some info about the columns in the `approvals` table:

`original_data` => All the fields in the Model before they were updated. This is a JSON column. This column is cast to the `AsArrayObject` [Cast](https://laravel.com/docs/9.x/eloquent-mutators#array-object-and-collection-casting)

`rolled_back_at` => A timestamp of when this was last rolled back to its original state

`audited_at` => The ID of the User who set the state

If you want to check if the Model data will be bypassed, use the `isApprovalBypassed` method.

```php
Expand Down
14 changes: 14 additions & 0 deletions UPGRADE.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
# Upgrade Guide

## v1.4.2 -> 1.4.3

If you wish to audit which User set the state for the Model, you need to publish and run a new Migration.

Run the `vendor:publish` command to bring in the new migration file:

```shell
php artisan vendor:publish
```

and choose `approval-migrations`

Run `php artisan migrate`

## v1.3.1 -> v1.4.0

To support the new `rollback` functionality, a new migration file is needed
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration {
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('approvals', function (Blueprint $table) {
$table->foreignId('audited_by')
->after('original_data')
->nullable()
->constrained('users');
});
}

/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('approvals', function (Blueprint $table) {
//
});
}
};
1 change: 1 addition & 0 deletions src/ApprovalServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public function configurePackage(Package $package): void
->hasMigrations([
'2022_02_12_195950_create_approvals_table',
'2023_10_09_204810_add_rolled_back_at_column_to_approvals_table',
'2023_11_17_002135_add_audited_by_column_to_approvals_table',
]);
}
}
13 changes: 9 additions & 4 deletions src/Scopes/ApprovalStateScope.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use Illuminate\Database\Eloquent\Relations\Relation;
use Illuminate\Database\Eloquent\Scope;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Schema;

class ApprovalStateScope implements Scope
{
Expand Down Expand Up @@ -131,11 +132,15 @@ protected function updateApprovalState(Builder $builder, ApprovalStatus $state):
ApprovalStatus::Rejected => Event::dispatch(new ModelRejected()),
};

$auditedData = ['state' => $state];

if (Schema::hasColumn($builder->getModel()->getTable(), 'audited_by')) {
$auditedData['audited_by'] = auth()->id();
}

return $builder
->find(id: $builder->getModel()->id)
->update([
'state' => $state,
]);
->update($auditedData);
}

/**
Expand All @@ -147,7 +152,7 @@ protected function addPostpone(Builder $builder): void
}

/**
* Set state as 'rejected'
* Set the state as 'rejected'
*/
protected function addReject(Builder $builder): void
{
Expand Down
33 changes: 33 additions & 0 deletions tests/Feature/Scopes/ApprovalStateScopeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -199,3 +199,36 @@

$this->assertDatabaseMissing(table: 'fake_models', data: $this->fakeModelData);
});

test(description: 'The model approver is listed correctly', closure: function () {
Schema::create('fake_users', callback: function (\Illuminate\Database\Schema\Blueprint $table) {
$table->id();
$table->string(column: 'name');
$table->string(column: 'email')->unique();
$table->string('password');
});

class FakeUser extends \Illuminate\Foundation\Auth\User
{
protected $guarded = [];
protected $table = 'fake_users';
public $timestamps = false;
}

$user = FakeUser::create([
'name' => 'Chris Mellor',
'email' => 'chris@mellor.pizza',
'password' => 'password',
]);

$this->be($user);

FakeModel::create($this->fakeModelData);

$approval = Approval::first();
$approval->approve();

expect($approval)->fresh()->audited_by->toBe(expected: $user->id);

Schema::dropIfExists('fake_users');
});

0 comments on commit b716e4e

Please sign in to comment.