diff --git a/README.md b/README.md index 6deff50..2294d3c 100644 --- a/README.md +++ b/README.md @@ -95,7 +95,18 @@ Log::all(); However, you can get activity logs from a model by using this. ```php -$post->logs(); +$post->logs; // get all model's logs +$post->log; // get the latest model's log +$post->logs()->where('log_type', 'edit')->get(); // get filtered logs +``` + +You allowed to specify the mode for the `edit` event log. There are two modes available now `simple`/`full`. The default mode is `full`. +```php +# config/user-activity-log.php + +# only can choose either one of them +'mode' => 'full', # the 'full' mode record everything +'mode' => 'simple', # the 'simple' mode only record the modified columns ``` ## ⚙️ Configuration @@ -122,10 +133,17 @@ return [ 'create' => true, 'edit' => true, 'delete' => true, + 'retrieve' => false, 'login' => true, 'logout' => true ], + # the mode is only for 'edit' event log + # the 'simple' mode only record the modified columns + # the 'full' mode record everything + # supported mode => 'simple' / 'full' + 'mode' => 'full', + # timezone for log date time (Change to your region time zone) # UTC is always the time zone being recorded. # define your timezone to have the accurate logs time and filtered record (Especially filtered by date time) diff --git a/config/user-activity-log.php b/config/user-activity-log.php index d85cf98..3a9dea6 100644 --- a/config/user-activity-log.php +++ b/config/user-activity-log.php @@ -20,12 +20,19 @@ 'create' => true, 'edit' => true, 'delete' => true, + 'retrieve' => false, 'login' => true, 'logout' => true ], + # the mode is only for 'edit' event log + # the 'simple' mode only record the modified columns + # the 'full' mode record everything + # supported mode => 'simple' / 'full' + 'mode' => 'full', + # timezone for log date time (Change to your region time zone) # UTC is always the time zone being recorded. # define your timezone to have the accurate logs time and filtered record (Especially filtered by date time) 'timezone' => 'UTC' -]; \ No newline at end of file +]; diff --git a/src/Events/RetrievedModel.php b/src/Events/RetrievedModel.php new file mode 100644 index 0000000..031a5da --- /dev/null +++ b/src/Events/RetrievedModel.php @@ -0,0 +1,20 @@ + request()->ip(), 'user_agent' => request()->userAgent() ]; + // insert log record Log::create([ 'user_id' => auth()?->user()?->id, @@ -42,6 +44,9 @@ protected function logging() protected function isLoggable() { + // always skip retrieve event for authenticatable model. Exp. App\Models\User + if ($this->isRetrieveAuthenticatableModel()) return false; + return config("user-activity-log.events.{$this->event_name}", false); } @@ -54,4 +59,11 @@ protected function getData() { return null; } -} \ No newline at end of file + + private function isRetrieveAuthenticatableModel() + { + return + $this->event_name === 'retrieve' && + $this->event?->model instanceof Authenticatable; + } +} diff --git a/src/Listeners/ModelListener.php b/src/Listeners/ModelListener.php new file mode 100644 index 0000000..489cdf7 --- /dev/null +++ b/src/Listeners/ModelListener.php @@ -0,0 +1,16 @@ +event_name === 'create' ? - Arr::except($this->event->model->toArray(), $this->event->model->log_hidden ?? []) : + if ($this->event_name === 'create') + return Arr::except($this->event->model->toArray(), $this->event->model->log_hidden ?? []); + + $mode = config("user-activity-log.mode", 'simple'); + return $this->event_name === 'edit' && $mode === 'simple' ? + Arr::except([...$this->event->model->getChanges(), 'id' => $this->event->model->id], $this->event->model->log_hidden ?? []) : Arr::except($this->event->model->getRawOriginal(), $this->event->model->log_hidden ?? []); } -} \ No newline at end of file +} diff --git a/src/Traits/Loggable.php b/src/Traits/Loggable.php index 8b5230f..e6e10ff 100644 --- a/src/Traits/Loggable.php +++ b/src/Traits/Loggable.php @@ -6,6 +6,7 @@ use Yungts97\LaravelUserActivityLog\Events\CreatedModel; use Yungts97\LaravelUserActivityLog\Events\DeletedModel; use Yungts97\LaravelUserActivityLog\Events\UpdatedModel; +use Yungts97\LaravelUserActivityLog\Events\RetrievedModel; trait Loggable { @@ -15,14 +16,17 @@ public function initializeLoggable() 'created' => CreatedModel::class, 'updated' => UpdatedModel::class, 'deleted' => DeletedModel::class, + 'retrieved' => RetrievedModel::class, ]; } + public function log() + { + return $this->hasOne(Log::class, 'data->id', 'id')->where('table_name', $this->getTable()); + } + public function logs() { - return Log::where([ - ['table_name', $this->getTable()], - ['data->id', $this->id] - ])->get(); + return $this->hasMany(Log::class, 'data->id', 'id')->where('table_name', $this->getTable()); } -} \ No newline at end of file +} diff --git a/src/UserActivityLogEventServiceProvider.php b/src/UserActivityLogEventServiceProvider.php index 506463d..3633608 100644 --- a/src/UserActivityLogEventServiceProvider.php +++ b/src/UserActivityLogEventServiceProvider.php @@ -5,13 +5,15 @@ use Illuminate\Auth\Events\Login; use Illuminate\Auth\Events\Logout; use Yungts97\LaravelUserActivityLog\Events\CreatedModel; -use Yungts97\LaravelUserActivityLog\Events\UpdatedModel; use Yungts97\LaravelUserActivityLog\Events\DeletedModel; -use Yungts97\LaravelUserActivityLog\Listeners\LogoutListener; +use Yungts97\LaravelUserActivityLog\Events\UpdatedModel; +use Yungts97\LaravelUserActivityLog\Events\RetrievedModel; use Yungts97\LaravelUserActivityLog\Listeners\LoginListener; +use Yungts97\LaravelUserActivityLog\Listeners\LogoutListener; use Yungts97\LaravelUserActivityLog\Listeners\CreatedListener; -use Yungts97\LaravelUserActivityLog\Listeners\UpdatedListener; use Yungts97\LaravelUserActivityLog\Listeners\DeletedListener; +use Yungts97\LaravelUserActivityLog\Listeners\UpdatedListener; +use Yungts97\LaravelUserActivityLog\Listeners\RetrievedListener; use Illuminate\Foundation\Support\Providers\EventServiceProvider; @@ -32,6 +34,9 @@ class UserActivityLogEventServiceProvider extends EventServiceProvider ], DeletedModel::class => [ DeletedListener::class + ], + RetrievedModel::class => [ + RetrievedListener::class ] ]; @@ -39,4 +44,4 @@ public function boot() { parent::boot(); } -} \ No newline at end of file +} diff --git a/tests/Feature/UserActivityLogTest.php b/tests/Feature/UserActivityLogTest.php index afe9af7..8dfbe72 100644 --- a/tests/Feature/UserActivityLogTest.php +++ b/tests/Feature/UserActivityLogTest.php @@ -75,6 +75,30 @@ function it_can_log_on_delete_event() ]); } + /** @test */ + function it_can_log_on_retrieve_event() + { + config(['user-activity-log.events.retrieve' => true]); + + //user login + $user = User::first(); + Auth::login($user); + + //create a post + $newPost = new Post(['name' => 'Post 1']); + $user->posts()->save($newPost); + + //retrieve a post + $post = Post::first(); + + //checking database have the activity log record + $this->assertDatabaseHas('logs', [ + 'log_type' => 'retrieve', + 'user_id' => $user->id, + 'table_name' => 'posts' + ]); + } + /** @test */ function it_can_skip_logging() { @@ -133,9 +157,70 @@ function it_can_get_logs_from_the_model() 'table_name' => 'posts' ]); - $logs = $newPost->logs(); + $logs = $newPost->logs; $this->assertCount(1, $logs); - - $this->assertEquals($newPost->toArray(), $logs[0]->data); + + $newPost->unsetRelation('logs'); + $this->assertEquals($newPost->toArray(), $logs->first()->data); + } + + /** @test */ + function it_can_log_on_edit_event_with_simple_mode() + { + config(['user-activity-log.mode' => 'simple']); + + //user login + $user = User::first(); + Auth::login($user); + + //create a post + $newPost = new Post(['name' => 'Post 1']); + $user->posts()->save($newPost); + + //edit the post + $newPost->name = "Post 1 edited"; + $newPost->save(); + + //checking database have the activity log record + $this->assertDatabaseHas('logs', [ + 'log_type' => 'edit', + 'user_id' => $user->id, + 'table_name' => 'posts' + ]); + + //since the latest log is edit type, so just simply use latest log to test + $actualLogData = $newPost->log->data; + $expectedLogData = [...$newPost->getChanges(), 'id' => $newPost->id]; + $this->assertTrue($expectedLogData == $actualLogData); + } + + /** @test */ + function it_can_log_on_edit_event_with_full_mode() + { + config(['user-activity-log.mode' => 'full']); + + //user login + $user = User::first(); + Auth::login($user); + + //create a post + $newPost = new Post(['name' => 'Post 1']); + $user->posts()->save($newPost); + $expectedLogData = $newPost->getRawOriginal(); + + //edit the post + $newPost->name = "Post 1 edited"; + $newPost->save(); + + //checking database have the activity log record + $this->assertDatabaseHas('logs', [ + 'log_type' => 'edit', + 'user_id' => $user->id, + 'table_name' => 'posts' + ]); + + //since the latest log is edit type, so just simply use latest log to test + $actualLogData = $newPost->log->data; + $this->assertTrue($expectedLogData == $actualLogData); } -} \ No newline at end of file +}