From d6ed4ec0e16319ca81b59dcc7789fd3410d0199e Mon Sep 17 00:00:00 2001 From: Eduard Lupacescu Date: Wed, 20 May 2020 00:29:45 +0300 Subject: [PATCH 1/3] Attach related repositories --- routes/api.php | 21 +++--- .../RepositoryAttachController.php | 65 ++++++++++++++++++ .../Requests/InteractWithRepositories.php | 62 ++++++++++++++++- src/Http/Requests/RepositoryAttachRequest.php | 8 +++ src/Http/Requests/RestifyRequest.php | 1 + src/Repositories/Repository.php | 13 ++++ .../RepositoryFilterControllerTest.php | 2 - .../RepositoryPivotControllerTest.php | 66 +++++++++++++++++++ tests/Factories/CompanyFactory.php | 21 ++++++ tests/Fixtures/Company/Company.php | 23 +++++++ tests/Fixtures/Company/CompanyRepository.php | 10 +++ tests/Fixtures/User/User.php | 8 +++ tests/IntegrationTest.php | 2 + ...2_22_000005_create_Company_user__table.php | 33 ++++++++++ ...19_12_22_000005_create_companies_table.php | 32 +++++++++ 15 files changed, 354 insertions(+), 13 deletions(-) create mode 100644 src/Http/Controllers/RepositoryAttachController.php create mode 100644 src/Http/Requests/RepositoryAttachRequest.php create mode 100644 tests/Controllers/RepositoryPivotControllerTest.php create mode 100644 tests/Factories/CompanyFactory.php create mode 100644 tests/Fixtures/Company/Company.php create mode 100644 tests/Fixtures/Company/CompanyRepository.php create mode 100644 tests/Migrations/2019_12_22_000005_create_Company_user__table.php create mode 100644 tests/Migrations/2019_12_22_000005_create_companies_table.php diff --git a/routes/api.php b/routes/api.php index dec06ba36..ca1fc4eb6 100644 --- a/routes/api.php +++ b/routes/api.php @@ -4,21 +4,26 @@ use Binaryk\LaravelRestify\Http\Controllers\RepositoryDestroyController; use Binaryk\LaravelRestify\Http\Controllers\RepositoryFilterController; use Binaryk\LaravelRestify\Http\Controllers\RepositoryIndexController; +use Binaryk\LaravelRestify\Http\Controllers\RepositoryAttachController; use Binaryk\LaravelRestify\Http\Controllers\RepositoryShowController; use Binaryk\LaravelRestify\Http\Controllers\RepositoryStoreController; use Binaryk\LaravelRestify\Http\Controllers\RepositoryUpdateController; use Illuminate\Support\Facades\Route; // Global Search... -Route::get('/search', '\\'.GlobalSearchController::class); +Route::get('/search', '\\' . GlobalSearchController::class); // Filters -Route::get('/{repository}/filters', '\\'.RepositoryFilterController::class); +Route::get('/{repository}/filters', '\\' . RepositoryFilterController::class); // API CRUD -Route::get('/{repository}', '\\'.RepositoryIndexController::class); -Route::post('/{repository}', '\\'.RepositoryStoreController::class); -Route::get('/{repository}/{repositoryId}', '\\'.RepositoryShowController::class); -Route::patch('/{repository}/{repositoryId}', '\\'.RepositoryUpdateController::class); -Route::put('/{repository}/{repositoryId}', '\\'.RepositoryUpdateController::class); -Route::delete('/{repository}/{repositoryId}', '\\'.RepositoryDestroyController::class); +Route::get('/{repository}', '\\' . RepositoryIndexController::class); +Route::post('/{repository}', '\\' . RepositoryStoreController::class); +Route::get('/{repository}/{repositoryId}', '\\' . RepositoryShowController::class); +Route::patch('/{repository}/{repositoryId}', '\\' . RepositoryUpdateController::class); +Route::put('/{repository}/{repositoryId}', '\\' . RepositoryUpdateController::class); +Route::delete('/{repository}/{repositoryId}', '\\' . RepositoryDestroyController::class); + + +// Attach related repository id +Route::post('/{repository}/{repositoryId}/attach/{relatedRepository}', '\\' . RepositoryAttachController::class); diff --git a/src/Http/Controllers/RepositoryAttachController.php b/src/Http/Controllers/RepositoryAttachController.php new file mode 100644 index 000000000..a017d42ad --- /dev/null +++ b/src/Http/Controllers/RepositoryAttachController.php @@ -0,0 +1,65 @@ +findModelOrFail(); + $repository = $request->repository()->allowToUpdate($request); + + 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 + )) + ); + } + + /** + * Initialize a fresh pivot model for the relationship. + * + * @param RestifyRequest $request + * @param $relationship + * @return mixed + * @throws \Binaryk\LaravelRestify\Exceptions\Eloquent\EntityNotFoundException + * @throws \Binaryk\LaravelRestify\Exceptions\UnauthorizedException + */ + protected function initializePivot(RestifyRequest $request, $relationship, $relatedKey) + { + $parentKey = $request->repositoryId; + + $parentKeyName = $relationship->getParentKeyName(); + $relatedKeyName = $relationship->getRelatedKeyName(); + + if ($parentKeyName !== $request->model()->getKeyName()) { + $parentKey = $request->findModelOrFail()->{$parentKeyName}; + } + + if ($relatedKeyName !== ($request->newRelatedRepository()::newModel())->getKeyName()) { + $relatedKey = $request->findRelatedModelOrFail()->{$relatedKeyName}; + } + + ($pivot = $relationship->newPivot())->forceFill([ + $relationship->getForeignPivotKeyName() => $parentKey, + $relationship->getRelatedPivotKeyName() => $relatedKey, + ]); + + if ($relationship->withTimestamps) { + $pivot->forceFill([ + $relationship->createdAt() => new DateTime, + $relationship->updatedAt() => new DateTime, + ]); + } + + return $pivot; + } + +} diff --git a/src/Http/Requests/InteractWithRepositories.php b/src/Http/Requests/InteractWithRepositories.php index 991f2cee9..cf7009273 100644 --- a/src/Http/Requests/InteractWithRepositories.php +++ b/src/Http/Requests/InteractWithRepositories.php @@ -44,7 +44,7 @@ public function repository($key = null): ?Repository ]), 404); } - if (! $repository::authorizedToUseRepository($this)) { + if (!$repository::authorizedToUseRepository($this)) { throw new UnauthorizedException(__('Unauthorized to view repository :name. See "allowRestify" policy.', [ 'name' => $repository, ]), 403); @@ -114,7 +114,7 @@ public function newRepositoryWith($model, $uriKey = null) */ public function newQueryWithoutScopes($uriKey = null) { - if (! $this->isViaRepository()) { + if (!$this->isViaRepository()) { return $this->model($uriKey)->newQueryWithoutScopes(); } @@ -152,15 +152,71 @@ public function findModelQuery($repositoryId = null, $uriKey = null) ); } + public function findModelOrFail($id = null) + { + if ($id) { + return $this->findModelQuery($id)->firstOrFail(); + } + + return once(function () { + return $this->findModelQuery()->firstOrFail(); + }); + } + + public function findRelatedModelOrFail() + { + return once(function () { + return $this->findRelatedQuery()->firstOrFail(); + }); + } + + + public function findRelatedQuery($relatedRepository = null, $relatedRepositoryId = null) + { + return $this->repository($relatedRepository ?? request('relatedRepository'))::newModel() + ->newQueryWithoutScopes() + ->whereKey($relatedRepositoryId ?? request('relatedRepositoryId')); + } + + + protected function findPivot(RestifyRequest $request, $model) + { + $pivot = $model->{$request->relatedRepository}()->getPivotAccessor(); + + return $model->{$request->viaRelationship}() + ->withoutGlobalScopes() + ->lockForUpdate() + ->findOrFail($request->relatedRepositoryId)->{$pivot}; + } + public function viaParentModel() { $parent = $this->repository($this->viaRepository); - return once(fn () => $parent::newModel()->newQueryWithoutScopes()->whereKey($this->viaRepositoryId)->firstOrFail()); + return once(fn() => $parent::newModel()->newQueryWithoutScopes()->whereKey($this->viaRepositoryId)->firstOrFail()); } public function viaQuery() { return $this->viaParentModel()->{$this->viaRelationship}(); } + + /** + * Get a new instance of the "related" resource being requested. + * + * @return Repository + */ + public function newRelatedRepository() + { + $resource = $this->relatedRepository(); + + return new $resource($resource::newModel()); + } + + public function relatedRepository() + { + return Restify::repositoryForKey($this->relatedRepository); + } + + } diff --git a/src/Http/Requests/RepositoryAttachRequest.php b/src/Http/Requests/RepositoryAttachRequest.php new file mode 100644 index 000000000..1586752ae --- /dev/null +++ b/src/Http/Requests/RepositoryAttachRequest.php @@ -0,0 +1,8 @@ +success(); } + public function attach(RestifyRequest $request, $repositoryId, Collection $pivots) + { + DB::transaction(function () use ($request, $pivots) { + return $pivots->map(fn($pivot) => $pivot->forceFill($request->except($request->relatedRepository))) + ->map(fn($pivot) => $pivot->save()); + }); + + return $this->response() + ->data($pivots) + ->created(); + } + public function destroy(RestifyRequest $request, $repositoryId) { $status = DB::transaction(function () { diff --git a/tests/Controllers/RepositoryFilterControllerTest.php b/tests/Controllers/RepositoryFilterControllerTest.php index f5c535799..b4a24c6d7 100644 --- a/tests/Controllers/RepositoryFilterControllerTest.php +++ b/tests/Controllers/RepositoryFilterControllerTest.php @@ -16,7 +16,6 @@ public function test_can_get_available_filters() $response = $this ->withoutExceptionHandling() ->getJson('restify-api/posts/filters') - ->dump() ->assertStatus(200); $this->assertCount(3, $response->json('data')); @@ -39,7 +38,6 @@ public function test_the_boolean_filter_is_applied() $response = $this ->withoutExceptionHandling() ->getJson('restify-api/posts?filters='.$filters) - ->dump() ->assertStatus(200); $this->assertCount(1, $response->json('data')); diff --git a/tests/Controllers/RepositoryPivotControllerTest.php b/tests/Controllers/RepositoryPivotControllerTest.php new file mode 100644 index 000000000..b6262b5ea --- /dev/null +++ b/tests/Controllers/RepositoryPivotControllerTest.php @@ -0,0 +1,66 @@ +mockUsers(2)->first(); + $company = factory(Company::class)->create(); + + $response = $this->postJson('restify-api/companies/' . $company->id . '/attach/users', [ + 'users' => $user->id, + 'is_admin' => true, + ]) + ->assertStatus(201); + + $response->assertJsonFragment([ + 'company_id' => '1', + 'user_id' => $user->id, + 'is_admin' => true, + ]); + } + + public function test_attach_multiple_users_to_a_company() + { + $user = $this->mockUsers(2)->first(); + $company = factory(Company::class)->create(); + $usersFromCompany = $this->getJson('/restify-api/users?viaRepository=companies&viaRepositoryId=1&viaRelationship=users'); + $this->assertCount(0, $usersFromCompany->json('data')); + + $response = $this->postJson('restify-api/companies/' . $company->id . '/attach/users', [ + 'users' => [1, 2], + 'is_admin' => true, + ]) + ->assertStatus(201); + + $response->assertJsonFragment([ + 'company_id' => '1', + 'user_id' => $user->id, + 'is_admin' => true, + ]); + + $usersFromCompany = $this->getJson('/restify-api/users?viaRepository=companies&viaRepositoryId=1&viaRelationship=users'); + $this->assertCount(2, $usersFromCompany->json('data')); + } + + public function test_after_attach_a_user_to_company_number_of_users_increased() + { + $user = $this->mockUsers()->first(); + $company = factory(Company::class)->create(); + + $usersFromCompany = $this->getJson('/restify-api/users?viaRepository=companies&viaRepositoryId=1&viaRelationship=users'); + $this->assertCount(0, $usersFromCompany->json('data')); + + $this->postJson('restify-api/companies/' . $company->id . '/attach/users', [ + 'users' => $user->id, + ]); + + $usersFromCompany = $this->getJson('/restify-api/users?viaRepository=companies&viaRepositoryId=1&viaRelationship=users'); + $this->assertCount(1, $usersFromCompany->json('data')); + } +} diff --git a/tests/Factories/CompanyFactory.php b/tests/Factories/CompanyFactory.php new file mode 100644 index 000000000..e0e43d8f3 --- /dev/null +++ b/tests/Factories/CompanyFactory.php @@ -0,0 +1,21 @@ +define(Company::class, function (Faker $faker) { + return [ + 'name' => $faker->name, + ]; +}); diff --git a/tests/Fixtures/Company/Company.php b/tests/Fixtures/Company/Company.php new file mode 100644 index 000000000..6caf50442 --- /dev/null +++ b/tests/Fixtures/Company/Company.php @@ -0,0 +1,23 @@ +belongsToMany(User::class, 'company_user', 'company_id', 'user_id') + ->withPivot([ + 'is_admin' + ]) + ->withTimestamps(); + } +} diff --git a/tests/Fixtures/Company/CompanyRepository.php b/tests/Fixtures/Company/CompanyRepository.php new file mode 100644 index 000000000..f6b59e693 --- /dev/null +++ b/tests/Fixtures/Company/CompanyRepository.php @@ -0,0 +1,10 @@ +hasMany(Post::class); } + public function companies() + { + return $this->belongsToMany(Company::class, 'company_user', 'user_id', 'company_id')->withPivot([ + 'is_admin' + ])->withTimestamps(); + } + /** * Set default test values. */ diff --git a/tests/IntegrationTest.php b/tests/IntegrationTest.php index c61a275a9..6c083d20e 100644 --- a/tests/IntegrationTest.php +++ b/tests/IntegrationTest.php @@ -5,6 +5,7 @@ use Binaryk\LaravelRestify\Exceptions\RestifyHandler; use Binaryk\LaravelRestify\LaravelRestifyServiceProvider; use Binaryk\LaravelRestify\Restify; +use Binaryk\LaravelRestify\Tests\Fixtures\Company\CompanyRepository; use Binaryk\LaravelRestify\Tests\Fixtures\Post\Post; use Binaryk\LaravelRestify\Tests\Fixtures\Post\PostAuthorizeRepository; use Binaryk\LaravelRestify\Tests\Fixtures\Post\PostMergeableRepository; @@ -178,6 +179,7 @@ public function loadRepositories() Restify::repositories([ UserRepository::class, PostRepository::class, + CompanyRepository::class, PostMergeableRepository::class, PostAuthorizeRepository::class, PostWithUnauthorizedFieldsRepository::class, diff --git a/tests/Migrations/2019_12_22_000005_create_Company_user__table.php b/tests/Migrations/2019_12_22_000005_create_Company_user__table.php new file mode 100644 index 000000000..84575f583 --- /dev/null +++ b/tests/Migrations/2019_12_22_000005_create_Company_user__table.php @@ -0,0 +1,33 @@ +foreignId('company_id')->constrained('companies'); + $table->foreignId('user_id')->constrained('users'); + $table->boolean('is_admin')->default(false); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('company_user'); + } +} diff --git a/tests/Migrations/2019_12_22_000005_create_companies_table.php b/tests/Migrations/2019_12_22_000005_create_companies_table.php new file mode 100644 index 000000000..7c2c2886b --- /dev/null +++ b/tests/Migrations/2019_12_22_000005_create_companies_table.php @@ -0,0 +1,32 @@ +id(); + $table->string('name'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('companies'); + } +} From a19f466a60327f548f2777accea160f92cc83ceb Mon Sep 17 00:00:00 2001 From: Eduard Lupacescu Date: Wed, 20 May 2020 00:50:08 +0300 Subject: [PATCH 2/3] Detach items --- routes/api.php | 2 + .../RepositoryDetachController.php | 65 +++++++++++++++++++ src/Http/Requests/RepositoryDetachRequest.php | 8 +++ src/Repositories/Repository.php | 55 +++++++++------- ...php => RepositoryAttachControllerTest.php} | 2 +- .../RepositoryDetachControllerTest.php | 43 ++++++++++++ 6 files changed, 152 insertions(+), 23 deletions(-) create mode 100644 src/Http/Controllers/RepositoryDetachController.php create mode 100644 src/Http/Requests/RepositoryDetachRequest.php rename tests/Controllers/{RepositoryPivotControllerTest.php => RepositoryAttachControllerTest.php} (97%) create mode 100644 tests/Controllers/RepositoryDetachControllerTest.php diff --git a/routes/api.php b/routes/api.php index ca1fc4eb6..d81e86b6b 100644 --- a/routes/api.php +++ b/routes/api.php @@ -2,6 +2,7 @@ use Binaryk\LaravelRestify\Http\Controllers\GlobalSearchController; use Binaryk\LaravelRestify\Http\Controllers\RepositoryDestroyController; +use Binaryk\LaravelRestify\Http\Controllers\RepositoryDetachController; use Binaryk\LaravelRestify\Http\Controllers\RepositoryFilterController; use Binaryk\LaravelRestify\Http\Controllers\RepositoryIndexController; use Binaryk\LaravelRestify\Http\Controllers\RepositoryAttachController; @@ -27,3 +28,4 @@ // Attach related repository id Route::post('/{repository}/{repositoryId}/attach/{relatedRepository}', '\\' . RepositoryAttachController::class); +Route::post('/{repository}/{repositoryId}/detach/{relatedRepository}', '\\' . RepositoryDetachController::class); diff --git a/src/Http/Controllers/RepositoryDetachController.php b/src/Http/Controllers/RepositoryDetachController.php new file mode 100644 index 000000000..a7c366c12 --- /dev/null +++ b/src/Http/Controllers/RepositoryDetachController.php @@ -0,0 +1,65 @@ +findModelOrFail(); + $repository = $request->repository()->allowToUpdate($request); + + return $repository->detach( + $request, $request->repositoryId, + collect(Arr::wrap($request->input($request->relatedRepository))) + ->map(fn($relatedRepositoryId) => $this->initializePivot( + $request, $model->{$request->viaRelationship ?? $request->relatedRepository}(), $relatedRepositoryId + )) + ); + } + + /** + * Initialize a fresh pivot model for the relationship. + * + * @param RestifyRequest $request + * @param $relationship + * @return mixed + * @throws \Binaryk\LaravelRestify\Exceptions\Eloquent\EntityNotFoundException + * @throws \Binaryk\LaravelRestify\Exceptions\UnauthorizedException + */ + protected function initializePivot(RestifyRequest $request, $relationship, $relatedKey) + { + $parentKey = $request->repositoryId; + + $parentKeyName = $relationship->getParentKeyName(); + $relatedKeyName = $relationship->getRelatedKeyName(); + + if ($parentKeyName !== $request->model()->getKeyName()) { + $parentKey = $request->findModelOrFail()->{$parentKeyName}; + } + + if ($relatedKeyName !== ($request->newRelatedRepository()::newModel())->getKeyName()) { + $relatedKey = $request->findRelatedModelOrFail()->{$relatedKeyName}; + } + + ($pivot = $relationship->newPivot())->forceFill([ + $relationship->getForeignPivotKeyName() => $parentKey, + $relationship->getRelatedPivotKeyName() => $relatedKey, + ]); + + if ($relationship->withTimestamps) { + $pivot->forceFill([ + $relationship->createdAt() => new DateTime, + $relationship->updatedAt() => new DateTime, + ]); + } + + return $pivot; + } + +} diff --git a/src/Http/Requests/RepositoryDetachRequest.php b/src/Http/Requests/RepositoryDetachRequest.php new file mode 100644 index 000000000..7dfabde02 --- /dev/null +++ b/src/Http/Requests/RepositoryDetachRequest.php @@ -0,0 +1,8 @@ +isViaRepository()) { + if (!$request->isViaRepository()) { return static::newModel()->query(); } @@ -239,8 +239,8 @@ public function collectFields(RestifyRequest $request) if ($this instanceof Mergeable) { $fillable = collect($this->resource->getFillable()) - ->filter(fn ($attribute) => $fields->contains('attribute', $attribute) === false) - ->map(fn ($attribute) => Field::new($attribute)); + ->filter(fn($attribute) => $fields->contains('attribute', $attribute) === false) + ->map(fn($attribute) => Field::new($attribute)); $fields = $fields->merge($fillable); } @@ -251,14 +251,14 @@ public function collectFields(RestifyRequest $request) private function indexFields(RestifyRequest $request): Collection { return $this->collectFields($request) - ->filter(fn (Field $field) => ! $field->isHiddenOnIndex($request, $this)) + ->filter(fn(Field $field) => !$field->isHiddenOnIndex($request, $this)) ->values(); } private function showFields(RestifyRequest $request): Collection { return $this->collectFields($request) - ->filter(fn (Field $field) => ! $field->isHiddenOnShow($request, $this)) + ->filter(fn(Field $field) => !$field->isHiddenOnShow($request, $this)) ->values(); } @@ -358,10 +358,10 @@ public static function routes(Router $router, $attributes, $wrap = false) public function resolveShowAttributes(RestifyRequest $request) { $fields = $this->showFields($request) - ->filter(fn (Field $field) => $field->authorize($request)) - ->each(fn (Field $field) => $field->resolveForShow($this)) - ->map(fn (Field $field) => $field->serializeToValue($request)) - ->mapWithKeys(fn ($value) => $value) + ->filter(fn(Field $field) => $field->authorize($request)) + ->each(fn(Field $field) => $field->resolveForShow($this)) + ->map(fn(Field $field) => $field->serializeToValue($request)) + ->mapWithKeys(fn($value) => $value) ->all(); if ($this instanceof Mergeable) { @@ -379,7 +379,7 @@ public function resolveShowAttributes(RestifyRequest $request) return false; } - if (! $field->authorize($request)) { + if (!$field->authorize($request)) { return false; } @@ -400,10 +400,10 @@ public function resolveIndexAttributes($request) { // Resolve the show method, and attach the value to the array $fields = $this->indexFields($request) - ->filter(fn (Field $field) => $field->authorize($request)) - ->each(fn (Field $field) => $field->resolveForIndex($this)) - ->map(fn (Field $field) => $field->serializeToValue($request)) - ->mapWithKeys(fn ($value) => $value) + ->filter(fn(Field $field) => $field->authorize($request)) + ->each(fn(Field $field) => $field->resolveForIndex($this)) + ->map(fn(Field $field) => $field->serializeToValue($request)) + ->mapWithKeys(fn($value) => $value) ->all(); if ($this instanceof Mergeable) { @@ -421,7 +421,7 @@ public function resolveIndexAttributes($request) return false; } - if (! $field->authorize($request)) { + if (!$field->authorize($request)) { return false; } @@ -467,7 +467,7 @@ public function resolveRelationships($request): array /** * @var AbstractPaginator $paginator */ $paginator = $this->resource->{$relation}()->paginate($request->get('relatablePerPage') ?? (static::$defaultRelatablePerPage ?? RestifySearchable::DEFAULT_RELATABLE_PER_PAGE)); - $withs[$relation] = $paginator->getCollection()->map(fn (Model $item) => [ + $withs[$relation] = $paginator->getCollection()->map(fn(Model $item) => [ 'attributes' => $item->toArray(), ]); } @@ -519,7 +519,7 @@ public function index(RestifyRequest $request) return static::resolveWith($value); })->filter(function (self $repository) use ($request) { return $repository->authorizedToShow($request); - })->values()->map(fn (self $repository) => $repository->serializeForIndex($request)); + })->values()->map(fn(self $repository) => $repository->serializeForIndex($request)); return $this->response([ 'meta' => RepositoryCollection::meta($paginator->toArray()), @@ -547,7 +547,7 @@ public function store(RestifyRequest $request) $this->resource->save(); } - $this->storeFields($request)->each(fn (Field $field) => $field->invokeAfter($request, $this->resource)); + $this->storeFields($request)->each(fn(Field $field) => $field->invokeAfter($request, $this->resource)); }); static::stored($this->resource, $request); @@ -587,6 +587,17 @@ public function attach(RestifyRequest $request, $repositoryId, Collection $pivot ->created(); } + public function detach(RestifyRequest $request, $repositoryId, Collection $pivots) + { + $deleted = DB::transaction(function () use ($request, $pivots) { + return $pivots->map(fn($pivot) => $pivot->delete()); + }); + + return $this->response() + ->data($deleted) + ->deleted(); + } + public function destroy(RestifyRequest $request, $repositoryId) { $status = DB::transaction(function () { @@ -657,7 +668,7 @@ public function response($content = '', $status = 200, array $headers = []): Res public function serializeForShow(RestifyRequest $request): array { return $this->filter([ - 'id' => $this->when($this->resource->id, fn () => $this->getShowId($request)), + 'id' => $this->when($this->resource->id, fn() => $this->getShowId($request)), 'type' => $this->when($type = $this->getType($request), $type), 'attributes' => $request->isShowRequest() ? $this->resolveShowAttributes($request) : $this->resolveIndexAttributes($request), 'relationships' => $this->when(value($related = $this->resolveRelationships($request)), $related), @@ -670,7 +681,7 @@ public function serializeForIndex(RestifyRequest $request): array return $this->filter([ 'id' => $this->when($id = $this->getShowId($request), $id), 'type' => $this->when($type = $this->getType($request), $type), - 'attributes' => $this->when((bool) $attrs = $this->resolveIndexAttributes($request), $attrs), + 'attributes' => $this->when((bool)$attrs = $this->resolveIndexAttributes($request), $attrs), 'relationships' => $this->when(value($related = $this->resolveRelationships($request)), $related), 'meta' => $this->when(value($meta = $this->resolveIndexMeta($request)), $meta), ]); @@ -713,12 +724,12 @@ protected static function fillFields(RestifyRequest $request, Model $model, Coll public static function uriTo(Model $model) { - return Restify::path().'/'.static::uriKey().'/'.$model->getKey(); + return Restify::path() . '/' . static::uriKey() . '/' . $model->getKey(); } public function availableFilters(RestifyRequest $request) { - return collect($this->filter($this->filters($request)))->each(fn (Filter $filter) => $filter->authorizedToSee($request)) + return collect($this->filter($this->filters($request)))->each(fn(Filter $filter) => $filter->authorizedToSee($request)) ->values(); } } diff --git a/tests/Controllers/RepositoryPivotControllerTest.php b/tests/Controllers/RepositoryAttachControllerTest.php similarity index 97% rename from tests/Controllers/RepositoryPivotControllerTest.php rename to tests/Controllers/RepositoryAttachControllerTest.php index b6262b5ea..e04174d6c 100644 --- a/tests/Controllers/RepositoryPivotControllerTest.php +++ b/tests/Controllers/RepositoryAttachControllerTest.php @@ -5,7 +5,7 @@ use Binaryk\LaravelRestify\Tests\Fixtures\Company\Company; use Binaryk\LaravelRestify\Tests\IntegrationTest; -class RepositoryPivotControllerTest extends IntegrationTest +class RepositoryAttachControllerTest extends IntegrationTest { public function test_attach_a_user_to_a_company() { diff --git a/tests/Controllers/RepositoryDetachControllerTest.php b/tests/Controllers/RepositoryDetachControllerTest.php new file mode 100644 index 000000000..a7fb2c42d --- /dev/null +++ b/tests/Controllers/RepositoryDetachControllerTest.php @@ -0,0 +1,43 @@ +mockUsers(2)->first(); + $company = factory(Company::class)->create(); + $company->users()->attach($user->id); + $usersFromCompany = $this->getJson('/restify-api/users?viaRepository=companies&viaRepositoryId=1&viaRelationship=users'); + $this->assertCount(1, $usersFromCompany->json('data')); + $this->postJson('restify-api/companies/' . $company->id . '/detach/users', [ + 'users' => $user->id, + ]) + ->assertStatus(204); + $usersFromCompany = $this->getJson('/restify-api/users?viaRepository=companies&viaRepositoryId=1&viaRelationship=users'); + $this->assertCount(0, $usersFromCompany->json('data')); + } + + public function test_detach_multiple_users_from_a_company() + { + $users = $this->mockUsers(3); + $company = factory(Company::class)->create(); + $company->users()->attach($users->pluck('id')); + + $usersFromCompany = $this->getJson('/restify-api/users?viaRepository=companies&viaRepositoryId=1&viaRelationship=users'); + $this->assertCount(3, $usersFromCompany->json('data')); + + $this->postJson('restify-api/companies/' . $company->id . '/detach/users', [ + 'users' => [1, 2] + ]) + ->assertStatus(204); + + $usersFromCompany = $this->getJson('/restify-api/users?viaRepository=companies&viaRepositoryId=1&viaRelationship=users'); + $this->assertCount(1, $usersFromCompany->json('data')); + } + +} From 0fa982edc53338ee593e185856ed7e692a1bfe35 Mon Sep 17 00:00:00 2001 From: Lupacescu Eduard Date: Wed, 20 May 2020 00:50:26 +0300 Subject: [PATCH 3/3] Apply fixes from StyleCI (#183) --- routes/api.php | 23 ++++----- .../RepositoryAttachController.php | 3 +- .../RepositoryDetachController.php | 3 +- .../Requests/InteractWithRepositories.php | 10 ++-- src/Http/Requests/RepositoryAttachRequest.php | 1 - src/Http/Requests/RepositoryDetachRequest.php | 1 - src/Repositories/Repository.php | 51 +++++++++---------- .../RepositoryAttachControllerTest.php | 6 +-- .../RepositoryDetachControllerTest.php | 7 ++- tests/Fixtures/Company/Company.php | 2 +- tests/Fixtures/User/User.php | 2 +- 11 files changed, 49 insertions(+), 60 deletions(-) diff --git a/routes/api.php b/routes/api.php index d81e86b6b..2a9908094 100644 --- a/routes/api.php +++ b/routes/api.php @@ -1,31 +1,30 @@ attach( $request, $request->repositoryId, collect(Arr::wrap($request->input($request->relatedRepository))) - ->map(fn($relatedRepositoryId) => $this->initializePivot( + ->map(fn ($relatedRepositoryId) => $this->initializePivot( $request, $model->{$request->viaRelationship ?? $request->relatedRepository}(), $relatedRepositoryId )) ); @@ -61,5 +61,4 @@ protected function initializePivot(RestifyRequest $request, $relationship, $rela return $pivot; } - } diff --git a/src/Http/Controllers/RepositoryDetachController.php b/src/Http/Controllers/RepositoryDetachController.php index a7c366c12..1adeb4a1d 100644 --- a/src/Http/Controllers/RepositoryDetachController.php +++ b/src/Http/Controllers/RepositoryDetachController.php @@ -17,7 +17,7 @@ public function __invoke(RepositoryDetachRequest $request) return $repository->detach( $request, $request->repositoryId, collect(Arr::wrap($request->input($request->relatedRepository))) - ->map(fn($relatedRepositoryId) => $this->initializePivot( + ->map(fn ($relatedRepositoryId) => $this->initializePivot( $request, $model->{$request->viaRelationship ?? $request->relatedRepository}(), $relatedRepositoryId )) ); @@ -61,5 +61,4 @@ protected function initializePivot(RestifyRequest $request, $relationship, $rela return $pivot; } - } diff --git a/src/Http/Requests/InteractWithRepositories.php b/src/Http/Requests/InteractWithRepositories.php index cf7009273..7d2030908 100644 --- a/src/Http/Requests/InteractWithRepositories.php +++ b/src/Http/Requests/InteractWithRepositories.php @@ -44,7 +44,7 @@ public function repository($key = null): ?Repository ]), 404); } - if (!$repository::authorizedToUseRepository($this)) { + if (! $repository::authorizedToUseRepository($this)) { throw new UnauthorizedException(__('Unauthorized to view repository :name. See "allowRestify" policy.', [ 'name' => $repository, ]), 403); @@ -114,7 +114,7 @@ public function newRepositoryWith($model, $uriKey = null) */ public function newQueryWithoutScopes($uriKey = null) { - if (!$this->isViaRepository()) { + if (! $this->isViaRepository()) { return $this->model($uriKey)->newQueryWithoutScopes(); } @@ -170,7 +170,6 @@ public function findRelatedModelOrFail() }); } - public function findRelatedQuery($relatedRepository = null, $relatedRepositoryId = null) { return $this->repository($relatedRepository ?? request('relatedRepository'))::newModel() @@ -178,7 +177,6 @@ public function findRelatedQuery($relatedRepository = null, $relatedRepositoryId ->whereKey($relatedRepositoryId ?? request('relatedRepositoryId')); } - protected function findPivot(RestifyRequest $request, $model) { $pivot = $model->{$request->relatedRepository}()->getPivotAccessor(); @@ -193,7 +191,7 @@ public function viaParentModel() { $parent = $this->repository($this->viaRepository); - return once(fn() => $parent::newModel()->newQueryWithoutScopes()->whereKey($this->viaRepositoryId)->firstOrFail()); + return once(fn () => $parent::newModel()->newQueryWithoutScopes()->whereKey($this->viaRepositoryId)->firstOrFail()); } public function viaQuery() @@ -217,6 +215,4 @@ public function relatedRepository() { return Restify::repositoryForKey($this->relatedRepository); } - - } diff --git a/src/Http/Requests/RepositoryAttachRequest.php b/src/Http/Requests/RepositoryAttachRequest.php index 1586752ae..972af0822 100644 --- a/src/Http/Requests/RepositoryAttachRequest.php +++ b/src/Http/Requests/RepositoryAttachRequest.php @@ -4,5 +4,4 @@ class RepositoryAttachRequest extends RestifyRequest { - } diff --git a/src/Http/Requests/RepositoryDetachRequest.php b/src/Http/Requests/RepositoryDetachRequest.php index 7dfabde02..15987806d 100644 --- a/src/Http/Requests/RepositoryDetachRequest.php +++ b/src/Http/Requests/RepositoryDetachRequest.php @@ -4,5 +4,4 @@ class RepositoryDetachRequest extends RestifyRequest { - } diff --git a/src/Repositories/Repository.php b/src/Repositories/Repository.php index 7377370ea..7ed21bdf2 100644 --- a/src/Repositories/Repository.php +++ b/src/Repositories/Repository.php @@ -8,7 +8,6 @@ use Binaryk\LaravelRestify\Fields\Field; use Binaryk\LaravelRestify\Fields\FieldCollection; use Binaryk\LaravelRestify\Filter; -use Binaryk\LaravelRestify\Http\Requests\RepositoryAttachRequest; use Binaryk\LaravelRestify\Http\Requests\RestifyRequest; use Binaryk\LaravelRestify\Restify; use Binaryk\LaravelRestify\Services\Search\RepositorySearchService; @@ -182,7 +181,7 @@ public static function newModel(): Model public static function query(RestifyRequest $request) { - if (!$request->isViaRepository()) { + if (! $request->isViaRepository()) { return static::newModel()->query(); } @@ -239,8 +238,8 @@ public function collectFields(RestifyRequest $request) if ($this instanceof Mergeable) { $fillable = collect($this->resource->getFillable()) - ->filter(fn($attribute) => $fields->contains('attribute', $attribute) === false) - ->map(fn($attribute) => Field::new($attribute)); + ->filter(fn ($attribute) => $fields->contains('attribute', $attribute) === false) + ->map(fn ($attribute) => Field::new($attribute)); $fields = $fields->merge($fillable); } @@ -251,14 +250,14 @@ public function collectFields(RestifyRequest $request) private function indexFields(RestifyRequest $request): Collection { return $this->collectFields($request) - ->filter(fn(Field $field) => !$field->isHiddenOnIndex($request, $this)) + ->filter(fn (Field $field) => ! $field->isHiddenOnIndex($request, $this)) ->values(); } private function showFields(RestifyRequest $request): Collection { return $this->collectFields($request) - ->filter(fn(Field $field) => !$field->isHiddenOnShow($request, $this)) + ->filter(fn (Field $field) => ! $field->isHiddenOnShow($request, $this)) ->values(); } @@ -358,10 +357,10 @@ public static function routes(Router $router, $attributes, $wrap = false) public function resolveShowAttributes(RestifyRequest $request) { $fields = $this->showFields($request) - ->filter(fn(Field $field) => $field->authorize($request)) - ->each(fn(Field $field) => $field->resolveForShow($this)) - ->map(fn(Field $field) => $field->serializeToValue($request)) - ->mapWithKeys(fn($value) => $value) + ->filter(fn (Field $field) => $field->authorize($request)) + ->each(fn (Field $field) => $field->resolveForShow($this)) + ->map(fn (Field $field) => $field->serializeToValue($request)) + ->mapWithKeys(fn ($value) => $value) ->all(); if ($this instanceof Mergeable) { @@ -379,7 +378,7 @@ public function resolveShowAttributes(RestifyRequest $request) return false; } - if (!$field->authorize($request)) { + if (! $field->authorize($request)) { return false; } @@ -400,10 +399,10 @@ public function resolveIndexAttributes($request) { // Resolve the show method, and attach the value to the array $fields = $this->indexFields($request) - ->filter(fn(Field $field) => $field->authorize($request)) - ->each(fn(Field $field) => $field->resolveForIndex($this)) - ->map(fn(Field $field) => $field->serializeToValue($request)) - ->mapWithKeys(fn($value) => $value) + ->filter(fn (Field $field) => $field->authorize($request)) + ->each(fn (Field $field) => $field->resolveForIndex($this)) + ->map(fn (Field $field) => $field->serializeToValue($request)) + ->mapWithKeys(fn ($value) => $value) ->all(); if ($this instanceof Mergeable) { @@ -421,7 +420,7 @@ public function resolveIndexAttributes($request) return false; } - if (!$field->authorize($request)) { + if (! $field->authorize($request)) { return false; } @@ -467,7 +466,7 @@ public function resolveRelationships($request): array /** * @var AbstractPaginator $paginator */ $paginator = $this->resource->{$relation}()->paginate($request->get('relatablePerPage') ?? (static::$defaultRelatablePerPage ?? RestifySearchable::DEFAULT_RELATABLE_PER_PAGE)); - $withs[$relation] = $paginator->getCollection()->map(fn(Model $item) => [ + $withs[$relation] = $paginator->getCollection()->map(fn (Model $item) => [ 'attributes' => $item->toArray(), ]); } @@ -519,7 +518,7 @@ public function index(RestifyRequest $request) return static::resolveWith($value); })->filter(function (self $repository) use ($request) { return $repository->authorizedToShow($request); - })->values()->map(fn(self $repository) => $repository->serializeForIndex($request)); + })->values()->map(fn (self $repository) => $repository->serializeForIndex($request)); return $this->response([ 'meta' => RepositoryCollection::meta($paginator->toArray()), @@ -547,7 +546,7 @@ public function store(RestifyRequest $request) $this->resource->save(); } - $this->storeFields($request)->each(fn(Field $field) => $field->invokeAfter($request, $this->resource)); + $this->storeFields($request)->each(fn (Field $field) => $field->invokeAfter($request, $this->resource)); }); static::stored($this->resource, $request); @@ -578,8 +577,8 @@ public function update(RestifyRequest $request, $repositoryId) public function attach(RestifyRequest $request, $repositoryId, Collection $pivots) { DB::transaction(function () use ($request, $pivots) { - return $pivots->map(fn($pivot) => $pivot->forceFill($request->except($request->relatedRepository))) - ->map(fn($pivot) => $pivot->save()); + return $pivots->map(fn ($pivot) => $pivot->forceFill($request->except($request->relatedRepository))) + ->map(fn ($pivot) => $pivot->save()); }); return $this->response() @@ -590,7 +589,7 @@ public function attach(RestifyRequest $request, $repositoryId, Collection $pivot public function detach(RestifyRequest $request, $repositoryId, Collection $pivots) { $deleted = DB::transaction(function () use ($request, $pivots) { - return $pivots->map(fn($pivot) => $pivot->delete()); + return $pivots->map(fn ($pivot) => $pivot->delete()); }); return $this->response() @@ -668,7 +667,7 @@ public function response($content = '', $status = 200, array $headers = []): Res public function serializeForShow(RestifyRequest $request): array { return $this->filter([ - 'id' => $this->when($this->resource->id, fn() => $this->getShowId($request)), + 'id' => $this->when($this->resource->id, fn () => $this->getShowId($request)), 'type' => $this->when($type = $this->getType($request), $type), 'attributes' => $request->isShowRequest() ? $this->resolveShowAttributes($request) : $this->resolveIndexAttributes($request), 'relationships' => $this->when(value($related = $this->resolveRelationships($request)), $related), @@ -681,7 +680,7 @@ public function serializeForIndex(RestifyRequest $request): array return $this->filter([ 'id' => $this->when($id = $this->getShowId($request), $id), 'type' => $this->when($type = $this->getType($request), $type), - 'attributes' => $this->when((bool)$attrs = $this->resolveIndexAttributes($request), $attrs), + 'attributes' => $this->when((bool) $attrs = $this->resolveIndexAttributes($request), $attrs), 'relationships' => $this->when(value($related = $this->resolveRelationships($request)), $related), 'meta' => $this->when(value($meta = $this->resolveIndexMeta($request)), $meta), ]); @@ -724,12 +723,12 @@ protected static function fillFields(RestifyRequest $request, Model $model, Coll public static function uriTo(Model $model) { - return Restify::path() . '/' . static::uriKey() . '/' . $model->getKey(); + return Restify::path().'/'.static::uriKey().'/'.$model->getKey(); } public function availableFilters(RestifyRequest $request) { - return collect($this->filter($this->filters($request)))->each(fn(Filter $filter) => $filter->authorizedToSee($request)) + return collect($this->filter($this->filters($request)))->each(fn (Filter $filter) => $filter->authorizedToSee($request)) ->values(); } } diff --git a/tests/Controllers/RepositoryAttachControllerTest.php b/tests/Controllers/RepositoryAttachControllerTest.php index e04174d6c..24e2ade68 100644 --- a/tests/Controllers/RepositoryAttachControllerTest.php +++ b/tests/Controllers/RepositoryAttachControllerTest.php @@ -12,7 +12,7 @@ public function test_attach_a_user_to_a_company() $user = $this->mockUsers(2)->first(); $company = factory(Company::class)->create(); - $response = $this->postJson('restify-api/companies/' . $company->id . '/attach/users', [ + $response = $this->postJson('restify-api/companies/'.$company->id.'/attach/users', [ 'users' => $user->id, 'is_admin' => true, ]) @@ -32,7 +32,7 @@ public function test_attach_multiple_users_to_a_company() $usersFromCompany = $this->getJson('/restify-api/users?viaRepository=companies&viaRepositoryId=1&viaRelationship=users'); $this->assertCount(0, $usersFromCompany->json('data')); - $response = $this->postJson('restify-api/companies/' . $company->id . '/attach/users', [ + $response = $this->postJson('restify-api/companies/'.$company->id.'/attach/users', [ 'users' => [1, 2], 'is_admin' => true, ]) @@ -56,7 +56,7 @@ public function test_after_attach_a_user_to_company_number_of_users_increased() $usersFromCompany = $this->getJson('/restify-api/users?viaRepository=companies&viaRepositoryId=1&viaRelationship=users'); $this->assertCount(0, $usersFromCompany->json('data')); - $this->postJson('restify-api/companies/' . $company->id . '/attach/users', [ + $this->postJson('restify-api/companies/'.$company->id.'/attach/users', [ 'users' => $user->id, ]); diff --git a/tests/Controllers/RepositoryDetachControllerTest.php b/tests/Controllers/RepositoryDetachControllerTest.php index a7fb2c42d..8d049a70c 100644 --- a/tests/Controllers/RepositoryDetachControllerTest.php +++ b/tests/Controllers/RepositoryDetachControllerTest.php @@ -14,7 +14,7 @@ public function test_detach_a_user_from_a_company() $company->users()->attach($user->id); $usersFromCompany = $this->getJson('/restify-api/users?viaRepository=companies&viaRepositoryId=1&viaRelationship=users'); $this->assertCount(1, $usersFromCompany->json('data')); - $this->postJson('restify-api/companies/' . $company->id . '/detach/users', [ + $this->postJson('restify-api/companies/'.$company->id.'/detach/users', [ 'users' => $user->id, ]) ->assertStatus(204); @@ -31,13 +31,12 @@ public function test_detach_multiple_users_from_a_company() $usersFromCompany = $this->getJson('/restify-api/users?viaRepository=companies&viaRepositoryId=1&viaRelationship=users'); $this->assertCount(3, $usersFromCompany->json('data')); - $this->postJson('restify-api/companies/' . $company->id . '/detach/users', [ - 'users' => [1, 2] + $this->postJson('restify-api/companies/'.$company->id.'/detach/users', [ + 'users' => [1, 2], ]) ->assertStatus(204); $usersFromCompany = $this->getJson('/restify-api/users?viaRepository=companies&viaRepositoryId=1&viaRelationship=users'); $this->assertCount(1, $usersFromCompany->json('data')); } - } diff --git a/tests/Fixtures/Company/Company.php b/tests/Fixtures/Company/Company.php index 6caf50442..d1ed1dc86 100644 --- a/tests/Fixtures/Company/Company.php +++ b/tests/Fixtures/Company/Company.php @@ -16,7 +16,7 @@ public function users() { return $this->belongsToMany(User::class, 'company_user', 'company_id', 'user_id') ->withPivot([ - 'is_admin' + 'is_admin', ]) ->withTimestamps(); } diff --git a/tests/Fixtures/User/User.php b/tests/Fixtures/User/User.php index 39fea7f61..7cab46a0e 100644 --- a/tests/Fixtures/User/User.php +++ b/tests/Fixtures/User/User.php @@ -85,7 +85,7 @@ public function posts() public function companies() { return $this->belongsToMany(Company::class, 'company_user', 'user_id', 'company_id')->withPivot([ - 'is_admin' + 'is_admin', ])->withTimestamps(); }