Skip to content

Commit

Permalink
feat: ability to customize policy class per controller
Browse files Browse the repository at this point in the history
  • Loading branch information
alexzarbn committed Dec 29, 2022
1 parent 55b5260 commit b436c04
Show file tree
Hide file tree
Showing 34 changed files with 305 additions and 400 deletions.
1 change: 1 addition & 0 deletions src/Contracts/ComponentsResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ public function resolveResourceClass(): string;
public function resolveCollectionResourceClass(): ?string;

public function bindRequestClass(string $requestClass): void;
public function bindPolicyClass(string $policyClass): void;
}
12 changes: 11 additions & 1 deletion src/Drivers/Standard/ComponentsResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Orion\Drivers\Standard;

use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Gate;
use Orion\Http\Requests\Request;
use Orion\Http\Resources\Resource;

Expand Down Expand Up @@ -62,6 +63,7 @@ public function getRequestClassesNamespace(): string
public function setRequestClassesNamespace(string $requestClassesNamespace): self
{
$this->requestClassesNamespace = $requestClassesNamespace;

return $this;
}

Expand Down Expand Up @@ -94,6 +96,7 @@ public function getResourceClassesNamespace(): string
public function setResourceClassesNamespace(string $resourceClassesNamespace): self
{
$this->resourceClassesNamespace = $resourceClassesNamespace;

return $this;
}

Expand All @@ -102,7 +105,9 @@ public function setResourceClassesNamespace(string $resourceClassesNamespace): s
*/
public function resolveCollectionResourceClass(): ?string
{
$collectionResourceClassName = $this->getResourceClassesNamespace().class_basename($this->resourceModelClass).'CollectionResource';
$collectionResourceClassName = $this->getResourceClassesNamespace().class_basename(
$this->resourceModelClass
).'CollectionResource';

if (class_exists($collectionResourceClassName)) {
return $collectionResourceClassName;
Expand All @@ -120,4 +125,9 @@ public function bindRequestClass(string $requestClass): void
{
App::bind(Request::class, $requestClass);
}

public function bindPolicyClass(string $policyClass): void
{
Gate::policy($this->resourceModelClass, $policyClass);
}
}
2 changes: 2 additions & 0 deletions src/Drivers/Standard/QueryBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,8 @@ function ($relationQuery) use ($relationField, $filterDescriptor) {
);
}
}

var_dump($query->toSql());
}

/**
Expand Down
32 changes: 30 additions & 2 deletions src/Http/Controllers/BaseController.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,11 @@ abstract class BaseController extends \Illuminate\Routing\Controller
*/
protected $collectionResource = null;

/**
* @var string|null $policy
*/
protected $policy;

/**
* @var ComponentsResolver $componentsResolver
*/
Expand Down Expand Up @@ -97,7 +102,7 @@ abstract class BaseController extends \Illuminate\Routing\Controller
public function __construct()
{
if (!$this->model) {
throw new BindingException('Model is not defined for ' . static::class);
throw new BindingException('Model is not defined for '.static::class);
}

$this->componentsResolver = App::makeWith(
Expand Down Expand Up @@ -127,7 +132,7 @@ public function __construct()
Paginator::class,
[
'defaultLimit' => $this->limit(),
'maxLimit' => $this->maxLimit()
'maxLimit' => $this->maxLimit(),
]
);
$this->searchBuilder = App::makeWith(
Expand Down Expand Up @@ -309,6 +314,10 @@ protected function resolveComponents(): void
protected function bindComponents(): void
{
$this->componentsResolver->bindRequestClass($this->getRequest());

if ($policy = $this->getPolicy()) {
$this->componentsResolver->bindPolicyClass($policy);
}
}

/**
Expand All @@ -330,6 +339,25 @@ public function setRequest(string $requestClass): self
return $this;
}

/**
* @return string|null
*/
public function getPolicy(): ?string
{
return $this->policy;
}

/**
* @param string $policy
* @return $this
*/
public function setPolicy(string $policy): self
{
$this->policy = $policy;

return $this;
}

/**
* Authorize a given action for the current user.
*
Expand Down
66 changes: 64 additions & 2 deletions src/Http/Controllers/RelationController.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@
use Orion\Concerns\HandlesRelationOneToManyOperations;
use Orion\Concerns\HandlesRelationStandardBatchOperations;
use Orion\Concerns\HandlesRelationStandardOperations;
use Orion\Contracts\ComponentsResolver;
use Orion\Contracts\QueryBuilder;
use Orion\Exceptions\BindingException;
use Orion\Http\Requests\Request;

abstract class RelationController extends BaseController
{
Expand All @@ -23,6 +23,11 @@ abstract class RelationController extends BaseController
*/
protected $relation;

/**
* @var string|null $policy
*/
protected $parentPolicy;

/**
* The list of pivot fields that can be set upon relation resource creation or update.
*
Expand All @@ -42,6 +47,11 @@ abstract class RelationController extends BaseController
*/
protected $relationQueryBuilder;

/**
* @var ComponentsResolver $parentComponentsResolver
*/
protected $parentComponentsResolver;

/**
* RelationController constructor.
*
Expand All @@ -50,9 +60,14 @@ abstract class RelationController extends BaseController
public function __construct()
{
if (!$this->relation) {
throw new BindingException('Relation is not defined for ' . static::class);
throw new BindingException('Relation is not defined for '.static::class);
}

$this->parentComponentsResolver = App::makeWith(
ComponentsResolver::class,
['resourceModelClass' => $this->getModel(),]
);

parent::__construct();

$this->relationQueryBuilder = App::makeWith(
Expand All @@ -66,6 +81,15 @@ public function __construct()
);
}

protected function bindComponents(): void
{
parent::bindComponents();

if ($parentPolicy = $this->getParentPolicy()) {
$this->parentComponentsResolver->bindPolicyClass($parentPolicy);
}
}

/**
* Retrieves model related to resource.
*
Expand Down Expand Up @@ -107,6 +131,25 @@ public function setRelation(string $relation): self
return $this;
}

/**
* @return string|null
*/
public function getParentPolicy(): ?string
{
return $this->parentPolicy;
}

/**
* @param string $policy
* @return $this
*/
public function setParentPolicy(string $policy): self
{
$this->parentPolicy = $policy;

return $this;
}

/**
* Creates new Eloquent query builder of the relation on the given parent model.
*
Expand Down Expand Up @@ -175,6 +218,25 @@ public function setRelationQueryBuilder(QueryBuilder $relationQueryBuilder): sel
return $this;
}

/**
* @return ComponentsResolver
*/
public function getParentComponentsResolver(): ComponentsResolver
{
return $this->parentComponentsResolver;
}

/**
* @param ComponentsResolver $componentsResolver
* @return $this
*/
public function setParentComponentsResolver(ComponentsResolver $componentsResolver): self
{
$this->parentComponentsResolver = $componentsResolver;

return $this;
}

/**
* A qualified name of the field used to fetch parent resource from the database.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ public function transforming_a_single_deleted_relation_resource(): void
ComponentsResolver::class,
function () {
$componentsResolverMock = Mockery::mock(\Orion\Drivers\Standard\ComponentsResolver::class)->makePartial();
$componentsResolverMock->shouldReceive('resolveResourceClass')->once()->andReturn(SampleResource::class);
$componentsResolverMock->shouldReceive('resolveResourceClass')->zeroOrMoreTimes()->andReturn(SampleResource::class);

return $componentsResolverMock;
}
Expand All @@ -158,4 +158,4 @@ public function deleting_a_single_relation_resource_and_getting_included_relatio

$this->assertResourceDeleted($response, $user, ['posts' => [$post->toArray()]]);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -75,15 +75,7 @@ public function transforming_a_single_restored_relation_resource(): void

Gate::policy(Category::class, GreenPolicy::class);

app()->bind(
ComponentsResolver::class,
function () {
$componentsResolverMock = Mockery::mock(\Orion\Drivers\Standard\ComponentsResolver::class)->makePartial();
$componentsResolverMock->shouldReceive('resolveResourceClass')->once()->andReturn(SampleResource::class);

return $componentsResolverMock;
}
);
$this->useResource(SampleResource::class);

$response = $this->post("/api/posts/{$post->id}/category/{$trashedCategory->id}/restore");

Expand All @@ -102,4 +94,4 @@ public function restoring_a_single_relation_resource_and_getting_included_relati

$this->assertResourceRestored($response, $trashedCategory, ['posts' => $trashedCategory->posts->toArray()]);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
namespace Orion\Tests\Feature\Relations\BelongsTo;

use Illuminate\Support\Facades\Gate;
use Mockery;
use Orion\Contracts\ComponentsResolver;
use Orion\Tests\Feature\TestCase;
use Orion\Tests\Fixtures\App\Http\Resources\SampleResource;
use Orion\Tests\Fixtures\App\Models\Category;
Expand Down Expand Up @@ -74,15 +72,7 @@ public function getting_a_single_transformed_relation_resource(): void
$user = factory(User::class)->create();
$post = factory(Post::class)->create(['user_id' => $user->id]);

app()->bind(
ComponentsResolver::class,
function () {
$componentsResolverMock = Mockery::mock(\Orion\Drivers\Standard\ComponentsResolver::class)->makePartial();
$componentsResolverMock->shouldReceive('resolveResourceClass')->once()->andReturn(SampleResource::class);

return $componentsResolverMock;
}
);
$this->useResource(SampleResource::class);

Gate::policy(User::class, GreenPolicy::class);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,15 +76,7 @@ public function updating_a_single_relation_resource_when_validation_fails(): voi
$post = factory(Post::class)->create(['user_id' => $user->id]);
$payload = ['email' => '[email protected]'];

app()->bind(
ComponentsResolver::class,
function () {
$componentsResolverMock = Mockery::mock(\Orion\Drivers\Standard\ComponentsResolver::class)->makePartial();
$componentsResolverMock->shouldReceive('resolveRequestClass')->once()->andReturn(UserRequest::class);

return $componentsResolverMock;
}
);
$this->useRequest(UserRequest::class);

Gate::policy(User::class, GreenPolicy::class);

Expand All @@ -102,15 +94,7 @@ public function transforming_a_single_updated_relation_resource(): void
$post = factory(Post::class)->create(['user_id' => $user->id]);
$payload = ['name' => 'test user updated'];

app()->bind(
ComponentsResolver::class,
function () {
$componentsResolverMock = Mockery::mock(\Orion\Drivers\Standard\ComponentsResolver::class)->makePartial();
$componentsResolverMock->shouldReceive('resolveResourceClass')->once()->andReturn(SampleResource::class);

return $componentsResolverMock;
}
);
$this->useResource(SampleResource::class);

Gate::policy(User::class, GreenPolicy::class);

Expand Down Expand Up @@ -144,4 +128,4 @@ public function updating_a_single_resource_and_getting_included_relation(): void
['posts' => $user->fresh('posts')->posts->toArray()]
);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -182,15 +182,7 @@ public function transforming_a_single_deleted_relation_resource(): void

$role = $user->roles()->first();

app()->bind(
ComponentsResolver::class,
function () {
$componentsResolverMock = Mockery::mock(\Orion\Drivers\Standard\ComponentsResolver::class)->makePartial();
$componentsResolverMock->shouldReceive('resolveResourceClass')->once()->andReturn(SampleResource::class);

return $componentsResolverMock;
}
);
$this->useResource(SampleResource::class);

Gate::policy(Role::class, GreenPolicy::class);

Expand All @@ -215,4 +207,4 @@ public function deleting_a_single_relation_resource_and_getting_included_relatio

$this->assertResourceDeleted($response, $role);
}
}
}
Loading

0 comments on commit b436c04

Please sign in to comment.