diff --git a/README.md b/README.md index c0e246e..a044376 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,7 @@ * Logs on DB for user logins and for actions made on models * [Strictus](https://github.com/php-strictus/strictus) for enforcing local variable types * Models extending from BaseModel use soft deletes by default +* Log actions made by users with the `created_by`, `updated_by` and `deleted_by` fields. Use the `$table->userActions()` in your migrations to add these fields. ## Using the Template @@ -223,11 +224,12 @@ Inside the `exa` folder, there are a lot of classes provided by this **skeleton* ### Models -* `BaseModel` - Base class that all your models should extend, already configured with the `CommonQueries` and `LogChanges` Traits. +* `BaseModel` - Base class that all your models should extend, already configured with the `CommonQueries`, `LogChanges`, `SoftDeletes` and `UserActions` Traits. * `ChangeLog` - Model for the table that logs all changes made on other models. * `CommonQueries` - This Trait provides a lot of methods for common queries that you can use with your models. * `HasUuidField` - This Trait provides UUID field support for models that don't want the UUID to be the primary key. * `LogChanges` - This Trait provides listeners for logging changes on the models. Check the class to know how you can customize your models with the properties of this Trait. +* `UserActions` - This Trait provides listeners for logging changes made by users on the models populating the `created_by`, `updated_by` and `deleted_by` fields. Check the class to know how you can customize your models with the properties of this Trait. ### Services diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 01d874d..4fb7759 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -5,8 +5,10 @@ namespace App\Providers; use Exa\Services\SlackClient; +use Illuminate\Database\Schema\Blueprint; use Illuminate\Http\Resources\Json\JsonResource; use Illuminate\Support\ServiceProvider; +use Modules\Auth\Models\User; final class AppServiceProvider extends ServiceProvider { @@ -24,6 +26,12 @@ public function register(): void public function boot(): void { JsonResource::withoutWrapping(); + + Blueprint::macro('userActions', function () { + $this->foreignId('created_by')->nullable()->constrained(table: User::getModelTable()); + $this->foreignId('updated_by')->nullable()->constrained(table: User::getModelTable()); + $this->foreignId('deleted_by')->nullable()->constrained(table: User::getModelTable()); + }); } private function registerSlackClient(): void diff --git a/database/migrations/0001_create_users_table.php b/database/migrations/0001_create_users_table.php index 420f73a..1f30df5 100644 --- a/database/migrations/0001_create_users_table.php +++ b/database/migrations/0001_create_users_table.php @@ -27,6 +27,7 @@ public function up(): void $table->rememberToken(); $table->timestamps(); $table->softDeletes(); + $table->userActions(); }); } } diff --git a/exa/Models/BaseModel.php b/exa/Models/BaseModel.php index 65ab812..b670709 100644 --- a/exa/Models/BaseModel.php +++ b/exa/Models/BaseModel.php @@ -11,5 +11,6 @@ abstract class BaseModel extends Model { use CommonQueries, LogChanges, - SoftDeletes; + SoftDeletes, + UserActions; } diff --git a/exa/Models/UserActions.php b/exa/Models/UserActions.php new file mode 100644 index 0000000..42241c7 --- /dev/null +++ b/exa/Models/UserActions.php @@ -0,0 +1,41 @@ +disableUserActions) { + return; + } + + $model->created_by = Auth::id(); + }); + + static::updating(function (Model $model) { + if ($model->disableUserActions) { + return; + } + + $model->updated_by = Auth::id(); + }); + + static::softDeleted(function (Model $model) { + if ($model->disableUserActions) { + return; + } + + $model->deleted_by = Auth::id(); + $model->save(); + }); + } +} diff --git a/modules/Auth/Models/User.php b/modules/Auth/Models/User.php index 212d3dc..f0374f6 100644 --- a/modules/Auth/Models/User.php +++ b/modules/Auth/Models/User.php @@ -7,6 +7,7 @@ use Exa\Models\CommonQueries; use Exa\Models\HasUuidField; use Exa\Models\LogChanges; +use Exa\Models\UserActions; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\SoftDeletes; @@ -24,7 +25,8 @@ final class User extends Authenticatable implements JWTSubject HasUuidField, LogChanges, Notifiable, - SoftDeletes; + SoftDeletes, + UserActions; protected $fillable = [ 'uuid', diff --git a/modules/Auth/Models/UserLogin.php b/modules/Auth/Models/UserLogin.php index f064869..5ed5120 100644 --- a/modules/Auth/Models/UserLogin.php +++ b/modules/Auth/Models/UserLogin.php @@ -10,6 +10,8 @@ final class UserLogin extends BaseModel { public bool $disableChangeLogs = true; + public bool $disableUserActions = true; + protected $fillable = [ 'user_id', 'ip', diff --git a/tests/Feature/Auth/CreateUserTest.php b/tests/Feature/Auth/CreateUserTest.php index c68a20e..0ce0fac 100644 --- a/tests/Feature/Auth/CreateUserTest.php +++ b/tests/Feature/Auth/CreateUserTest.php @@ -6,11 +6,13 @@ use Modules\Auth\Support\Role; it('creates a new user', function () { - expect($this->actingAs(testUser(Role::ADMIN))->post('v1/users', dumbUserData())) + $user = testUser(Role::ADMIN); + expect($this->actingAs($user)->post('v1/users', dumbUserData())) ->assertCreated(); $this->assertDatabaseHas(User::getModelTable(), [ 'email' => 'john.doe@example.com', + 'created_by' => $user->id, ]); }); diff --git a/tests/Feature/Auth/DeleteUserTest.php b/tests/Feature/Auth/DeleteUserTest.php index 96b3af1..059f91a 100644 --- a/tests/Feature/Auth/DeleteUserTest.php +++ b/tests/Feature/Auth/DeleteUserTest.php @@ -7,12 +7,14 @@ it('deletes user', function () { $newUser = testUser(Role::REGULAR); + $adminUser = testUser(Role::ADMIN); - expect($this->actingAs(testUser(Role::ADMIN))->delete("v1/users/{$newUser->uuid}")) + expect($this->actingAs($adminUser)->delete("v1/users/{$newUser->uuid}")) ->assertNoContent(); $this->assertSoftDeleted(User::getModelTable(), [ 'email' => $newUser->email, + 'deleted_by' => $adminUser->id, ]); }); diff --git a/tests/Feature/Auth/UpdateUserTest.php b/tests/Feature/Auth/UpdateUserTest.php index e0a1fff..2e842c4 100644 --- a/tests/Feature/Auth/UpdateUserTest.php +++ b/tests/Feature/Auth/UpdateUserTest.php @@ -14,13 +14,15 @@ 'active' => false, ]; - expect($this->actingAs(testUser(Role::ADMIN))->put("v1/users/{$newUser->uuid}", $params)) + $adminUser = testUser(Role::ADMIN); + expect($this->actingAs($adminUser)->put("v1/users/{$newUser->uuid}", $params)) ->assertOk(); $this->assertDatabaseHas(User::getModelTable(), [ 'email' => 'test@test.com', 'role' => Role::VIEWER->value, 'active' => false, + 'updated_by' => $adminUser->id, ]); }); @@ -53,6 +55,7 @@ $this->assertDatabaseHas(User::getModelTable(), [ 'email' => 'test@test.com', + 'updated_by' => $user->id, ]); });