diff --git a/src/Http/Controllers/RepositoryAttachController.php b/src/Http/Controllers/RepositoryAttachController.php index 41e701a1d..c25a5eefa 100644 --- a/src/Http/Controllers/RepositoryAttachController.php +++ b/src/Http/Controllers/RepositoryAttachController.php @@ -4,8 +4,10 @@ use Binaryk\LaravelRestify\Http\Requests\RepositoryAttachRequest; use Binaryk\LaravelRestify\Http\Requests\RestifyRequest; +use Binaryk\LaravelRestify\Repositories\Repository; use DateTime; use Illuminate\Support\Arr; +use Illuminate\Support\Str; class RepositoryAttachController extends RepositoryController { @@ -14,12 +16,16 @@ public function __invoke(RepositoryAttachRequest $request) $model = $request->findModelOrFail(); $repository = $request->repository()->allowToUpdate($request); + if (is_callable($method = $this->guessMethodName($request, $repository))) { + return call_user_func($method, $request, $repository, $model); + } + return $repository->attach( $request, $request->repositoryId, collect(Arr::wrap($request->input($request->relatedRepository))) - ->map(fn ($relatedRepositoryId) => $this->initializePivot( - $request, $model->{$request->viaRelationship ?? $request->relatedRepository}(), $relatedRepositoryId - )) + ->map(fn ($relatedRepositoryId) => $this->initializePivot( + $request, $model->{$request->viaRelationship ?? $request->relatedRepository}(), $relatedRepositoryId + )) ); } @@ -61,4 +67,21 @@ protected function initializePivot(RestifyRequest $request, $relationship, $rela return $pivot; } + + public function guessMethodName(RestifyRequest $request, Repository $repository): ?callable + { + $key = $request->relatedRepository; + + if (array_key_exists($key, $repository::getAttachers()) && is_callable($cb = $repository::getAttachers()[$key])) { + return $cb; + } + + $methodGuesser = 'attach'.Str::studly($request->relatedRepository); + + if (method_exists($repository, $methodGuesser)) { + return [$repository, $methodGuesser]; + } + + return null; + } } diff --git a/src/Repositories/Repository.php b/src/Repositories/Repository.php index eb3c422af..a848f2956 100644 --- a/src/Repositories/Repository.php +++ b/src/Repositories/Repository.php @@ -104,6 +104,13 @@ abstract class Repository implements RestifySearchable, JsonSerializable */ public static $middlewares = []; + /** + * The list of attach callable's. + * + * @var array + */ + public static $attachers = []; + /** * Get the underlying model instance for the resource. * @@ -743,4 +750,9 @@ public static function collectMiddlewares(RestifyRequest $request): ?Collection { return collect(static::$middlewares); } + + public static function getAttachers(): array + { + return static::$attachers; + } } diff --git a/tests/Controllers/RepositoryAttachInterceptorTest.php b/tests/Controllers/RepositoryAttachInterceptorTest.php new file mode 100644 index 000000000..55c2dc590 --- /dev/null +++ b/tests/Controllers/RepositoryAttachInterceptorTest.php @@ -0,0 +1,22 @@ +create(); + $user = $this->mockUsers()->first(); + + $this->postJson('restify-api/roles/'.$role->id.'/attach/users', [ + 'users' => $user->id, + ]) + ->assertCreated(); + + $this->assertDatabaseCount('model_has_roles', 1); + } +} diff --git a/tests/Factories/Roleactory.php b/tests/Factories/Roleactory.php new file mode 100644 index 000000000..e631a95cb --- /dev/null +++ b/tests/Factories/Roleactory.php @@ -0,0 +1,22 @@ +define(Role::class, function (Faker $faker) { + return [ + 'name' => $faker->word, + 'guard_name' => 'web', + ]; +}); diff --git a/tests/Fixtures/Role/ModelHasRole.php b/tests/Fixtures/Role/ModelHasRole.php new file mode 100644 index 000000000..9845b2ad7 --- /dev/null +++ b/tests/Fixtures/Role/ModelHasRole.php @@ -0,0 +1,14 @@ + $model->id, + 'model_type' => User::class, + 'model_id' => $request->get('users'), + ]); + + return $this->response()->created(); + } +} diff --git a/tests/IntegrationTest.php b/tests/IntegrationTest.php index 4f69f6677..3c4a4d59f 100644 --- a/tests/IntegrationTest.php +++ b/tests/IntegrationTest.php @@ -13,6 +13,7 @@ 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\Role\RoleRepository; use Binaryk\LaravelRestify\Tests\Fixtures\User\User; use Binaryk\LaravelRestify\Tests\Fixtures\User\UserRepository; use Illuminate\Contracts\Auth\Authenticatable; @@ -188,6 +189,7 @@ public function loadRepositories() PostWithUnauthorizedFieldsRepository::class, PostUnauthorizedFieldRepository::class, PostWithHiddenFieldRepository::class, + RoleRepository::class, ]); } diff --git a/tests/Migrations/2019_12_22_000006_create_roles_table.php b/tests/Migrations/2019_12_22_000006_create_roles_table.php new file mode 100644 index 000000000..28c3f6865 --- /dev/null +++ b/tests/Migrations/2019_12_22_000006_create_roles_table.php @@ -0,0 +1,49 @@ +bigIncrements('id'); + $table->string('name'); + $table->string('guard_name'); + $table->timestamps(); + }); + + Schema::create('model_has_roles', function (Blueprint $table) { + $table->unsignedBigInteger('role_id'); + + $table->string('model_type'); + $table->unsignedBigInteger('model_id'); + $table->index(['model_id', 'model_type'], 'model_has_roles_model_id_model_type_index'); + + $table->foreign('role_id') + ->references('id') + ->on('roles') + ->onDelete('cascade'); + + $table->primary(['role_id', 'model_id', 'model_type'], + 'model_has_roles_role_model_type_primary'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('roles'); + } +}