From 4e97b27de0698b53f5b0f09f9a8c583166029e59 Mon Sep 17 00:00:00 2001
From: GoatFreezy <alexis.vrain@gmail.com>
Date: Mon, 3 Mar 2025 22:39:36 +0100
Subject: [PATCH 1/4] Feat: Ability to disable auditable

---
 src/AuditableTrait.php               |  3 +++
 src/AuditableTraitObserver.php       |  8 +++----
 tests/Feature/AuditableModelTest.php | 33 ++++++++++++----------------
 3 files changed, 21 insertions(+), 23 deletions(-)

diff --git a/src/AuditableTrait.php b/src/AuditableTrait.php
index 49cf3aa..7df0334 100644
--- a/src/AuditableTrait.php
+++ b/src/AuditableTrait.php
@@ -12,6 +12,9 @@
  */
 trait AuditableTrait
 {
+
+    public $auditable = true;
+    
     /**
      * Boot the audit trait for a model.
      */
diff --git a/src/AuditableTraitObserver.php b/src/AuditableTraitObserver.php
index 08613b8..ff637c8 100644
--- a/src/AuditableTraitObserver.php
+++ b/src/AuditableTraitObserver.php
@@ -11,7 +11,7 @@ class AuditableTraitObserver
      */
     public function creating(Model $model): void
     {
-        if (method_exists($model, 'getCreatedByColumn')) {
+        if (method_exists($model, 'getCreatedByColumn') && $model->auditable) {
             $createdBy = $model->getCreatedByColumn();
 
             if (! $model->$createdBy) {
@@ -19,7 +19,7 @@ public function creating(Model $model): void
             }
         }
 
-        if (method_exists($model, 'getUpdatedByColumn')) {
+        if (method_exists($model, 'getUpdatedByColumn') && $model->auditable) {
             $updatedBy = $model->getUpdatedByColumn();
 
             if (! $model->$updatedBy) {
@@ -41,7 +41,7 @@ protected function getAuthenticatedUserId(): int|string|null
      */
     public function updating(Model $model): void
     {
-        if (method_exists($model, 'getUpdatedByColumn')) {
+        if (method_exists($model, 'getUpdatedByColumn') && $model->auditable) {
             $updatedBy = $model->getUpdatedByColumn();
 
             if (! $model->isDirty($updatedBy)) {
@@ -55,7 +55,7 @@ public function updating(Model $model): void
      */
     public function saved(Model $model): void
     {
-        if (method_exists($model, 'getUpdatedByColumn')) {
+        if (method_exists($model, 'getUpdatedByColumn') && $model->auditable) {
             $updatedBy = $model->getUpdatedByColumn();
 
             if ($this->getAuthenticatedUserId() && $this->getAuthenticatedUserId() != $model->$updatedBy && $model->isDirty()) {
diff --git a/tests/Feature/AuditableModelTest.php b/tests/Feature/AuditableModelTest.php
index 89f5c3d..619ce5c 100644
--- a/tests/Feature/AuditableModelTest.php
+++ b/tests/Feature/AuditableModelTest.php
@@ -48,32 +48,27 @@
     expect($post->deleted_by)->toBe($user->id);
 });
 
-test('a model wont be updated if edited by another user without it being dirty', function () {
-    $user = User::create([
+test('a post can be created without audit', function () {
+    $user = User::forceCreate([
         'name' => 'John Doe',
         'email' => 'john@example.com',
     ]);
 
     actingAs($user);
 
-    $anotherUser = User::create([
-        'name' => 'Jane Doe',
-        'email' => 'jane@example.com',
-    ]);
+    $post = new Post();
+    $post->title = 'Hello World';
+    $post->auditable = false;
+    $post->save();
 
-    DB::table('posts')->insert([
-        'title' => 'Hello World',
-        'created_by' => $anotherUser->id,
-        'updated_by' => $anotherUser->id,
-    ]);
-
-    $model = Post::first();
-
-    expect($model->created_by)->toBe($anotherUser->id);
-    expect($model->updated_by)->toBe($anotherUser->id);
+    expect($post->created_by)->toBe(null);
+    expect($post->updated_by)->toBe(null);
+    expect($post->deleted_by)->toBe(null);
 
-    $model->save();
+    $post->auditable = true;
+    $post->title = 'Hello World 2';
+    $post->save();
 
-    expect($model->created_by)->toBe($anotherUser->id);
-    expect($model->updated_by)->toBe($anotherUser->id);
+    expect($post->updated_by)->toBe($user->id);
+    expect($post->deleted_by)->toBe(null);
 });

From 11545d31325cdba7de07dc26b932ac5823c5d313 Mon Sep 17 00:00:00 2001
From: GoatFreezy <alexis.vrain@gmail.com>
Date: Mon, 3 Mar 2025 23:31:49 +0100
Subject: [PATCH 2/4] Type & Doc $auditable + Use pint to format

---
 src/AuditableTrait.php               | 4 ++--
 tests/Feature/AuditableModelTest.php | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/AuditableTrait.php b/src/AuditableTrait.php
index 7df0334..ac3cc0f 100644
--- a/src/AuditableTrait.php
+++ b/src/AuditableTrait.php
@@ -9,12 +9,12 @@
 /**
  * @property Model $creator
  * @property Model $updater
+ * @property bool $auditable
  */
 trait AuditableTrait
 {
+    public bool $auditable = true;
 
-    public $auditable = true;
-    
     /**
      * Boot the audit trait for a model.
      */
diff --git a/tests/Feature/AuditableModelTest.php b/tests/Feature/AuditableModelTest.php
index 619ce5c..e511b96 100644
--- a/tests/Feature/AuditableModelTest.php
+++ b/tests/Feature/AuditableModelTest.php
@@ -56,7 +56,7 @@
 
     actingAs($user);
 
-    $post = new Post();
+    $post = new Post;
     $post->title = 'Hello World';
     $post->auditable = false;
     $post->save();

From 9a6c92ae9fba011747aeaa8cac9f1c8a7f8d170f Mon Sep 17 00:00:00 2001
From: GoatFreezy <alexis.vrain@gmail.com>
Date: Mon, 24 Mar 2025 23:39:56 +0100
Subject: [PATCH 3/4] Implement withoutAudits to match laravel behavior

---
 src/AuditableTrait.php                    | 27 +++++++++++++++++++++--
 src/AuditableTraitObserver.php            |  8 +++----
 src/AuditableWithDeletesTraitObserver.php |  4 ++--
 tests/Feature/AuditableModelTest.php      | 17 +++++++-------
 4 files changed, 40 insertions(+), 16 deletions(-)

diff --git a/src/AuditableTrait.php b/src/AuditableTrait.php
index ac3cc0f..5789347 100644
--- a/src/AuditableTrait.php
+++ b/src/AuditableTrait.php
@@ -9,11 +9,11 @@
 /**
  * @property Model $creator
  * @property Model $updater
- * @property bool $auditable
+ * @property bool $auditing
  */
 trait AuditableTrait
 {
-    public bool $auditable = true;
+    protected static bool $auditing = true;
 
     /**
      * Boot the audit trait for a model.
@@ -23,6 +23,29 @@ public static function bootAuditableTrait(): void
         static::observe(new AuditableTraitObserver);
     }
 
+    /**
+     * Disable auditing.
+     */
+    public static function withoutAudits(callable $callback)
+    {
+        $previousState = static::$auditing;
+        static::$auditing = false;
+
+        try {
+            return $callback();
+        } finally {
+            static::$auditing = $previousState;
+        }
+    }
+
+    /**
+     * Check is auditing is enabled.
+     */
+    public function isAuditable(): bool
+    {
+        return static::$auditing;
+    }
+
     /**
      * Get user model who created the record.
      */
diff --git a/src/AuditableTraitObserver.php b/src/AuditableTraitObserver.php
index ff637c8..26b670a 100644
--- a/src/AuditableTraitObserver.php
+++ b/src/AuditableTraitObserver.php
@@ -11,7 +11,7 @@ class AuditableTraitObserver
      */
     public function creating(Model $model): void
     {
-        if (method_exists($model, 'getCreatedByColumn') && $model->auditable) {
+        if (method_exists($model, 'getCreatedByColumn') && $model->isAuditable()) {
             $createdBy = $model->getCreatedByColumn();
 
             if (! $model->$createdBy) {
@@ -19,7 +19,7 @@ public function creating(Model $model): void
             }
         }
 
-        if (method_exists($model, 'getUpdatedByColumn') && $model->auditable) {
+        if (method_exists($model, 'getUpdatedByColumn') && $model->isAuditable()) {
             $updatedBy = $model->getUpdatedByColumn();
 
             if (! $model->$updatedBy) {
@@ -41,7 +41,7 @@ protected function getAuthenticatedUserId(): int|string|null
      */
     public function updating(Model $model): void
     {
-        if (method_exists($model, 'getUpdatedByColumn') && $model->auditable) {
+        if (method_exists($model, 'getUpdatedByColumn') && $model->isAuditable()) {
             $updatedBy = $model->getUpdatedByColumn();
 
             if (! $model->isDirty($updatedBy)) {
@@ -55,7 +55,7 @@ public function updating(Model $model): void
      */
     public function saved(Model $model): void
     {
-        if (method_exists($model, 'getUpdatedByColumn') && $model->auditable) {
+        if (method_exists($model, 'getUpdatedByColumn') && $model->isAuditable()) {
             $updatedBy = $model->getUpdatedByColumn();
 
             if ($this->getAuthenticatedUserId() && $this->getAuthenticatedUserId() != $model->$updatedBy && $model->isDirty()) {
diff --git a/src/AuditableWithDeletesTraitObserver.php b/src/AuditableWithDeletesTraitObserver.php
index 7598a9e..a0bb916 100644
--- a/src/AuditableWithDeletesTraitObserver.php
+++ b/src/AuditableWithDeletesTraitObserver.php
@@ -11,7 +11,7 @@ class AuditableWithDeletesTraitObserver
      */
     public function deleting(Model $model): void
     {
-        if (method_exists($model, 'getDeletedByColumn')) {
+        if (method_exists($model, 'getDeletedByColumn') && $model->isAuditable()) {
             $deletedBy = $model->getDeletedByColumn();
 
             $model->$deletedBy = $this->getAuthenticatedUserId();
@@ -32,7 +32,7 @@ protected function getAuthenticatedUserId(): int|string|null
      */
     public function restoring(Model $model): void
     {
-        if (method_exists($model, 'getDeletedByColumn')) {
+        if (method_exists($model, 'getDeletedByColumn') && $model->isAuditable()) {
             $deletedBy = $model->getDeletedByColumn();
 
             $model->$deletedBy = null;
diff --git a/tests/Feature/AuditableModelTest.php b/tests/Feature/AuditableModelTest.php
index e511b96..25964cb 100644
--- a/tests/Feature/AuditableModelTest.php
+++ b/tests/Feature/AuditableModelTest.php
@@ -56,16 +56,17 @@
 
     actingAs($user);
 
-    $post = new Post;
-    $post->title = 'Hello World';
-    $post->auditable = false;
-    $post->save();
+    Post::withoutAudits(function () {
+        $post = new Post;
+        $post->title = 'Hello World';
+        $post->save();
 
-    expect($post->created_by)->toBe(null);
-    expect($post->updated_by)->toBe(null);
-    expect($post->deleted_by)->toBe(null);
+        expect($post->created_by)->toBe(null);
+        expect($post->updated_by)->toBe(null);
+        expect($post->deleted_by)->toBe(null);
+    });
 
-    $post->auditable = true;
+    $post = Post::first();
     $post->title = 'Hello World 2';
     $post->save();
 

From 5a2b70b639bc9dfe18e372e0599f21cd288e1301 Mon Sep 17 00:00:00 2001
From: GoatFreezy <alexis.vrain@gmail.com>
Date: Mon, 24 Mar 2025 23:45:59 +0100
Subject: [PATCH 4/4] Fix rebase removed test

---
 tests/Feature/AuditableModelTest.php | 30 ++++++++++++++++++++++++++++
 1 file changed, 30 insertions(+)

diff --git a/tests/Feature/AuditableModelTest.php b/tests/Feature/AuditableModelTest.php
index 25964cb..acb7c2e 100644
--- a/tests/Feature/AuditableModelTest.php
+++ b/tests/Feature/AuditableModelTest.php
@@ -48,6 +48,36 @@
     expect($post->deleted_by)->toBe($user->id);
 });
 
+test('a model wont be updated if edited by another user without it being dirty', function () {
+    $user = User::create([
+        'name' => 'John Doe',
+        'email' => 'john@example.com',
+    ]);
+
+    actingAs($user);
+
+    $anotherUser = User::create([
+        'name' => 'Jane Doe',
+        'email' => 'jane@example.com',
+    ]);
+
+    DB::table('posts')->insert([
+        'title' => 'Hello World',
+        'created_by' => $anotherUser->id,
+        'updated_by' => $anotherUser->id,
+    ]);
+
+    $model = Post::first();
+
+    expect($model->created_by)->toBe($anotherUser->id);
+    expect($model->updated_by)->toBe($anotherUser->id);
+
+    $model->save();
+
+    expect($model->created_by)->toBe($anotherUser->id);
+    expect($model->updated_by)->toBe($anotherUser->id);
+});
+
 test('a post can be created without audit', function () {
     $user = User::forceCreate([
         'name' => 'John Doe',