feat: add fieldsBefore and fieldsAfter ApiResource extenders (#4106)

This commit is contained in:
Sami Mazouz 2024-11-09 14:50:39 +01:00 committed by GitHub
parent c401e678f3
commit 3b69af2ae6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 403 additions and 331 deletions

View File

@ -25,6 +25,8 @@ class ApiResource implements ExtenderInterface
private array $removeEndpoints = [];
private array $endpoint = [];
private array $fields = [];
private array $fieldsBefore = [];
private array $fieldsAfter = [];
private array $removeFields = [];
private array $field = [];
private array $sorts = [];
@ -93,6 +95,32 @@ class ApiResource implements ExtenderInterface
return $this;
}
/**
* Add fields to the resource before a certain field.
*
* @param string $before the name of the field to add the new fields before.
* @param callable|class-string $fields must be a callable that returns an array of objects that implement \Tobyz\JsonApiServer\Schema\Field.
*/
public function fieldsBefore(string $before, callable|string $fields): self
{
$this->fieldsBefore[] = [$before, $fields];
return $this;
}
/**
* Add fields to the resource after a certain field.
*
* @param string $after the name of the field to add the new fields after.
* @param callable|class-string $fields must be a callable that returns an array of objects that implement \Tobyz\JsonApiServer\Schema\Field.
*/
public function fieldsAfter(string $after, callable|string $fields): self
{
$this->fieldsAfter[] = [$after, $fields];
return $this;
}
/**
* Remove fields from the resource.
*
@ -221,6 +249,26 @@ class ApiResource implements ExtenderInterface
$fields = array_merge($fields, $newFieldsCallback());
}
foreach ($this->fieldsBefore as [$before, $newFieldsCallback]) {
$newFieldsCallback = ContainerUtil::wrapCallback($newFieldsCallback, $container);
$newFields = $newFieldsCallback();
$beforeIndex = array_search($before, array_column($fields, 'name'));
if ($beforeIndex !== false) {
array_splice($fields, $beforeIndex, 0, $newFields);
}
}
foreach ($this->fieldsAfter as [$after, $newFieldsCallback]) {
$newFieldsCallback = ContainerUtil::wrapCallback($newFieldsCallback, $container);
$newFields = $newFieldsCallback();
$afterIndex = array_search($after, array_column($fields, 'name'));
if ($afterIndex !== false) {
array_splice($fields, $afterIndex + 1, 0, $newFields);
}
}
foreach ($this->removeFields as $field) {
[$fieldsToRemove, $condition] = $field;

View File

@ -15,8 +15,9 @@ use Flarum\Api\Endpoint\Index;
use Flarum\Api\Endpoint\Show;
use Flarum\Api\Resource\AbstractDatabaseResource;
use Flarum\Api\Resource\DiscussionResource;
use Flarum\Api\Resource\ForumResource;
use Flarum\Api\Resource\UserResource;
use Flarum\Api\Schema\Relationship\ToMany;
use Flarum\Api\Schema;
use Flarum\Api\Sort\SortColumn;
use Flarum\Discussion\Discussion;
use Flarum\Extend;
@ -25,11 +26,12 @@ use Flarum\Post\Post;
use Flarum\Testing\integration\RetrievesAuthorizedUsers;
use Flarum\Testing\integration\TestCase;
use Flarum\User\User;
use Illuminate\Database\PostgresConnection;
use Illuminate\Support\Arr;
use PHPUnit\Framework\Attributes\Test;
use Tobyz\JsonApiServer\Schema\Field\Field;
class ApiControllerTest extends TestCase
class ApiResourceTest extends TestCase
{
use RetrievesAuthorizedUsers;
@ -45,15 +47,21 @@ class ApiControllerTest extends TestCase
$this->normalUser()
],
Discussion::class => [
['id' => 1, 'title' => 'Custom Discussion Title', 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 2, 'first_post_id' => 0, 'comment_count' => 1, 'is_private' => 0],
['id' => 2, 'title' => 'Custom Discussion Title', 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 3, 'first_post_id' => 0, 'comment_count' => 1, 'is_private' => 0],
['id' => 3, 'title' => 'Custom Discussion Title', 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 1, 'first_post_id' => 0, 'comment_count' => 1, 'is_private' => 0],
['id' => 1, 'title' => 'Custom Discussion Title', 'created_at' => Carbon::now()->addMinutes(1)->toDateTimeString(), 'user_id' => 2, 'first_post_id' => 0, 'comment_count' => 1, 'is_private' => 0],
['id' => 2, 'title' => 'Custom Discussion Title', 'created_at' => Carbon::now()->addMinutes(2)->toDateTimeString(), 'user_id' => 3, 'first_post_id' => 0, 'comment_count' => 1, 'is_private' => 0],
['id' => 3, 'title' => 'Custom Discussion Title', 'created_at' => Carbon::now()->addMinutes(3)->toDateTimeString(), 'user_id' => 1, 'first_post_id' => 0, 'comment_count' => 1, 'is_private' => 0],
['id' => 4, 'title' => 'Custom Discussion Title', 'created_at' => Carbon::now()->addMinutes(4)->toDateTimeString(), 'user_id' => 2, 'first_post_id' => 0, 'comment_count' => 1, 'is_private' => 0],
['id' => 5, 'title' => 'Custom Discussion Title', 'created_at' => Carbon::now()->addMinutes(5)->toDateTimeString(), 'user_id' => 2, 'first_post_id' => 0, 'comment_count' => 1, 'is_private' => 0],
['id' => 6, 'title' => 'Custom Discussion Title', 'created_at' => Carbon::now()->addMinutes(6)->toDateTimeString(), 'user_id' => 2, 'first_post_id' => 0, 'comment_count' => 1, 'is_private' => 0],
],
Post::class => [
['id' => 1, 'discussion_id' => 3, 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 2, 'type' => 'discussionRenamed', 'content' => '<t><p>can i haz relationz?</p></t>'],
['id' => 2, 'discussion_id' => 2, 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 2, 'type' => 'discussionRenamed', 'content' => '<t><p>can i haz relationz?</p></t>'],
['id' => 3, 'discussion_id' => 1, 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 2, 'type' => 'discussionRenamed', 'content' => '<t><p>can i haz relationz?</p></t>'],
['id' => 3, 'discussion_id' => 1, 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 1, 'type' => 'discussionRenamed', 'content' => '<t><p>can i haz relationz?</p></t>'],
['id' => 5, 'discussion_id' => 6, 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 2, 'type' => 'discussionRenamed', 'content' => '<t><p>can i haz relationz?</p></t>'],
],
]);
}
@ -243,7 +251,7 @@ class ApiControllerTest extends TestCase
->hasMany('customApiControllerRelation', Discussion::class, 'user_id'),
(new Extend\ApiResource(UserResource::class))
->fields(fn () => [
ToMany::make('customApiControllerRelation')
Schema\Relationship\ToMany::make('customApiControllerRelation')
->type('discussions')
->includable(),
])
@ -271,7 +279,7 @@ class ApiControllerTest extends TestCase
->hasMany('customApiControllerRelation2', Discussion::class, 'user_id'),
(new Extend\ApiResource(UserResource::class))
->fields(fn () => [
ToMany::make('customApiControllerRelation2')
Schema\Relationship\ToMany::make('customApiControllerRelation2')
->type('discussions')
->includable(),
])
@ -333,7 +341,7 @@ class ApiControllerTest extends TestCase
->hasMany('customApiControllerRelation2', Discussion::class, 'user_id'),
(new Extend\ApiResource(UserResource::class))
->fields(fn () => [
ToMany::make('customApiControllerRelation2')
Schema\Relationship\ToMany::make('customApiControllerRelation2')
->type('discussions')
->includable(),
])
@ -362,7 +370,7 @@ class ApiControllerTest extends TestCase
$payload = json_decode($response->getBody()->getContents(), true);
$this->assertCount(3, $payload['data']);
$this->assertCount(6, $payload['data']);
}
#[Test]
@ -444,7 +452,7 @@ class ApiControllerTest extends TestCase
$payload = json_decode($body = $response->getBody()->getContents(), true);
$this->assertEquals(200, $response->getStatusCode(), $body);
$this->assertEquals([3, 1, 2], Arr::pluck($payload['data'], 'id'));
$this->assertEquals([3, 1, 4, 5, 6, 2], Arr::pluck($payload['data'], 'id'));
}
#[Test]
@ -502,7 +510,12 @@ class ApiControllerTest extends TestCase
$payload = json_decode($response->getBody()->getContents(), true);
$this->assertEquals(200, $response->getStatusCode());
$this->assertEquals([2, 1, 3], Arr::pluck($payload['data'], 'id'));
if ($this->database() instanceof PostgresConnection) {
$this->assertEquals([2, 1, 4, 5, 6, 3], Arr::pluck($payload['data'], 'id'));
} else {
$this->assertEquals([2, 6, 5, 4, 1, 3], Arr::pluck($payload['data'], 'id'));
}
}
#[Test]
@ -709,6 +722,326 @@ class ApiControllerTest extends TestCase
$this->assertFalse($users->pluck('firstLevelRelation')->filter->relationLoaded('secondLevelRelation')->isEmpty());
}
#[Test]
public function custom_attributes_dont_exist_by_default()
{
$this->app();
$response = $this->send(
$this->request('GET', '/api', [
'authenticatedAs' => 1,
])
);
$payload = json_decode($response->getBody()->getContents(), true);
$this->assertArrayNotHasKey('customAttribute', $payload['data']['attributes']);
}
#[Test]
public function custom_attributes_exist_if_added()
{
$this->extend(
(new Extend\ApiResource(ForumResource::class))
->fields(fn () => [
Schema\Boolean::make('customAttribute')
->get(fn () => true),
])
);
$this->app();
$response = $this->send(
$this->request('GET', '/api', [
'authenticatedAs' => 1,
])
);
$payload = json_decode($response->getBody()->getContents(), true);
$this->assertArrayHasKey('customAttribute', $payload['data']['attributes']);
}
#[Test]
public function custom_attributes_exist_if_added_before_field()
{
$this->extend(
(new Extend\ApiResource(DiscussionResource::class))
->fieldsBefore('title', fn () => [
Schema\Boolean::make('customAttribute')
->writable()
->set(fn (Discussion $discussion) => $this->assertNull($discussion->title))
])
);
$response = $this->send(
$this->request('POST', '/api/discussions', [
'authenticatedAs' => 1,
'json' => [
'data' => [
'type' => 'discussions',
'attributes' => [
'title' => 'Custom Discussion Title',
'customAttribute' => true,
'content' => 'Custom Discussion Content',
],
],
],
])
);
$this->assertEquals(201, $response->getStatusCode(), $response->getBody()->getContents());
}
#[Test]
public function custom_attributes_exist_if_added_after_field()
{
$this->extend(
(new Extend\ApiResource(DiscussionResource::class))
->fieldsAfter('title', fn () => [
Schema\Boolean::make('customAttribute')
->writable()
->set(fn (Discussion $discussion) => $this->assertNotNull($discussion->title))
])
);
$response = $this->send(
$this->request('POST', '/api/discussions', [
'authenticatedAs' => 1,
'json' => [
'data' => [
'type' => 'discussions',
'attributes' => [
'title' => 'Custom Discussion Title',
'customAttribute' => true,
'content' => 'Custom Discussion Content',
],
],
],
])
);
$this->assertEquals(201, $response->getStatusCode(), $response->getBody()->getContents());
}
#[Test]
public function custom_attributes_with_invokable_exist_if_added()
{
$this->extend(
(new Extend\ApiResource(ForumResource::class))
->fields(CustomAttributesInvokableClass::class)
);
$this->app();
$response = $this->send(
$this->request('GET', '/api', [
'authenticatedAs' => 1,
])
);
$payload = json_decode($response->getBody()->getContents(), true);
$this->assertArrayHasKey('customAttributeFromInvokable', $payload['data']['attributes']);
}
#[Test]
public function custom_attributes_exist_if_added_to_parent_class()
{
$this->extend(
(new Extend\ApiResource(AbstractDatabaseResource::class))
->fields(fn () => [
Schema\Boolean::make('customAttribute')
->get(fn () => true),
])
);
$this->app();
$response = $this->send(
$this->request('GET', '/api/users/2', [
'authenticatedAs' => 1,
])
);
$payload = json_decode($response->getBody()->getContents(), true);
$this->assertArrayHasKey('customAttribute', $payload['data']['attributes']);
}
#[Test]
public function custom_attributes_prioritize_child_classes()
{
$this->extend(
(new Extend\ApiResource(AbstractDatabaseResource::class))
->fields(fn () => [
Schema\Str::make('customAttribute')
->get(fn () => 'initialValue')
]),
(new Extend\ApiResource(UserResource::class))
->fields(fn () => [
Schema\Str::make('customAttribute')
->get(fn () => 'newValue')
]),
);
$this->app();
$response = $this->send(
$this->request('GET', '/api/users/2', [
'authenticatedAs' => 1,
])
);
$payload = json_decode($response->getBody()->getContents(), true);
$this->assertArrayHasKey('customAttribute', $payload['data']['attributes']);
$this->assertEquals('newValue', $payload['data']['attributes']['customAttribute']);
}
#[Test]
public function custom_attributes_can_be_overridden()
{
$this->extend(
(new Extend\ApiResource(UserResource::class))
->fields(fn () => [
Schema\Str::make('someCustomAttribute')
->get(fn () => 'newValue'),
])
->fields(fn () => [
Schema\Str::make('someCustomAttribute')
->get(fn () => 'secondValue'),
Schema\Str::make('someOtherCustomAttribute')
->get(fn () => 'secondValue'),
])
->fields(fn () => [
Schema\Str::make('someOtherCustomAttribute')
->get(fn () => 'newValue'),
])
);
$this->app();
$response = $this->send(
$this->request('GET', '/api/users/2', [
'authenticatedAs' => 1,
])
);
$payload = json_decode($response->getBody()->getContents(), true);
$this->assertArrayHasKey('someCustomAttribute', $payload['data']['attributes']);
$this->assertEquals('secondValue', $payload['data']['attributes']['someCustomAttribute']);
$this->assertArrayHasKey('someOtherCustomAttribute', $payload['data']['attributes']);
$this->assertEquals('newValue', $payload['data']['attributes']['someOtherCustomAttribute']);
}
#[Test]
public function custom_relations_dont_exist_by_default()
{
$this->extend(
(new Extend\ApiResource(UserResource::class))
->endpoint(Show::class, function (Show $endpoint): Show {
return $endpoint->addDefaultInclude(['customSerializerRelation', 'postCustomRelation', 'anotherCustomRelation']);
})
);
$response = $this->send(
$this->request('GET', '/api/users/2', [
'authenticatedAs' => 1,
])
);
$this->assertEquals(400, $response->getStatusCode());
}
#[Test]
public function custom_hasMany_relationship_exists_if_added()
{
$this->extend(
(new Extend\Model(User::class))
->hasMany('customSerializerRelation', Discussion::class, 'user_id'),
(new Extend\ApiResource(UserResource::class))
->fields(fn () => [
Schema\Relationship\ToMany::make('customSerializerRelation')
->type('discussions')
->includable()
])
->endpoint(Show::class, function (Show $endpoint) {
return $endpoint->addDefaultInclude(['customSerializerRelation']);
})
);
$response = $this->send(
$this->request('GET', '/api/users/2', [
'authenticatedAs' => 1,
])
);
$responseJson = json_decode($response->getBody(), true);
$this->assertArrayHasKey('customSerializerRelation', $responseJson['data']['relationships']);
$this->assertCount(4, $responseJson['data']['relationships']['customSerializerRelation']['data']);
}
#[Test]
public function custom_hasOne_relationship_exists_if_added()
{
$this->extend(
(new Extend\Model(User::class))
->hasOne('customSerializerRelation', Discussion::class, 'user_id'),
(new Extend\ApiResource(UserResource::class))
->fields(fn () => [
Schema\Relationship\ToOne::make('customSerializerRelation')
->type('discussions')
->includable()
])
->endpoint(Show::class, function (Show $endpoint) {
return $endpoint->addDefaultInclude(['customSerializerRelation']);
})
);
$response = $this->send(
$this->request('GET', '/api/users/2', [
'authenticatedAs' => 1,
])
);
$responseJson = json_decode($response->getBody(), true);
$this->assertArrayHasKey('customSerializerRelation', $responseJson['data']['relationships']);
$this->assertEquals('discussions', $responseJson['data']['relationships']['customSerializerRelation']['data']['type']);
}
#[Test]
public function custom_relationship_is_inherited_to_child_classes()
{
$this->extend(
(new Extend\Model(User::class))
->hasMany('anotherCustomRelation', Discussion::class, 'user_id'),
(new Extend\ApiResource(AbstractDatabaseResource::class))
->fields(fn () => [
Schema\Relationship\ToMany::make('anotherCustomRelation')
->type('discussions')
->includable()
])
->endpoint(Show::class, function (Show $endpoint) {
return $endpoint->addDefaultInclude(['anotherCustomRelation']);
})
);
$response = $this->send(
$this->request('GET', '/api/users/2', [
'authenticatedAs' => 1,
])
);
$responseJson = json_decode($response->getBody(), true);
$this->assertArrayHasKey('anotherCustomRelation', $responseJson['data']['relationships']);
$this->assertCount(4, $responseJson['data']['relationships']['anotherCustomRelation']['data']);
}
}
class CustomAfterEndpointInvokableClass
@ -720,3 +1053,14 @@ class CustomAfterEndpointInvokableClass
return $discussion;
}
}
class CustomAttributesInvokableClass
{
public function __invoke(): array
{
return [
Schema\Boolean::make('customAttributeFromInvokable')
->get(fn () => true),
];
}
}

View File

@ -1,320 +0,0 @@
<?php
/*
* This file is part of Flarum.
*
* For detailed copyright and license information, please view the
* LICENSE file that was distributed with this source code.
*/
namespace Flarum\Tests\integration\extenders;
use Carbon\Carbon;
use Flarum\Api\Endpoint\Show;
use Flarum\Api\Resource\AbstractDatabaseResource;
use Flarum\Api\Resource\ForumResource;
use Flarum\Api\Resource\UserResource;
use Flarum\Api\Schema;
use Flarum\Discussion\Discussion;
use Flarum\Extend;
use Flarum\Post\Post;
use Flarum\Testing\integration\RetrievesAuthorizedUsers;
use Flarum\Testing\integration\TestCase;
use Flarum\User\User;
use PHPUnit\Framework\Attributes\Test;
class ApiSerializerTest extends TestCase
{
use RetrievesAuthorizedUsers;
/**
* @inheritDoc
*/
protected function setUp(): void
{
parent::setUp();
$this->prepareDatabase([
User::class => [
$this->normalUser()
],
Discussion::class => [
['id' => 1, 'title' => 'Custom Discussion Title', 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 2, 'first_post_id' => 0, 'comment_count' => 1, 'is_private' => 0],
['id' => 2, 'title' => 'Custom Discussion Title', 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 2, 'first_post_id' => 0, 'comment_count' => 1, 'is_private' => 0],
['id' => 3, 'title' => 'Custom Discussion Title', 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 2, 'first_post_id' => 0, 'comment_count' => 1, 'is_private' => 0],
],
Post::class => [
['id' => 1, 'discussion_id' => 3, 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 2, 'type' => 'discussionRenamed', 'content' => '<t><p>can i haz relationz?</p></t>'],
],
]);
}
#[Test]
public function custom_attributes_dont_exist_by_default()
{
$this->app();
$response = $this->send(
$this->request('GET', '/api', [
'authenticatedAs' => 1,
])
);
$payload = json_decode($response->getBody()->getContents(), true);
$this->assertArrayNotHasKey('customAttribute', $payload['data']['attributes']);
}
#[Test]
public function custom_attributes_exist_if_added()
{
$this->extend(
(new Extend\ApiResource(ForumResource::class))
->fields(fn () => [
Schema\Boolean::make('customAttribute')
->get(fn () => true),
])
);
$this->app();
$response = $this->send(
$this->request('GET', '/api', [
'authenticatedAs' => 1,
])
);
$payload = json_decode($response->getBody()->getContents(), true);
$this->assertArrayHasKey('customAttribute', $payload['data']['attributes']);
}
#[Test]
public function custom_attributes_with_invokable_exist_if_added()
{
$this->extend(
(new Extend\ApiResource(ForumResource::class))
->fields(CustomAttributesInvokableClass::class)
);
$this->app();
$response = $this->send(
$this->request('GET', '/api', [
'authenticatedAs' => 1,
])
);
$payload = json_decode($response->getBody()->getContents(), true);
$this->assertArrayHasKey('customAttributeFromInvokable', $payload['data']['attributes']);
}
#[Test]
public function custom_attributes_exist_if_added_to_parent_class()
{
$this->extend(
(new Extend\ApiResource(AbstractDatabaseResource::class))
->fields(fn () => [
Schema\Boolean::make('customAttribute')
->get(fn () => true),
])
);
$this->app();
$response = $this->send(
$this->request('GET', '/api/users/2', [
'authenticatedAs' => 1,
])
);
$payload = json_decode($response->getBody()->getContents(), true);
$this->assertArrayHasKey('customAttribute', $payload['data']['attributes']);
}
#[Test]
public function custom_attributes_prioritize_child_classes()
{
$this->extend(
(new Extend\ApiResource(AbstractDatabaseResource::class))
->fields(fn () => [
Schema\Str::make('customAttribute')
->get(fn () => 'initialValue')
]),
(new Extend\ApiResource(UserResource::class))
->fields(fn () => [
Schema\Str::make('customAttribute')
->get(fn () => 'newValue')
]),
);
$this->app();
$response = $this->send(
$this->request('GET', '/api/users/2', [
'authenticatedAs' => 1,
])
);
$payload = json_decode($response->getBody()->getContents(), true);
$this->assertArrayHasKey('customAttribute', $payload['data']['attributes']);
$this->assertEquals('newValue', $payload['data']['attributes']['customAttribute']);
}
#[Test]
public function custom_attributes_can_be_overridden()
{
$this->extend(
(new Extend\ApiResource(UserResource::class))
->fields(fn () => [
Schema\Str::make('someCustomAttribute')
->get(fn () => 'newValue'),
])
->fields(fn () => [
Schema\Str::make('someCustomAttribute')
->get(fn () => 'secondValue'),
Schema\Str::make('someOtherCustomAttribute')
->get(fn () => 'secondValue'),
])
->fields(fn () => [
Schema\Str::make('someOtherCustomAttribute')
->get(fn () => 'newValue'),
])
);
$this->app();
$response = $this->send(
$this->request('GET', '/api/users/2', [
'authenticatedAs' => 1,
])
);
$payload = json_decode($response->getBody()->getContents(), true);
$this->assertArrayHasKey('someCustomAttribute', $payload['data']['attributes']);
$this->assertEquals('secondValue', $payload['data']['attributes']['someCustomAttribute']);
$this->assertArrayHasKey('someOtherCustomAttribute', $payload['data']['attributes']);
$this->assertEquals('newValue', $payload['data']['attributes']['someOtherCustomAttribute']);
}
#[Test]
public function custom_relations_dont_exist_by_default()
{
$this->extend(
(new Extend\ApiResource(UserResource::class))
->endpoint(Show::class, function (Show $endpoint): Show {
return $endpoint->addDefaultInclude(['customSerializerRelation', 'postCustomRelation', 'anotherCustomRelation']);
})
);
$response = $this->send(
$this->request('GET', '/api/users/2', [
'authenticatedAs' => 1,
])
);
$this->assertEquals(400, $response->getStatusCode());
}
#[Test]
public function custom_hasMany_relationship_exists_if_added()
{
$this->extend(
(new Extend\Model(User::class))
->hasMany('customSerializerRelation', Discussion::class, 'user_id'),
(new Extend\ApiResource(UserResource::class))
->fields(fn () => [
Schema\Relationship\ToMany::make('customSerializerRelation')
->type('discussions')
->includable()
])
->endpoint(Show::class, function (Show $endpoint) {
return $endpoint->addDefaultInclude(['customSerializerRelation']);
})
);
$response = $this->send(
$this->request('GET', '/api/users/2', [
'authenticatedAs' => 1,
])
);
$responseJson = json_decode($response->getBody(), true);
$this->assertArrayHasKey('customSerializerRelation', $responseJson['data']['relationships']);
$this->assertCount(3, $responseJson['data']['relationships']['customSerializerRelation']['data']);
}
#[Test]
public function custom_hasOne_relationship_exists_if_added()
{
$this->extend(
(new Extend\Model(User::class))
->hasOne('customSerializerRelation', Discussion::class, 'user_id'),
(new Extend\ApiResource(UserResource::class))
->fields(fn () => [
Schema\Relationship\ToOne::make('customSerializerRelation')
->type('discussions')
->includable()
])
->endpoint(Show::class, function (Show $endpoint) {
return $endpoint->addDefaultInclude(['customSerializerRelation']);
})
);
$response = $this->send(
$this->request('GET', '/api/users/2', [
'authenticatedAs' => 1,
])
);
$responseJson = json_decode($response->getBody(), true);
$this->assertArrayHasKey('customSerializerRelation', $responseJson['data']['relationships']);
$this->assertEquals('discussions', $responseJson['data']['relationships']['customSerializerRelation']['data']['type']);
}
#[Test]
public function custom_relationship_is_inherited_to_child_classes()
{
$this->extend(
(new Extend\Model(User::class))
->hasMany('anotherCustomRelation', Discussion::class, 'user_id'),
(new Extend\ApiResource(AbstractDatabaseResource::class))
->fields(fn () => [
Schema\Relationship\ToMany::make('anotherCustomRelation')
->type('discussions')
->includable()
])
->endpoint(Show::class, function (Show $endpoint) {
return $endpoint->addDefaultInclude(['anotherCustomRelation']);
})
);
$response = $this->send(
$this->request('GET', '/api/users/2', [
'authenticatedAs' => 1,
])
);
$responseJson = json_decode($response->getBody(), true);
$this->assertArrayHasKey('anotherCustomRelation', $responseJson['data']['relationships']);
$this->assertCount(3, $responseJson['data']['relationships']['anotherCustomRelation']['data']);
}
}
class CustomAttributesInvokableClass
{
public function __invoke(): array
{
return [
Schema\Boolean::make('customAttributeFromInvokable')
->get(fn () => true),
];
}
}