diff --git a/docs/docs/4.0/actions/actions.md b/docs/docs/4.0/actions/actions.md index 1d0b60488..57bb0d4c3 100644 --- a/docs/docs/4.0/actions/actions.md +++ b/docs/docs/4.0/actions/actions.md @@ -169,7 +169,23 @@ You can apply any filter or eager loadings as for an usual request: POST: api/api/restify/posts/actions?action=publish-posts-action&id=1&filters= ``` -This will apply the match for the `id = 1` and `filtetr` along with the match for the `repositories` payload you're sending. +This will apply the match for the `id = 1` and `filter` along with the match for the `repositories` payload you're sending. + +### Modify query + +Similar with the way we can modify the query applied to the repository, we can do the same by adding the `indexQuery` method on the action: + +```php +class PublishPostAction extends Action +{ + public static function indexQuery(RestifyRequest $request, $query) + { + $query->whereNotNull('published_at'); + } + + //... +} +``` ## All @@ -179,8 +195,6 @@ Sometimes you may need to apply an action for all models. For this you can send: repositories: 'all' ``` -## Chunk - Under the hood Restify will take by 200 chunks entries from the database and the handle method for these in a DB transaction. You are free to modify this default number of chunks: ```php @@ -200,7 +214,6 @@ public function actions(RestifyRequest $request) } ``` - And available actions only for a specific repository id could be listed like: ```http request @@ -277,3 +290,59 @@ class DisableProfileAction extends Action //... } ``` + +## Action Log + +It is often useful to view a log of the actions that have been run against a model, or seeing when the model was updated, deleted or created (and by whom). Thankfully, Restify makes it a breeze to add an action log to a model by attaching the `Binaryk\LaravelRestify\Models\Concerns\HasActionLogs` trait to the repository's corresponding Eloquent model. + +Having `HasActionLogs` trait attached to your model, all of the actions and CRUD operations will be logged into the database into the `action_logs` table. + +You can display them by attaching to the repository related for example: + +```php +// PostRepository.php +use Binaryk\LaravelRestify\Fields\MorphToMany; +use Binaryk\LaravelRestify\Repositories\ActionLogRepository; + +public static function related(): array +{ + return [ + 'logs' => MorphToMany::make('actionLogs', 'actionLogs', ActionLogRepository::class), + ]; +} +``` + +So now you can call the posts with logs `api/restify/posts/1?related=logs`, and it will return you the list of actions performed for posts: + +```json +[ + { + "id": "1", + "type": "action_logs", + "attributes": { + "batch_id": "048686bb-cd22-41a7-a6db-3eba29678d74", + "user_id": "1", + "name": "Stored", + "actionable_type": "App\\Models\\Post", + "actionable_id": "1", + "target_type": "App\\Models\\Post", + "target_id": "1", + "model_type": "App\\Models\\Post", + "model_id": "1", + "fields": "", + "status": "finished", + "original": "", + "changes": [], + "exception": "" + }, + "meta": { + "authorizedToShow": true, + "authorizedToStore": true, + "authorizedToUpdate": true, + "authorizedToDelete": true + } + } +] +``` + +Definitely you can use your own `ActionLogRepository` to represent the data returned, maybe you prefer to represent the user details or something else. diff --git a/src/Repositories/ActionLogRepository.php b/src/Repositories/ActionLogRepository.php index 36cd295a9..c44ee70f1 100644 --- a/src/Repositories/ActionLogRepository.php +++ b/src/Repositories/ActionLogRepository.php @@ -12,9 +12,20 @@ class ActionLogRepository extends Repository public function fields(RestifyRequest $request) { return [ - field('actionable_type'), - - field('actionable_id'), + field('batch_id')->readonly(), + field('user_id')->readonly(), + field('name')->readonly(), + field('actionable_type')->readonly(), + field('actionable_id')->readonly(), + field('target_type')->readonly(), + field('target_id')->readonly(), + field('model_type')->readonly(), + field('model_id')->readonly(), + field('fields')->readonly(), + field('status')->readonly(), + field('original')->readonly(), + field('changes')->readonly(), + field('exception')->readonly(), ]; } } diff --git a/src/Repositories/Repository.php b/src/Repositories/Repository.php index 4c2a6c82a..dfefb80aa 100644 --- a/src/Repositories/Repository.php +++ b/src/Repositories/Repository.php @@ -2,7 +2,6 @@ namespace Binaryk\LaravelRestify\Repositories; -use Binaryk\LaravelRestify\Contracts\ActionLogable; use Binaryk\LaravelRestify\Contracts\RestifySearchable; use Binaryk\LaravelRestify\Controllers\RestResponse; use Binaryk\LaravelRestify\Eager\Related; @@ -15,6 +14,7 @@ use Binaryk\LaravelRestify\Filter; use Binaryk\LaravelRestify\Http\Requests\RepositoryStoreBulkRequest; use Binaryk\LaravelRestify\Http\Requests\RestifyRequest; +use Binaryk\LaravelRestify\Models\Concerns\HasActionLogs; use Binaryk\LaravelRestify\Models\CreationAware; use Binaryk\LaravelRestify\Repositories\Concerns\InteractsWithAttachers; use Binaryk\LaravelRestify\Repositories\Concerns\Mockable; @@ -705,7 +705,7 @@ public function store(RestifyRequest $request) } } - if ($this->resource instanceof ActionLogable) { + if (in_array(HasActionLogs::class, class_uses_recursive($this->resource))) { Restify::actionLog() ->forRepositoryStored($this->resource, $request->user(), $dirty) ->save(); @@ -765,7 +765,7 @@ public function update(RestifyRequest $request, $repositoryId) static::fillFields($request, $this->resource, $fields); - if ($this->resource instanceof ActionLogable) { + if (in_array(HasActionLogs::class, class_uses_recursive($this->resource))) { Restify::actionLog() ->forRepositoryUpdated($this->resource, $request->user()) ->save(); @@ -842,7 +842,7 @@ public function detach(RestifyRequest $request, $repositoryId, Collection $pivot public function destroy(RestifyRequest $request, $repositoryId) { $status = DB::transaction(function () use ($request) { - if ($this->resource instanceof ActionLogable) { + if (in_array(HasActionLogs::class, class_uses_recursive($this->resource))) { Restify::actionLog() ->forRepositoryDestroy($this->resource, $request->user()) ->save(); diff --git a/tests/Controllers/RepositoryIndexControllerTest.php b/tests/Controllers/RepositoryIndexControllerTest.php index 67672daf3..705aee725 100644 --- a/tests/Controllers/RepositoryIndexControllerTest.php +++ b/tests/Controllers/RepositoryIndexControllerTest.php @@ -120,7 +120,7 @@ public function test_using_custom_related_casts() ]); } - public function test_repository_with_deep_relations() + public function test_repository_with_nested_relations() { CompanyRepository::partialMock() ->expects('related') @@ -139,6 +139,7 @@ public function test_repository_with_deep_relations() }); $response = $this->getJson(CompanyRepository::uriKey().'?related=users.posts') + ->dump() ->assertOk(); $this->assertCount(1, $response->json('data.0.relationships.users')); diff --git a/tests/Fixtures/Post/Post.php b/tests/Fixtures/Post/Post.php index c220a9abc..a041f7fad 100644 --- a/tests/Fixtures/Post/Post.php +++ b/tests/Fixtures/Post/Post.php @@ -2,14 +2,13 @@ namespace Binaryk\LaravelRestify\Tests\Fixtures\Post; -use Binaryk\LaravelRestify\Contracts\ActionLogable; use Binaryk\LaravelRestify\Contracts\RestifySearchable; use Binaryk\LaravelRestify\Models\Concerns\HasActionLogs; use Binaryk\LaravelRestify\Tests\Fixtures\User\User; use Binaryk\LaravelRestify\Traits\InteractWithSearch; use Illuminate\Database\Eloquent\Model; -class Post extends Model implements RestifySearchable, ActionLogable +class Post extends Model implements RestifySearchable { use InteractWithSearch, HasActionLogs; diff --git a/tests/Fixtures/Post/PublishPostAction.php b/tests/Fixtures/Post/PublishPostAction.php index b118f0107..cf86be098 100644 --- a/tests/Fixtures/Post/PublishPostAction.php +++ b/tests/Fixtures/Post/PublishPostAction.php @@ -4,6 +4,7 @@ use Binaryk\LaravelRestify\Actions\Action; use Binaryk\LaravelRestify\Http\Requests\ActionRequest; +use Binaryk\LaravelRestify\Http\Requests\RestifyRequest; use Illuminate\Http\JsonResponse; use Illuminate\Support\Collection; @@ -11,6 +12,11 @@ class PublishPostAction extends Action { public static $applied = []; + public static function indexQuery(RestifyRequest $request, $query) + { + $query->whereNotNull('published_at'); + } + public function handle(ActionRequest $request, Collection $models): JsonResponse { static::$applied[] = $models; diff --git a/tests/Repositories/ActionLogRepositoryTest.php b/tests/Repositories/ActionLogRepositoryTest.php new file mode 100644 index 000000000..40f5fc8a5 --- /dev/null +++ b/tests/Repositories/ActionLogRepositoryTest.php @@ -0,0 +1,50 @@ +authenticate(); + + $log = ActionLog::forRepositoryStored( + factory(User::class)->create(), + $this->authenticatedAs + ); + + $log->save(); + + $this->getJson(ActionLogRepository::uriKey()) + ->assertOk() + ->assertJsonStructure([ + 'data' => [ + [ + 'id', + 'type', + 'attributes' => [ + 'name', + 'user_id', + 'actionable_type', + 'actionable_id', + ], + ], + ], + ])->json('data'); + } +}