Skip to content

Commit 6907c1e

Browse files
authored
Matches/definition (#316)
* Definition with related. * Apply fixes from StyleCI (#313) * wip * Apply fixes from StyleCI (#314) * wip * Apply fixes from StyleCI (#315) * Matches improvement. * Apply fixes from StyleCI (#317)
1 parent 609bed8 commit 6907c1e

File tree

12 files changed

+181
-41
lines changed

12 files changed

+181
-41
lines changed

src/Commands/StubCommand.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ protected function make($table)
9393
break;
9494
case 'bigint':
9595
case 'int':
96+
case 'integer':
9697
if ($columnDefinition->getAutoincrement() === true) {
9798
//primary key
9899
return;

src/Eager/Related.php

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
3+
namespace Binaryk\LaravelRestify\Eager;
4+
5+
use Binaryk\LaravelRestify\Fields\EagerField;
6+
use Binaryk\LaravelRestify\Repositories\Repository;
7+
use Binaryk\LaravelRestify\Traits\Make;
8+
9+
class Related
10+
{
11+
use Make;
12+
13+
private string $relation;
14+
15+
private ?EagerField $field;
16+
17+
public function __construct(string $relation, EagerField $field = null)
18+
{
19+
$this->relation = $relation;
20+
$this->field = $field;
21+
}
22+
23+
public function isEager(): bool
24+
{
25+
return ! is_null($this->field);
26+
}
27+
28+
public function getRelation(): string
29+
{
30+
return $this->relation;
31+
}
32+
33+
public function resolveField(Repository $repository): EagerField
34+
{
35+
return $this->field->resolve($repository);
36+
}
37+
}

src/Eager/RelatedCollection.php

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<?php
2+
3+
namespace Binaryk\LaravelRestify\Eager;
4+
5+
use Binaryk\LaravelRestify\Fields\EagerField;
6+
use Binaryk\LaravelRestify\Fields\Field;
7+
use Binaryk\LaravelRestify\Http\Requests\RestifyRequest;
8+
use Illuminate\Support\Collection;
9+
10+
class RelatedCollection extends Collection
11+
{
12+
public function intoAssoc(): self
13+
{
14+
return $this->mapWithKeys(function ($value, $key) {
15+
return [
16+
is_numeric($key) ? $value : $key => $value,
17+
];
18+
});
19+
}
20+
21+
public function forEager(RestifyRequest $request): self
22+
{
23+
return $this->filter(fn ($value, $key) => $value instanceof EagerField)
24+
->filter(fn (Field $field) => $field->authorize($request))
25+
->unique('attribute');
26+
}
27+
28+
public function inRequest(RestifyRequest $request): self
29+
{
30+
return $this
31+
->filter(fn ($field, $key) => in_array($key, str_getcsv($request->input('related'))))
32+
->unique();
33+
}
34+
35+
public function mapIntoRelated(RestifyRequest $request)
36+
{
37+
return $this->map(function ($value, $key) {
38+
return Related::make($key, $value instanceof EagerField ? $value : null);
39+
});
40+
}
41+
42+
public function authorized(RestifyRequest $request)
43+
{
44+
return $this->intoAssoc()
45+
->filter(fn ($key, $value) => $key instanceof EagerField ? $key->authorize($request) : true);
46+
}
47+
}

src/Fields/EagerField.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace Binaryk\LaravelRestify\Fields;
44

5+
use Binaryk\LaravelRestify\Repositories\Repository;
56
use Illuminate\Auth\Access\AuthorizationException;
67
use Illuminate\Http\Request;
78
use Illuminate\Support\Facades\Gate;
@@ -18,7 +19,7 @@ class EagerField extends Field
1819
/**
1920
* The class name of the related repository.
2021
*
21-
* @var string
22+
* @var Repository
2223
*/
2324
public string $repositoryClass;
2425

@@ -55,9 +56,10 @@ public function resolve($repository, $attribute = null)
5556
->eagerState();
5657
} catch (AuthorizationException $e) {
5758
$class = get_class($relatedModel);
59+
$field = class_basename(get_called_class());
5860
$policy = get_class(Gate::getPolicyFor($relatedModel));
5961

60-
abort(403, "You are not authorized to see the [{$class}] relationship from the BelongsTo field from the BelongsTo field. Check the [show] method from the [$policy]");
62+
abort(403, "You are not authorized to see the [{$class}] relationship from the {$field} field from the {$field} field. Check the [show] method from the [$policy]");
6163
}
6264

6365
return $this;

src/Repositories/Concerns/Mockable.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use Mockery;
66
use Mockery\MockInterface;
7+
use RuntimeException;
78

89
trait Mockable
910
{
@@ -151,7 +152,7 @@ public static function clearResolvedInstances()
151152
*
152153
* @return string
153154
*
154-
* @throws \RuntimeException
155+
* @throws RuntimeException
155156
*/
156157
public static function uriKey()
157158
{

src/Repositories/Repository.php

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use Binaryk\LaravelRestify\Contracts\RestifySearchable;
66
use Binaryk\LaravelRestify\Controllers\RestResponse;
7+
use Binaryk\LaravelRestify\Eager\Related;
78
use Binaryk\LaravelRestify\Exceptions\InstanceOfException;
89
use Binaryk\LaravelRestify\Fields\BelongsToMany;
910
use Binaryk\LaravelRestify\Fields\EagerField;
@@ -511,23 +512,24 @@ public function resolveRelationships($request): array
511512
{
512513
$withs = collect();
513514

514-
/** * To avoid circular relationships and deep stack calls, we will do not load eager fields. */
515-
if (! $this->isEagerState()) {
516-
$this->collectFields($request)
517-
->forEager($request, $this)
518-
->filter(fn (EagerField $field) => $field->isShownOnShow($request, $this))
519-
->each(fn (EagerField $field) => $withs->put($field->attribute, $field->resolve($this)->value));
520-
}
515+
static::collectRelated()
516+
->authorized($request)
517+
->inRequest($request)
518+
->mapIntoRelated($request)
519+
->each(function (Related $related) use ($request, $withs) {
520+
$relation = $related->getRelation();
521521

522-
collect(str_getcsv($request->input('related')))
523-
->filter(fn ($relation) => in_array($relation, static::getRelated()))
524-
->each(function ($relation) use ($request, $withs) {
525522
if (Str::contains($relation, '.')) {
526523
$this->resource->loadMissing($relation);
527524

528525
return $withs->put($key = Str::before($relation, '.'), Arr::get($this->resource->relationsToArray(), $key));
529526
}
530527

528+
/** * To avoid circular relationships and deep stack calls, we will do not load eager fields. */
529+
if ($related->isEager() && $this->isEagerState() === false) {
530+
return $withs->put($relation, $related->resolveField($this)->value);
531+
}
532+
531533
$paginator = $this->resource->relationLoaded($relation)
532534
? $this->resource->{$relation}
533535
: $this->resource->{$relation}();
@@ -861,6 +863,11 @@ public function allowToDestroy(RestifyRequest $request)
861863
return $this;
862864
}
863865

866+
/**
867+
* @param $request
868+
* @return $this
869+
* @throws \Illuminate\Auth\Access\AuthorizationException
870+
*/
864871
public function allowToShow($request): self
865872
{
866873
$this->authorizeToShow($request);

src/Traits/InteractWithSearch.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace Binaryk\LaravelRestify\Traits;
44

5+
use Binaryk\LaravelRestify\Eager\RelatedCollection;
56
use Binaryk\LaravelRestify\Filter;
67
use Binaryk\LaravelRestify\Filters\MatchFilter;
78
use Binaryk\LaravelRestify\Filters\SearchableFilter;
@@ -42,6 +43,11 @@ public static function getRelated()
4243
return static::$related ?? [];
4344
}
4445

46+
public static function collectRelated(): RelatedCollection
47+
{
48+
return RelatedCollection::make(static::getRelated());
49+
}
50+
4551
/**
4652
* @return array
4753
*/

tests/Fields/BelongsToFieldTest.php

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,20 +36,32 @@ protected function tearDown(): void
3636
Repository::clearResolvedInstances();
3737
}
3838

39-
public function test_present_on_relations()
39+
public function test_present_on_show_when_specified_related()
4040
{
4141
$post = factory(Post::class)->create([
4242
'user_id' => factory(User::class),
4343
]);
4444

45-
$this->get(PostWithUserRepository::uriKey()."/$post->id")
45+
$relationships = $this->get(PostWithUserRepository::uriKey()."/$post->id?related=user")
4646
->assertJsonStructure([
4747
'data' => [
4848
'relationships' => [
49-
'user',
49+
'user' => [
50+
'id',
51+
'type',
52+
'attributes',
53+
],
5054
],
5155
],
52-
]);
56+
])
57+
->json('data.relationships');
58+
59+
$this->assertNotNull($relationships);
60+
61+
$relationships = $this->get(PostWithUserRepository::uriKey()."/$post->id")
62+
->json('data.relationships');
63+
64+
$this->assertNull($relationships);
5365
}
5466

5567
public function test_unauthorized_see_relationship()
@@ -61,7 +73,7 @@ public function test_unauthorized_see_relationship()
6173
tap(factory(Post::class)->create([
6274
'user_id' => factory(User::class),
6375
]), function ($post) {
64-
$this->get(PostWithUserRepository::uriKey()."/{$post->id}")
76+
$this->get(PostWithUserRepository::uriKey()."/{$post->id}?related=user")
6577
->assertForbidden();
6678
});
6779
}
@@ -179,6 +191,13 @@ class PostWithUserRepository extends Repository
179191
{
180192
public static $model = Post::class;
181193

194+
public static function getRelated()
195+
{
196+
return [
197+
'user' => BelongsTo::make('user', 'user', UserRepository::class),
198+
];
199+
}
200+
182201
public function fields(RestifyRequest $request)
183202
{
184203
return [

tests/Fields/BelongsToManyFieldTest.php

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,15 @@ protected function setUp(): void
2222
]);
2323
}
2424

25-
public function test_displays_on_relationships_show()
25+
public function test_belongs_to_many_displays_on_relationships_show()
2626
{
2727
$company = tap(factory(Company::class)->create(), function (Company $company) {
2828
$company->users()->attach(
2929
factory(User::class, 5)->create()
3030
);
3131
});
3232

33-
$this->get(CompanyWithUsersRepository::uriKey()."/{$company->id}")
33+
$this->get(CompanyWithUsersRepository::uriKey()."/{$company->id}?related=users")
3434
->assertJsonStructure([
3535
'data' => [
3636
'relationships' => [
@@ -86,18 +86,23 @@ class CompanyWithUsersRepository extends Repository
8686
{
8787
public static $model = Company::class;
8888

89-
public function fields(RestifyRequest $request)
89+
public static function getRelated()
9090
{
9191
return [
92-
field('name'),
93-
94-
BelongsToMany::make('users', 'users', UserRepository::class)
92+
'users' => BelongsToMany::make('users', 'users', UserRepository::class)
9593
->hideFromShow(function () {
9694
return $_SERVER['hide_users_from_show'] ?? false;
9795
}),
9896
];
9997
}
10098

99+
public function fields(RestifyRequest $request)
100+
{
101+
return [
102+
field('name'),
103+
];
104+
}
105+
101106
public static function uriKey()
102107
{
103108
return 'companies-with-users-repository';

tests/Fields/HasManyTest.php

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,13 @@ protected function tearDown(): void
3636
Repository::clearResolvedInstances();
3737
}
3838

39-
public function test_present_on_relations()
39+
public function test_has_many_present_on_relations()
4040
{
4141
$post = factory(Post::class)->create([
4242
'user_id' => factory(User::class),
4343
]);
4444

45-
$this->get(UserWithPosts::uriKey()."/$post->id")
45+
$this->get(UserWithPosts::uriKey()."/$post->id?related=posts")
4646
->assertJsonStructure([
4747
'data' => [
4848
'relationships' => [
@@ -52,17 +52,17 @@ public function test_present_on_relations()
5252
]);
5353
}
5454

55-
public function test_paginated_on_relation()
55+
public function test_has_many_paginated_on_relation()
5656
{
5757
$user = tap($this->mockUsers()->first(), function ($user) {
5858
$this->mockPosts($user->id, 22);
5959
});
6060

61-
$this->get(UserWithPosts::uriKey()."/{$user->id}?relatablePerPage=20")
61+
$this->get(UserWithPosts::uriKey()."/{$user->id}?related=posts&relatablePerPage=20")
6262
->assertJsonCount(20, 'data.relationships.posts');
6363
}
6464

65-
public function test_unauthorized_see_relationship_posts()
65+
public function test_has_many_unauthorized_see_relationship_posts()
6666
{
6767
$_SERVER['restify.post.show'] = false;
6868

@@ -71,7 +71,7 @@ public function test_unauthorized_see_relationship_posts()
7171
$this->mockPosts($user->id, 20);
7272
});
7373

74-
$this->get(UserWithPosts::uriKey()."/$user->id")
74+
$this->get(UserWithPosts::uriKey()."/$user->id?related=posts")
7575
->assertForbidden();
7676
}
7777

@@ -290,14 +290,19 @@ class UserWithPosts extends Repository
290290
{
291291
public static $model = User::class;
292292

293+
public static function getRelated()
294+
{
295+
return [
296+
'posts' => HasMany::make('posts', 'posts', PostRepository::class),
297+
];
298+
}
299+
293300
public function fields(RestifyRequest $request)
294301
{
295302
return [
296303
field('name'),
297304
field('email'),
298305
field('password'),
299-
300-
HasMany::make('posts', 'posts', PostRepository::class),
301306
];
302307
}
303308
}

0 commit comments

Comments
 (0)