From ea9b39129ab0d7649ba7eeac89609dbdf85d8e70 Mon Sep 17 00:00:00 2001 From: Christopher Gammie Date: Sat, 9 Mar 2024 09:53:30 +0000 Subject: [PATCH] fix: throw if soft delete is prevented by listener --- src/Drivers/SoftDeleteDriver.php | 32 ++++++++++++++++--------- tests/lib/Acceptance/SoftDeleteTest.php | 17 +++++++++++++ 2 files changed, 38 insertions(+), 11 deletions(-) diff --git a/src/Drivers/SoftDeleteDriver.php b/src/Drivers/SoftDeleteDriver.php index 4e174ec..c728892 100644 --- a/src/Drivers/SoftDeleteDriver.php +++ b/src/Drivers/SoftDeleteDriver.php @@ -23,6 +23,7 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; use InvalidArgumentException; +use RuntimeException; class SoftDeleteDriver extends StandardDriver { @@ -80,17 +81,26 @@ public function persist(Model $model): bool * * @see https://github.com/cloudcreativity/laravel-json-api/issues/371 */ - if ($this->willSoftDelete($model)) { - $column = $model->getDeletedAtColumn(); - // save the original date so we can put it back later on. - $deletedAt = $model->{$column}; - // delete the record so that deleting and deleted events get fired. - $response = $model->delete(); // capture the response - // if everything worked out then update the $deletedAt - if ($response === false){ - // apply the original date back before saving, so that we keep date provided by the client. - $model->{$column} = $deletedAt; - } + if ($this->willSoftDelete($model)) { + assert(method_exists($model, 'getDeletedAtColumn')); + $column = $model->getDeletedAtColumn(); + // save the original date so we can put it back later on. + $deletedAt = $model->{$column}; + // delete the record so that deleting and deleted events get fired. + $response = $model->delete(); // capture the response + + // if a listener prevented the delete from happening, we need to throw as we are in an invalid state. + // developers should prevent this scenario from happening either through authorization or validation. + if ($response === false) { + throw new RuntimeException(sprintf( + 'Failed to soft delete model - %s:%s', + $model::class, + $model->getKey(), + )); + } + + // apply the original date back before saving, so that we keep date provided by the client. + $model->{$column} = $deletedAt; } return (bool) $model->save(); diff --git a/tests/lib/Acceptance/SoftDeleteTest.php b/tests/lib/Acceptance/SoftDeleteTest.php index 46344a6..d74be32 100644 --- a/tests/lib/Acceptance/SoftDeleteTest.php +++ b/tests/lib/Acceptance/SoftDeleteTest.php @@ -332,6 +332,23 @@ public function testItDoesNotSoftDeleteOnUpdate(): void ])); } + public function testItDoesNotSoftDeleteOnUpdateIfListenerReturnsFalse(): void + { + $post = Post::factory()->create(['deleted_at' => null]); + + $data = ['deletedAt' => now()->toJSON(), 'title' => 'Hello World!']; + + Post::deleting(fn() => false); + + $this->expectException(\RuntimeException::class); + $this->expectExceptionMessage('Failed to soft delete model - App\Models\Post:' . $post->getKey()); + + $this->schema + ->repository() + ->update($post) + ->store($data); + } + public function testItRestores(): void { $restored = false;