Skip to content

Commit 053fbfc

Browse files
authored
Repository related supports now a custom casts. (#302)
* Repository related supports now a custom casts. * Apply fixes from StyleCI (#303) * docs
1 parent 6bbdaec commit 053fbfc

File tree

12 files changed

+198
-35
lines changed

12 files changed

+198
-35
lines changed

config/config.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,22 @@
7878
AuthorizeRestify::class,
7979
],
8080

81+
/*
82+
|--------------------------------------------------------------------------
83+
| Used to format data.
84+
|--------------------------------------------------------------------------
85+
|
86+
*/
87+
'casts' => [
88+
/*
89+
|--------------------------------------------------------------------------
90+
| Casting the related entities format.
91+
|--------------------------------------------------------------------------
92+
|
93+
*/
94+
'related' => \Binaryk\LaravelRestify\Repositories\Casts\RelatedCast::class,
95+
],
96+
8197
/*
8298
|--------------------------------------------------------------------------
8399
| Restify Exception Handler

docs/docs/4.0/filtering/filtering.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,45 @@ This means that we could use `posts` query for eager loading posts:
260260
GET: /api/restify/users?related=posts
261261
```
262262

263+
## Custom data
264+
265+
You are not limited to add only relations under the `related` array. You can use whatever you want, for instance you can return a simple model, or a collection. Basically any serializable data could be added there. For example:
266+
267+
268+
```php
269+
public static $related = [
270+
'foo'
271+
];
272+
```
273+
274+
Then in the `Post` model we can define this method as:
275+
276+
```php
277+
public function foo() {
278+
return collect([1, 2]);
279+
}
280+
```
281+
282+
### Custom data format
283+
284+
You can use a custom related cast class (aka transformer). You can do so by modifying the `restify.casts.related` property. The default related cast is `Binaryk\LaravelRestify\Repositories\Casts\RelatedCast`.
285+
286+
The cast class should extends the `Binaryk\LaravelRestify\Repositories\Casts\RepositoryCast` abstract class.
287+
288+
This is the default cast:
289+
290+
```php
291+
'casts' => [
292+
/*
293+
|--------------------------------------------------------------------------
294+
| Casting the related entities format.
295+
|--------------------------------------------------------------------------
296+
|
297+
*/
298+
'related' => \Binaryk\LaravelRestify\Repositories\Casts\RelatedCast::class,
299+
],
300+
```
301+
263302
## Pagination
264303

265304
Laravel Restify has returns `index` items paginates. The default `perPage` is 15.

src/Fields/EagerField.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,14 @@ class EagerField extends Field
2222
*/
2323
public string $repositoryClass;
2424

25+
public function __construct($attribute, callable $resolveCallback = null)
26+
{
27+
parent::__construct($attribute, $resolveCallback);
28+
29+
$this->showOnShow()
30+
->hideFromIndex();
31+
}
32+
2533
/**
2634
* Determine if the field should be displayed for the given request.
2735
*

src/LaravelRestifyServiceProvider.php

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,6 @@ protected function registerPublishing()
7777
__DIR__.'/../config/config.php' => config_path('restify.php'),
7878
], 'restify-config');
7979

80-
if (! $this->app->configurationIsCached()) {
81-
$this->mergeConfigFrom(__DIR__.'/../config/config.php', 'laravel-restify');
82-
}
80+
$this->mergeConfigFrom(__DIR__.'/../config/config.php', 'restify');
8381
}
8482
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
namespace Binaryk\LaravelRestify\Repositories\Casts;
4+
5+
use Binaryk\LaravelRestify\Contracts\RestifySearchable;
6+
use Illuminate\Database\Eloquent\Builder;
7+
use Illuminate\Database\Eloquent\Relations\Relation;
8+
use Illuminate\Http\Request;
9+
use Illuminate\Support\Collection;
10+
11+
class RelatedCast extends RepositoryCast
12+
{
13+
public static function fromBuilder(Request $request, Builder $builder): Collection
14+
{
15+
return $builder->take($request->input('relatablePerPage') ?? (static::$defaultRelatablePerPage ?? RestifySearchable::DEFAULT_RELATABLE_PER_PAGE))->get();
16+
}
17+
18+
public static function fromRelation(Request $request, Relation $relation): Collection
19+
{
20+
return $relation->take($request->input('relatablePerPage') ?? (static::$defaultRelatablePerPage ?? RestifySearchable::DEFAULT_RELATABLE_PER_PAGE))->get();
21+
}
22+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
namespace Binaryk\LaravelRestify\Repositories\Casts;
4+
5+
use Illuminate\Database\Eloquent\Builder;
6+
use Illuminate\Database\Eloquent\Relations\Relation;
7+
use Illuminate\Http\Request;
8+
use Illuminate\Support\Collection;
9+
10+
abstract class RepositoryCast
11+
{
12+
abstract public static function fromBuilder(Request $request, Builder $builder): Collection;
13+
14+
abstract public static function fromRelation(Request $request, Relation $relation): Collection;
15+
}

src/Repositories/Repository.php

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
use Binaryk\LaravelRestify\Fields\Field;
1111
use Binaryk\LaravelRestify\Fields\FieldCollection;
1212
use Binaryk\LaravelRestify\Filter;
13-
use Binaryk\LaravelRestify\Http\Requests\RepositoryShowRequest;
1413
use Binaryk\LaravelRestify\Http\Requests\RepositoryStoreBulkRequest;
1514
use Binaryk\LaravelRestify\Http\Requests\RestifyRequest;
1615
use Binaryk\LaravelRestify\Models\CreationAware;
@@ -513,7 +512,7 @@ public function resolveRelationships($request): array
513512
$withs = collect();
514513

515514
/** * To avoid circular relationships and deep stack calls, we will do not load eager fields. */
516-
if (! $this->isEagerState() && $request instanceof RepositoryShowRequest) {
515+
if (! $this->isEagerState()) {
517516
$this->collectFields($request)
518517
->forEager($request, $this)
519518
->filter(fn (EagerField $field) => $field->isShownOnShow($request, $this))
@@ -534,9 +533,9 @@ public function resolveRelationships($request): array
534533
: $this->resource->{$relation}();
535534

536535
collect([
537-
Builder::class => fn () => $withs->put($relation, $paginator->take($request->input('relatablePerPage') ?? (static::$defaultRelatablePerPage ?? RestifySearchable::DEFAULT_RELATABLE_PER_PAGE))->get()),
536+
Builder::class => fn () => $withs->put($relation, (static::$relatedCast)::fromBuilder($request, $paginator)),
538537

539-
Relation::class => fn () => $withs->put($relation, $paginator->take($request->input('relatablePerPage') ?? (static::$defaultRelatablePerPage ?? RestifySearchable::DEFAULT_RELATABLE_PER_PAGE))->get()),
538+
Relation::class => fn () => $withs->put($relation, (static::$relatedCast)::fromRelation($request, $paginator)),
540539

541540
Collection::class => fn () => $withs->put($relation, $paginator),
542541

src/Repositories/RepositoryEvents.php

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,44 @@
22

33
namespace Binaryk\LaravelRestify\Repositories;
44

5+
use Binaryk\LaravelRestify\Repositories\Casts\RepositoryCast;
6+
57
trait RepositoryEvents
68
{
9+
/**
10+
* Used to convert collections for relations.
11+
*
12+
* @var RepositoryCast
13+
*/
14+
public static RepositoryCast $relatedCast;
15+
716
/**
817
* The array of booted repositories.
918
*
1019
* @var array
1120
*/
1221
protected static $booted = [];
1322

23+
/**
24+
* Perform any actions required before the repository boots.
25+
*
26+
* @return void
27+
*/
28+
protected static function booting()
29+
{
30+
//
31+
}
32+
33+
/**
34+
* Boot the repository.
35+
*
36+
* @return void
37+
*/
38+
protected static function boot()
39+
{
40+
static::$relatedCast = app(config('restify.casts.related'));
41+
}
42+
1443
/**
1544
* Perform any actions required after the repository boots.
1645
*
@@ -26,6 +55,8 @@ protected function bootIfNotBooted()
2655
if (! isset(static::$booted[static::class])) {
2756
static::$booted[static::class] = true;
2857

58+
static::booting();
59+
static::boot();
2960
static::booted();
3061
}
3162
}

tests/Controllers/RepositoryIndexControllerTest.php

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use Binaryk\LaravelRestify\Tests\Fixtures\Company\CompanyRepository;
77
use Binaryk\LaravelRestify\Tests\Fixtures\Post\Post;
88
use Binaryk\LaravelRestify\Tests\Fixtures\Post\PostRepository;
9+
use Binaryk\LaravelRestify\Tests\Fixtures\Post\RelatedCastWithAttributes;
910
use Binaryk\LaravelRestify\Tests\Fixtures\User\User;
1011
use Binaryk\LaravelRestify\Tests\IntegrationTest;
1112
use Illuminate\Foundation\Testing\RefreshDatabase;
@@ -66,14 +67,14 @@ public function test_repository_order()
6667

6768
$response = $this
6869
->getJson('posts?sort=-title')
69-
->assertStatus(200);
70+
->assertOk();
7071

7172
$this->assertEquals('zzz', $response->json('data.0.attributes.title'));
7273
$this->assertEquals('aaa', $response->json('data.1.attributes.title'));
7374

7475
$response = $this
7576
->getJson('posts?order=-title')
76-
->assertStatus(200);
77+
->assertOk();
7778

7879
$this->assertEquals('zzz', $response->json('data.1.attributes.title'));
7980
$this->assertEquals('aaa', $response->json('data.0.attributes.title'));
@@ -88,12 +89,39 @@ public function test_repository_with_relations()
8889
factory(Post::class)->create(['user_id' => $user->id]);
8990

9091
$response = $this->getJson('posts?related=user')
91-
->assertStatus(200);
92+
->assertOk();
9293

9394
$this->assertCount(1, $response->json('data.0.relationships.user'));
9495
$this->assertArrayNotHasKey('user', $response->json('data.0.attributes'));
9596
}
9697

98+
public function test_using_custom_related_casts()
99+
{
100+
PostRepository::$related = ['user'];
101+
102+
config([
103+
'restify.casts.related' => RelatedCastWithAttributes::class,
104+
]);
105+
106+
$user = $this->mockUsers(1)->first();
107+
108+
factory(Post::class)->create(['user_id' => $user->id]);
109+
110+
$this->getJson('posts?related=user')
111+
->assertOk()
112+
->assertJsonStructure([
113+
'data' => [
114+
[
115+
'relationships' => [
116+
'user' => [
117+
['attributes'],
118+
],
119+
],
120+
],
121+
],
122+
]);
123+
}
124+
97125
public function test_repository_with_deep_relations()
98126
{
99127
CompanyRepository::$related = ['users.posts'];
@@ -124,7 +152,7 @@ public function test_paginated_repository_with_relations()
124152
factory(Post::class, 20)->create(['user_id' => $user->id]);
125153

126154
$response = $this->getJson('posts?related=user&page=2')
127-
->assertStatus(200);
155+
->assertOk();
128156

129157
$this->assertCount(1, $response->json('data.0.relationships.user'));
130158
$this->assertArrayNotHasKey('user', $response->json('data.0.attributes'));

tests/Feature/Authentication/AuthServiceRegisterTest.php

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,9 @@
22

33
namespace Binaryk\LaravelRestify\Tests\Feature\Authentication;
44

5-
use Binaryk\LaravelRestify\Contracts\Passportable;
6-
use Binaryk\LaravelRestify\Exceptions\AuthenticatableUserException;
75
use Binaryk\LaravelRestify\Exceptions\Eloquent\EntityNotFoundException;
86
use Binaryk\LaravelRestify\Models\LaravelRestifyModel;
97
use Binaryk\LaravelRestify\Services\AuthService;
10-
use Binaryk\LaravelRestify\Tests\Fixtures\User\SampleUser;
118
use Binaryk\LaravelRestify\Tests\Fixtures\User\User;
129
use Binaryk\LaravelRestify\Tests\IntegrationTest;
1310
use Illuminate\Auth\Access\AuthorizationException;
@@ -35,27 +32,6 @@ protected function setUp(): void
3532
$this->authService = resolve(AuthService::class);
3633
}
3734

38-
public function test_register_throw_user_not_authenticatable()
39-
{
40-
$this->app->instance(User::class, (new class extends SampleUser implements Passportable {
41-
}));
42-
43-
$user = [
44-
'name' => 'Eduard Lupacescu',
45-
'email' => '[email protected]',
46-
'password' => 'password',
47-
'password_confirmation' => 'password',
48-
'remember_token' => Str::random(10),
49-
];
50-
51-
$request = new Request([], []);
52-
53-
$request->merge($user);
54-
55-
$this->expectException(AuthenticatableUserException::class);
56-
$this->authService->register($request);
57-
}
58-
5935
public function test_user_query_throw_container_does_not_have_model_reflection_exception()
6036
{
6137
$this->app['config']->set('auth.providers.users.model', null);

0 commit comments

Comments
 (0)