Skip to content

Commit b436c04

Browse files
committed
feat: ability to customize policy class per controller
1 parent 55b5260 commit b436c04

File tree

34 files changed

+305
-400
lines changed

34 files changed

+305
-400
lines changed

src/Contracts/ComponentsResolver.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,5 @@ public function resolveResourceClass(): string;
1313
public function resolveCollectionResourceClass(): ?string;
1414

1515
public function bindRequestClass(string $requestClass): void;
16+
public function bindPolicyClass(string $policyClass): void;
1617
}

src/Drivers/Standard/ComponentsResolver.php

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace Orion\Drivers\Standard;
44

55
use Illuminate\Support\Facades\App;
6+
use Illuminate\Support\Facades\Gate;
67
use Orion\Http\Requests\Request;
78
use Orion\Http\Resources\Resource;
89

@@ -62,6 +63,7 @@ public function getRequestClassesNamespace(): string
6263
public function setRequestClassesNamespace(string $requestClassesNamespace): self
6364
{
6465
$this->requestClassesNamespace = $requestClassesNamespace;
66+
6567
return $this;
6668
}
6769

@@ -94,6 +96,7 @@ public function getResourceClassesNamespace(): string
9496
public function setResourceClassesNamespace(string $resourceClassesNamespace): self
9597
{
9698
$this->resourceClassesNamespace = $resourceClassesNamespace;
99+
97100
return $this;
98101
}
99102

@@ -102,7 +105,9 @@ public function setResourceClassesNamespace(string $resourceClassesNamespace): s
102105
*/
103106
public function resolveCollectionResourceClass(): ?string
104107
{
105-
$collectionResourceClassName = $this->getResourceClassesNamespace().class_basename($this->resourceModelClass).'CollectionResource';
108+
$collectionResourceClassName = $this->getResourceClassesNamespace().class_basename(
109+
$this->resourceModelClass
110+
).'CollectionResource';
106111

107112
if (class_exists($collectionResourceClassName)) {
108113
return $collectionResourceClassName;
@@ -120,4 +125,9 @@ public function bindRequestClass(string $requestClass): void
120125
{
121126
App::bind(Request::class, $requestClass);
122127
}
128+
129+
public function bindPolicyClass(string $policyClass): void
130+
{
131+
Gate::policy($this->resourceModelClass, $policyClass);
132+
}
123133
}

src/Drivers/Standard/QueryBuilder.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,8 @@ function ($relationQuery) use ($relationField, $filterDescriptor) {
150150
);
151151
}
152152
}
153+
154+
var_dump($query->toSql());
153155
}
154156

155157
/**

src/Http/Controllers/BaseController.php

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,11 @@ abstract class BaseController extends \Illuminate\Routing\Controller
5959
*/
6060
protected $collectionResource = null;
6161

62+
/**
63+
* @var string|null $policy
64+
*/
65+
protected $policy;
66+
6267
/**
6368
* @var ComponentsResolver $componentsResolver
6469
*/
@@ -97,7 +102,7 @@ abstract class BaseController extends \Illuminate\Routing\Controller
97102
public function __construct()
98103
{
99104
if (!$this->model) {
100-
throw new BindingException('Model is not defined for ' . static::class);
105+
throw new BindingException('Model is not defined for '.static::class);
101106
}
102107

103108
$this->componentsResolver = App::makeWith(
@@ -127,7 +132,7 @@ public function __construct()
127132
Paginator::class,
128133
[
129134
'defaultLimit' => $this->limit(),
130-
'maxLimit' => $this->maxLimit()
135+
'maxLimit' => $this->maxLimit(),
131136
]
132137
);
133138
$this->searchBuilder = App::makeWith(
@@ -309,6 +314,10 @@ protected function resolveComponents(): void
309314
protected function bindComponents(): void
310315
{
311316
$this->componentsResolver->bindRequestClass($this->getRequest());
317+
318+
if ($policy = $this->getPolicy()) {
319+
$this->componentsResolver->bindPolicyClass($policy);
320+
}
312321
}
313322

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

342+
/**
343+
* @return string|null
344+
*/
345+
public function getPolicy(): ?string
346+
{
347+
return $this->policy;
348+
}
349+
350+
/**
351+
* @param string $policy
352+
* @return $this
353+
*/
354+
public function setPolicy(string $policy): self
355+
{
356+
$this->policy = $policy;
357+
358+
return $this;
359+
}
360+
333361
/**
334362
* Authorize a given action for the current user.
335363
*

src/Http/Controllers/RelationController.php

Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@
1010
use Orion\Concerns\HandlesRelationOneToManyOperations;
1111
use Orion\Concerns\HandlesRelationStandardBatchOperations;
1212
use Orion\Concerns\HandlesRelationStandardOperations;
13+
use Orion\Contracts\ComponentsResolver;
1314
use Orion\Contracts\QueryBuilder;
1415
use Orion\Exceptions\BindingException;
15-
use Orion\Http\Requests\Request;
1616

1717
abstract class RelationController extends BaseController
1818
{
@@ -23,6 +23,11 @@ abstract class RelationController extends BaseController
2323
*/
2424
protected $relation;
2525

26+
/**
27+
* @var string|null $policy
28+
*/
29+
protected $parentPolicy;
30+
2631
/**
2732
* The list of pivot fields that can be set upon relation resource creation or update.
2833
*
@@ -42,6 +47,11 @@ abstract class RelationController extends BaseController
4247
*/
4348
protected $relationQueryBuilder;
4449

50+
/**
51+
* @var ComponentsResolver $parentComponentsResolver
52+
*/
53+
protected $parentComponentsResolver;
54+
4555
/**
4656
* RelationController constructor.
4757
*
@@ -50,9 +60,14 @@ abstract class RelationController extends BaseController
5060
public function __construct()
5161
{
5262
if (!$this->relation) {
53-
throw new BindingException('Relation is not defined for ' . static::class);
63+
throw new BindingException('Relation is not defined for '.static::class);
5464
}
5565

66+
$this->parentComponentsResolver = App::makeWith(
67+
ComponentsResolver::class,
68+
['resourceModelClass' => $this->getModel(),]
69+
);
70+
5671
parent::__construct();
5772

5873
$this->relationQueryBuilder = App::makeWith(
@@ -66,6 +81,15 @@ public function __construct()
6681
);
6782
}
6883

84+
protected function bindComponents(): void
85+
{
86+
parent::bindComponents();
87+
88+
if ($parentPolicy = $this->getParentPolicy()) {
89+
$this->parentComponentsResolver->bindPolicyClass($parentPolicy);
90+
}
91+
}
92+
6993
/**
7094
* Retrieves model related to resource.
7195
*
@@ -107,6 +131,25 @@ public function setRelation(string $relation): self
107131
return $this;
108132
}
109133

134+
/**
135+
* @return string|null
136+
*/
137+
public function getParentPolicy(): ?string
138+
{
139+
return $this->parentPolicy;
140+
}
141+
142+
/**
143+
* @param string $policy
144+
* @return $this
145+
*/
146+
public function setParentPolicy(string $policy): self
147+
{
148+
$this->parentPolicy = $policy;
149+
150+
return $this;
151+
}
152+
110153
/**
111154
* Creates new Eloquent query builder of the relation on the given parent model.
112155
*
@@ -175,6 +218,25 @@ public function setRelationQueryBuilder(QueryBuilder $relationQueryBuilder): sel
175218
return $this;
176219
}
177220

221+
/**
222+
* @return ComponentsResolver
223+
*/
224+
public function getParentComponentsResolver(): ComponentsResolver
225+
{
226+
return $this->parentComponentsResolver;
227+
}
228+
229+
/**
230+
* @param ComponentsResolver $componentsResolver
231+
* @return $this
232+
*/
233+
public function setParentComponentsResolver(ComponentsResolver $componentsResolver): self
234+
{
235+
$this->parentComponentsResolver = $componentsResolver;
236+
237+
return $this;
238+
}
239+
178240
/**
179241
* A qualified name of the field used to fetch parent resource from the database.
180242
*

tests/Feature/Relations/BelongsTo/BelongsToRelationStandardDeleteOperationsTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ public function transforming_a_single_deleted_relation_resource(): void
133133
ComponentsResolver::class,
134134
function () {
135135
$componentsResolverMock = Mockery::mock(\Orion\Drivers\Standard\ComponentsResolver::class)->makePartial();
136-
$componentsResolverMock->shouldReceive('resolveResourceClass')->once()->andReturn(SampleResource::class);
136+
$componentsResolverMock->shouldReceive('resolveResourceClass')->zeroOrMoreTimes()->andReturn(SampleResource::class);
137137

138138
return $componentsResolverMock;
139139
}
@@ -158,4 +158,4 @@ public function deleting_a_single_relation_resource_and_getting_included_relatio
158158

159159
$this->assertResourceDeleted($response, $user, ['posts' => [$post->toArray()]]);
160160
}
161-
}
161+
}

tests/Feature/Relations/BelongsTo/BelongsToRelationStandardRestoreOperationsTest.php

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -75,15 +75,7 @@ public function transforming_a_single_restored_relation_resource(): void
7575

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

78-
app()->bind(
79-
ComponentsResolver::class,
80-
function () {
81-
$componentsResolverMock = Mockery::mock(\Orion\Drivers\Standard\ComponentsResolver::class)->makePartial();
82-
$componentsResolverMock->shouldReceive('resolveResourceClass')->once()->andReturn(SampleResource::class);
83-
84-
return $componentsResolverMock;
85-
}
86-
);
78+
$this->useResource(SampleResource::class);
8779

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

@@ -102,4 +94,4 @@ public function restoring_a_single_relation_resource_and_getting_included_relati
10294

10395
$this->assertResourceRestored($response, $trashedCategory, ['posts' => $trashedCategory->posts->toArray()]);
10496
}
105-
}
97+
}

tests/Feature/Relations/BelongsTo/BelongsToRelationStandardShowOperationsTest.php

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@
33
namespace Orion\Tests\Feature\Relations\BelongsTo;
44

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

77-
app()->bind(
78-
ComponentsResolver::class,
79-
function () {
80-
$componentsResolverMock = Mockery::mock(\Orion\Drivers\Standard\ComponentsResolver::class)->makePartial();
81-
$componentsResolverMock->shouldReceive('resolveResourceClass')->once()->andReturn(SampleResource::class);
82-
83-
return $componentsResolverMock;
84-
}
85-
);
75+
$this->useResource(SampleResource::class);
8676

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

tests/Feature/Relations/BelongsTo/BelongsToRelationStandardUpdateOperationsTest.php

Lines changed: 3 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -76,15 +76,7 @@ public function updating_a_single_relation_resource_when_validation_fails(): voi
7676
$post = factory(Post::class)->create(['user_id' => $user->id]);
7777
$payload = ['email' => '[email protected]'];
7878

79-
app()->bind(
80-
ComponentsResolver::class,
81-
function () {
82-
$componentsResolverMock = Mockery::mock(\Orion\Drivers\Standard\ComponentsResolver::class)->makePartial();
83-
$componentsResolverMock->shouldReceive('resolveRequestClass')->once()->andReturn(UserRequest::class);
84-
85-
return $componentsResolverMock;
86-
}
87-
);
79+
$this->useRequest(UserRequest::class);
8880

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

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

105-
app()->bind(
106-
ComponentsResolver::class,
107-
function () {
108-
$componentsResolverMock = Mockery::mock(\Orion\Drivers\Standard\ComponentsResolver::class)->makePartial();
109-
$componentsResolverMock->shouldReceive('resolveResourceClass')->once()->andReturn(SampleResource::class);
110-
111-
return $componentsResolverMock;
112-
}
113-
);
97+
$this->useResource(SampleResource::class);
11498

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

@@ -144,4 +128,4 @@ public function updating_a_single_resource_and_getting_included_relation(): void
144128
['posts' => $user->fresh('posts')->posts->toArray()]
145129
);
146130
}
147-
}
131+
}

tests/Feature/Relations/BelongsToMany/BelongsToManyRelationStandardDeleteOperationsTest.php

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -182,15 +182,7 @@ public function transforming_a_single_deleted_relation_resource(): void
182182

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

185-
app()->bind(
186-
ComponentsResolver::class,
187-
function () {
188-
$componentsResolverMock = Mockery::mock(\Orion\Drivers\Standard\ComponentsResolver::class)->makePartial();
189-
$componentsResolverMock->shouldReceive('resolveResourceClass')->once()->andReturn(SampleResource::class);
190-
191-
return $componentsResolverMock;
192-
}
193-
);
185+
$this->useResource(SampleResource::class);
194186

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

@@ -215,4 +207,4 @@ public function deleting_a_single_relation_resource_and_getting_included_relatio
215207

216208
$this->assertResourceDeleted($response, $role);
217209
}
218-
}
210+
}

0 commit comments

Comments
 (0)