Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/Fields/Field.php
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ public function fillAttribute(RestifyRequest $request, $model, int $bulkRow = nu
}

if ($request->isStoreRequest() && is_callable($this->storeCallback)) {
return $model->{$this->attribute} = call_user_func(
return call_user_func(
$this->storeCallback, $request, $model, $this->attribute, $bulkRow
);
}
Expand All @@ -238,7 +238,7 @@ public function fillAttribute(RestifyRequest $request, $model, int $bulkRow = nu
}

if ($request->isUpdateRequest() && is_callable($this->updateCallback)) {
return $model->{$this->attribute} = call_user_func(
return call_user_func(
$this->updateCallback, $request, $model, $this->attribute, $bulkRow
);
}
Expand Down
3 changes: 2 additions & 1 deletion src/Http/Controllers/RepositoryAttachController.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class RepositoryAttachController extends RepositoryController
public function __invoke(RepositoryAttachRequest $request)
{
$model = $request->findModelOrFail();
$repository = $request->repository()->allowToUpdate($request);
$repository = $request->repository();

if (is_callable($method = $this->guessMethodName($request, $repository))) {
return call_user_func($method, $request, $repository, $model);
Expand All @@ -23,6 +23,7 @@ public function __invoke(RepositoryAttachRequest $request)
return $repository->attach(
$request, $request->repositoryId,
collect(Arr::wrap($request->input($request->relatedRepository)))
->filter(fn ($relatedRepositoryId) => $request->repository()->allowToAttach($request, $request->attachRelatedModels()))
->map(fn ($relatedRepositoryId) => $this->initializePivot(
$request, $model->{$request->viaRelationship ?? $request->relatedRepository}(), $relatedRepositoryId
))
Expand Down
3 changes: 2 additions & 1 deletion src/Http/Controllers/RepositoryDetachController.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ public function __invoke(RepositoryDetachRequest $request)
return $repository->detach(
$request, $request->repositoryId,
collect(Arr::wrap($request->input($request->relatedRepository)))
->map(fn ($relatedRepositoryId) => $this->initializePivot(
->filter(fn ($relatedRepositoryId) => $request->repository()->allowToDetach($request, $request->detachRelatedModels()))
->map(fn ($relatedRepositoryId) => $this->initializePivot(
$request, $model->{$request->viaRelationship ?? $request->relatedRepository}(), $relatedRepositoryId
))
);
Expand Down
17 changes: 17 additions & 0 deletions src/Http/Requests/RepositoryAttachRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,23 @@

namespace Binaryk\LaravelRestify\Http\Requests;

use Binaryk\LaravelRestify\Restify;
use Illuminate\Support\Arr;
use Illuminate\Support\Collection;

class RepositoryAttachRequest extends RestifyRequest
{
public function attachRelatedModels(): Collection
{
$relatedRepository = $this->repository(
Restify::repositoryForTable($table = $this->relatedRepository)::uriKey()
);

if (is_null($relatedRepository)) {
abort(400, "Missing repository for the [$table] table");
}

return collect(Arr::wrap($this->input($this->relatedRepository)))
->map(fn ($id) => $relatedRepository->model()->newModelQuery()->whereKey($id)->first());
}
}
17 changes: 17 additions & 0 deletions src/Http/Requests/RepositoryDetachRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,23 @@

namespace Binaryk\LaravelRestify\Http\Requests;

use Binaryk\LaravelRestify\Restify;
use Illuminate\Support\Arr;
use Illuminate\Support\Collection;

class RepositoryDetachRequest extends RestifyRequest
{
public function detachRelatedModels(): Collection
{
$relatedRepository = $this->repository(
Restify::repositoryForTable($table = $this->relatedRepository)::uriKey()
);

if (is_null($relatedRepository)) {
abort(400, "Missing repository for the [$table] table");
}

return collect(Arr::wrap($this->input($this->relatedRepository)))
->map(fn ($id) => $relatedRepository->model()->newModelQuery()->whereKey($id)->first());
}
}
20 changes: 20 additions & 0 deletions src/Repositories/Repository.php
Original file line number Diff line number Diff line change
Expand Up @@ -690,6 +690,8 @@ public function update(RestifyRequest $request, $repositoryId)
return $this->resource;
});

$this->updateFields($request)->each(fn (Field $field) => $field->invokeAfter($request, $this->resource));

return $this->response()
->data($this->serializeForShow($request))
->success();
Expand Down Expand Up @@ -754,6 +756,24 @@ public function allowToUpdate(RestifyRequest $request, $payload = null): self
return $this;
}

public function allowToAttach(RestifyRequest $request, Collection $attachers): self
{
$methodGuesser = 'attach'.Str::studly($request->relatedRepository);

$attachers->each(fn ($model) => $this->authorizeToAttach($request, $methodGuesser, $model));

return $this;
}

public function allowToDetach(RestifyRequest $request, Collection $attachers): self
{
$methodGuesser = 'detach'.Str::studly($request->relatedRepository);

$attachers->each(fn ($model) => $this->authorizeToDetach($request, $methodGuesser, $model));

return $this;
}

public function allowToUpdateBulk(RestifyRequest $request, $payload = null): self
{
$this->authorizeToUpdateBulk($request);
Expand Down
20 changes: 20 additions & 0 deletions src/Repositories/ValidatingTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,26 @@ public static function validatorForUpdate(RestifyRequest $request, $resource = n
});
}

public static function validatorForAttach(RestifyRequest $request, $resource = null, array $plainPayload = null)
{
/** * @var Repository $on */
$on = $resource ?? static::resolveWith(static::newModel());

$messages = $on->collectFields($request)->flatMap(function ($k) {
$messages = [];
foreach ($k->messages as $ruleFor => $message) {
$messages[$k->attribute.'.'.$ruleFor] = $message;
}

return $messages;
})->toArray();

return Validator::make($plainPayload ?? $request->all(), $on->getUpdatingRules($request), $messages)->after(function ($validator) use ($request) {
static::afterValidation($request, $validator);
static::afterUpdatingValidation($request, $validator);
});
}

public static function validatorForUpdateBulk(RestifyRequest $request, $resource = null, array $plainPayload = null)
{
/** * @var Repository $on */
Expand Down
15 changes: 14 additions & 1 deletion src/Restify.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public static function repositoryForKey($key)
}

/**
* Get the repository class name for a given key.
* Get the repository class name for a given model.
*
* @param string $model
* @return string
Expand All @@ -68,6 +68,19 @@ public static function repositoryForModel($model)
});
}

/**
* Get the repository class name for a given table name.
*
* @param string $table
* @return string
*/
public static function repositoryForTable($table)
{
return collect(static::$repositories)->first(function ($value) use ($table) {
return app($value::$model)->getTable() === $table;
});
}

/**
* Register the given repositories.
*
Expand Down
30 changes: 30 additions & 0 deletions src/Traits/AuthorizableModels.php
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,36 @@ public function authorizeToUpdate(Request $request)
$this->authorizeTo($request, 'update');
}

public function authorizeToAttach(Request $request, $method, $model)
{
if (! static::authorizable()) {
return true;
}

$authorized = method_exists(Gate::getPolicyFor($this->model()), $method)
? Gate::check($method, [$this->model(), $model])
: true;

if (false === $authorized) {
throw new AuthorizationException();
}
}

public function authorizeToDetach(Request $request, $method, $model)
{
if (! static::authorizable()) {
return true;
}

$authorized = method_exists(Gate::getPolicyFor($this->model()), $method)
? Gate::check($method, [$this->model(), $model])
: true;

if (false === $authorized) {
throw new AuthorizationException();
}
}

public function authorizeToUpdateBulk(Request $request)
{
$this->authorizeTo($request, 'updateBulk');
Expand Down
63 changes: 63 additions & 0 deletions tests/Controllers/RepositoryAttachControllerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
namespace Binaryk\LaravelRestify\Tests\Controllers;

use Binaryk\LaravelRestify\Tests\Fixtures\Company\Company;
use Binaryk\LaravelRestify\Tests\Fixtures\Company\CompanyPolicy;
use Binaryk\LaravelRestify\Tests\Fixtures\User\User;
use Binaryk\LaravelRestify\Tests\IntegrationTest;
use Illuminate\Support\Facades\Gate;

class RepositoryAttachControllerTest extends IntegrationTest
{
Expand Down Expand Up @@ -63,4 +66,64 @@ 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(1, $usersFromCompany->json('data'));
}

public function test_policy_to_attach_a_user_to_a_company()
{
Gate::policy(Company::class, CompanyPolicy::class);

$user = $this->mockUsers(2)->first();
$company = factory(Company::class)->create();
$this->authenticate(
factory(User::class)->create()
);

$_SERVER['allow_attach_users'] = false;

$this->postJson('restify-api/companies/'.$company->id.'/attach/users', [
'users' => $user->id,
'is_admin' => true,
])
->assertForbidden();

$_SERVER['allow_attach_users'] = true;

$this->postJson('restify-api/companies/'.$company->id.'/attach/users', [
'users' => $user->id,
'is_admin' => true,
])
->assertCreated();
}

public function test_policy_to_detach_a_user_to_a_company()
{
Gate::policy(Company::class, CompanyPolicy::class);

$user = $this->mockUsers(2)->first();
$company = factory(Company::class)->create();
$this->authenticate(
factory(User::class)->create()
);

$this->postJson('restify-api/companies/'.$company->id.'/attach/users', [
'users' => $user->id,
'is_admin' => true,
])
->assertCreated();

$_SERVER['allow_detach_users'] = false;

$this->postJson('restify-api/companies/'.$company->id.'/detach/users', [
'users' => $user->id,
'is_admin' => true,
])
->assertForbidden();

$_SERVER['allow_detach_users'] = true;

$this->postJson('restify-api/companies/'.$company->id.'/detach/users', [
'users' => $user->id,
'is_admin' => true,
])
->assertNoContent();
}
}
Loading