diff --git a/src/Fields/Field.php b/src/Fields/Field.php index fa15d98c7..dcc5ad0e9 100644 --- a/src/Fields/Field.php +++ b/src/Fields/Field.php @@ -97,6 +97,13 @@ class Field extends OrganicField implements JsonSerializable */ protected $defaultCallback; + /** + * Closure be used for the field's default value when store/update. + * + * @var callable + */ + protected $appendCallback; + /** * Closure be used to be called after the field value stored. */ @@ -190,12 +197,22 @@ public function fillAttribute(RestifyRequest $request, $model) { $this->resolveValueBeforeUpdate($request, $model); - if (isset($this->fillCallback)) { + if ($this->isHidden($request)) { + if (! isset($this->appendCallback)) { + return; + } + } + + if (! $this->isHidden($request) && isset($this->fillCallback)) { return call_user_func( $this->fillCallback, $request, $model, $this->attribute ); } + if (isset($this->appendCallback)) { + return $this->fillAttributeFromAppend($request, $model, $this->attribute); + } + if ($request->isStoreRequest() && is_callable($this->storeCallback)) { return call_user_func( $this->storeCallback, $request, $model, $this->attribute @@ -227,6 +244,28 @@ protected function fillAttributeFromRequest(RestifyRequest $request, $model, $at } } + /** + * Fill the model with the default value. + * + * @param RestifyRequest $request + * @param $model + * @param $attribute + */ + protected function fillAttributeFromAppend(RestifyRequest $request, $model, $attribute) + { + if (! isset($this->appendCallback)) { + return; + } + + if (is_callable($this->appendCallback)) { + return $model->{$attribute} = call_user_func( + $this->appendCallback, $request, $model, $attribute + ); + } + + $model->{$attribute} = $this->appendCallback; + } + /** * @return callable|string|null */ @@ -477,4 +516,33 @@ public function invokeAfter(RestifyRequest $request, $repository) call_user_func($this->afterUpdateCallback, $this->resolveAttribute($repository, $this->attribute), $this->valueBeforeUpdate, $repository, $request); } } + + /** + * Indicate whatever the input is hidden or not. + * + * @param bool $callback + * @return $this + */ + public function hidden($callback = true) + { + $this->hideFromIndex($callback) + ->hideFromShow($callback); + + $this->hiddenCallback = $callback; + + return $this; + } + + /** + * Append values when store/update. + * + * @param callable|string $value + * @return $this + */ + public function append($value) + { + $this->appendCallback = $value; + + return $this; + } } diff --git a/src/Fields/OrganicField.php b/src/Fields/OrganicField.php index a6c03b3f9..e41cfedfe 100644 --- a/src/Fields/OrganicField.php +++ b/src/Fields/OrganicField.php @@ -16,6 +16,8 @@ abstract class OrganicField extends BaseField public $readonlyCallback; + public $hiddenCallback; + public array $rules = []; public array $storingRules = []; @@ -64,6 +66,10 @@ public function hideFromIndex($callback = true) public function isShownOnShow(RestifyRequest $request, $repository): bool { + if ($this->isHidden($request)) { + return false; + } + if (is_callable($this->showOnShow)) { $this->showOnShow = call_user_func($this->showOnShow, $request, $repository); } @@ -78,6 +84,10 @@ public function isHiddenOnShow(RestifyRequest $request, $repository): bool public function isShownOnIndex(RestifyRequest $request, $repository): bool { + if ($this->isHidden($request)) { + return false; + } + return false === $this->isHiddenOnIndex($request, $repository); } @@ -158,4 +168,15 @@ public function isShownOnStore(RestifyRequest $request, $repository): bool { return ! $this->isReadonly($request); } + + public function isHidden(RestifyRequest $request) + { + return with($this->hiddenCallback, function ($callback) use ($request) { + if ($callback === true || (is_callable($callback) && call_user_func($callback, $request))) { + return true; + } + + return false; + }); + } } diff --git a/tests/Controllers/RepositoryShowControllerTest.php b/tests/Controllers/RepositoryShowControllerTest.php index 84ad5b51a..2d63b1a85 100644 --- a/tests/Controllers/RepositoryShowControllerTest.php +++ b/tests/Controllers/RepositoryShowControllerTest.php @@ -95,4 +95,39 @@ public function test_show_mergeable_repository_containes_model_attributes_and_lo ], ]); } + + public function test_repository_hidden_fields_are_not_visible() + { + factory(Post::class)->create(['title' => 'Eduard']); + + $response = $this->getJson('/restify-api/post-with-hidden-fields/1'); + + $this->assertArrayNotHasKey('user_id', $response->json('data.attributes')); + } + + public function test_repository_hidden_fields_could_not_be_updated() + { + $post = factory(Post::class)->create(['user_id' => 2, 'title' => 'Eduard']); + + $oldUserId = $post->user_id; + + $this->putJson('/restify-api/post-with-hidden-fields/1', [ + 'title' => 'Updated title', + 'user_id' => 1, + ]); + + $this->assertEquals($oldUserId, Post::find($post->id)->user_id); + } + + public function test_repository_hidden_fields_could_be_updated_through_append() + { + $post = factory(Post::class)->create(['user_id' => 2, 'title' => 'Eduard', 'category' => 'Hidden category before update.']); + + $this->putJson('/restify-api/post-with-hidden-fields/1', [ + 'title' => 'Updated title', + 'category' => 'Trying to update hidden category.', + ]); + + $this->assertEquals('Append category for a hidden field.', $post->fresh()->category); + } } diff --git a/tests/Fixtures/Post/PostWithHiddenFieldRepository.php b/tests/Fixtures/Post/PostWithHiddenFieldRepository.php new file mode 100644 index 000000000..482ccf69e --- /dev/null +++ b/tests/Fixtures/Post/PostWithHiddenFieldRepository.php @@ -0,0 +1,27 @@ +hidden(), + + Field::new('category')->hidden()->append('Append category for a hidden field.'), + + Field::new('title'), + ]; + } +} diff --git a/tests/IntegrationTest.php b/tests/IntegrationTest.php index 6c083d20e..17c1c7213 100644 --- a/tests/IntegrationTest.php +++ b/tests/IntegrationTest.php @@ -11,6 +11,7 @@ use Binaryk\LaravelRestify\Tests\Fixtures\Post\PostMergeableRepository; use Binaryk\LaravelRestify\Tests\Fixtures\Post\PostRepository; use Binaryk\LaravelRestify\Tests\Fixtures\Post\PostUnauthorizedFieldRepository; +use Binaryk\LaravelRestify\Tests\Fixtures\Post\PostWithHiddenFieldRepository; use Binaryk\LaravelRestify\Tests\Fixtures\Post\PostWithUnauthorizedFieldsRepository; use Binaryk\LaravelRestify\Tests\Fixtures\User\User; use Binaryk\LaravelRestify\Tests\Fixtures\User\UserRepository; @@ -184,6 +185,7 @@ public function loadRepositories() PostAuthorizeRepository::class, PostWithUnauthorizedFieldsRepository::class, PostUnauthorizedFieldRepository::class, + PostWithHiddenFieldRepository::class, ]); } diff --git a/tests/Unit/FieldTest.php b/tests/Unit/FieldTest.php index d4cdda291..f38703018 100644 --- a/tests/Unit/FieldTest.php +++ b/tests/Unit/FieldTest.php @@ -126,6 +126,9 @@ public function test_field_fill_callback_has_high_priority() /** * @var Field $field */ $field = Field::new('title') + ->append(function () { + return 'from append callback'; + }) ->fillCallback(function ($request, $model) { $model->title = 'from fill callback'; }) @@ -248,5 +251,52 @@ public function test_field_can_have_custom_label() $field->resolveForIndex((object) ['name' => 'Binaryk'], 'name'); $this->assertEquals('custom_label', $field->label); + $this->assertEquals('custom_label', $field->jsonSerialize()['attribute']); + } + + public function test_field_can_be_filled_from_the_append_value() + { + $request = new RepositoryStoreRequest([], []); + + $request->merge([ + 'title' => 'Title from the request.', + ]); + + $model = new class extends Model { + protected $table = 'posts'; + protected $fillable = ['title']; + }; + + /** * @var Field $field */ + $field = Field::new('title')->append('Append title'); + + $field->fillAttribute($request, $model); + + $model->save(); + + $this->assertEquals($model->title, 'Append title'); + } + + public function test_field_can_be_filled_from_the_append_callback() + { + $request = new RepositoryStoreRequest([], []); + + $request->merge([ + 'title' => 'Title from the request.', + ]); + + $model = new class extends Model { + protected $table = 'posts'; + protected $fillable = ['title']; + }; + + /** * @var Field $field */ + $field = Field::new('title')->append(fn () => 'Append title'); + + $field->fillAttribute($request, $model); + + $model->save(); + + $this->assertEquals($model->title, 'Append title'); } }