mirror of
https://github.com/flarum/core.git
synced 2025-08-01 14:10:37 +02:00
ApiSerializer Extender (#2438)
This commit is contained in:
@@ -17,6 +17,8 @@ use Flarum\Api\Serializer\AbstractSerializer;
|
|||||||
*
|
*
|
||||||
* This event is fired when a serializer is constructing an array of resource
|
* This event is fired when a serializer is constructing an array of resource
|
||||||
* attributes for API output.
|
* attributes for API output.
|
||||||
|
*
|
||||||
|
* @deprecated in beta 15, removed in beta 16
|
||||||
*/
|
*/
|
||||||
class Serializing
|
class Serializing
|
||||||
{
|
{
|
||||||
|
@@ -16,6 +16,7 @@ use Flarum\Event\GetApiRelationship;
|
|||||||
use Flarum\User\User;
|
use Flarum\User\User;
|
||||||
use Illuminate\Contracts\Container\Container;
|
use Illuminate\Contracts\Container\Container;
|
||||||
use Illuminate\Contracts\Events\Dispatcher;
|
use Illuminate\Contracts\Events\Dispatcher;
|
||||||
|
use Illuminate\Support\Arr;
|
||||||
use InvalidArgumentException;
|
use InvalidArgumentException;
|
||||||
use LogicException;
|
use LogicException;
|
||||||
use Psr\Http\Message\ServerRequestInterface as Request;
|
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||||
@@ -47,6 +48,16 @@ abstract class AbstractSerializer extends BaseAbstractSerializer
|
|||||||
*/
|
*/
|
||||||
protected static $container;
|
protected static $container;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var callable[]
|
||||||
|
*/
|
||||||
|
protected static $mutators = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected static $customRelations = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Request
|
* @return Request
|
||||||
*/
|
*/
|
||||||
@@ -83,6 +94,18 @@ abstract class AbstractSerializer extends BaseAbstractSerializer
|
|||||||
|
|
||||||
$attributes = $this->getDefaultAttributes($model);
|
$attributes = $this->getDefaultAttributes($model);
|
||||||
|
|
||||||
|
foreach (array_reverse(array_merge([static::class], class_parents($this))) as $class) {
|
||||||
|
if (isset(static::$mutators[$class])) {
|
||||||
|
foreach (static::$mutators[$class] as $callback) {
|
||||||
|
$attributes = array_merge(
|
||||||
|
$attributes,
|
||||||
|
$callback($this, $model, $attributes)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated in beta 15, removed in beta 16
|
||||||
static::$dispatcher->dispatch(
|
static::$dispatcher->dispatch(
|
||||||
new Serializing($this, $model, $attributes)
|
new Serializing($this, $model, $attributes)
|
||||||
);
|
);
|
||||||
@@ -102,7 +125,7 @@ abstract class AbstractSerializer extends BaseAbstractSerializer
|
|||||||
* @param DateTime|null $date
|
* @param DateTime|null $date
|
||||||
* @return string|null
|
* @return string|null
|
||||||
*/
|
*/
|
||||||
protected function formatDate(DateTime $date = null)
|
public function formatDate(DateTime $date = null)
|
||||||
{
|
{
|
||||||
if ($date) {
|
if ($date) {
|
||||||
return $date->format(DateTime::RFC3339);
|
return $date->format(DateTime::RFC3339);
|
||||||
@@ -130,10 +153,20 @@ abstract class AbstractSerializer extends BaseAbstractSerializer
|
|||||||
*/
|
*/
|
||||||
protected function getCustomRelationship($model, $name)
|
protected function getCustomRelationship($model, $name)
|
||||||
{
|
{
|
||||||
|
// Deprecated in beta 15, removed in beta 16
|
||||||
$relationship = static::$dispatcher->until(
|
$relationship = static::$dispatcher->until(
|
||||||
new GetApiRelationship($this, $name, $model)
|
new GetApiRelationship($this, $name, $model)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
foreach (array_merge([static::class], class_parents($this)) as $class) {
|
||||||
|
$callback = Arr::get(static::$customRelations, "$class.$name");
|
||||||
|
|
||||||
|
if (is_callable($callback)) {
|
||||||
|
$relationship = $callback($this, $model);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ($relationship && ! ($relationship instanceof Relationship)) {
|
if ($relationship && ! ($relationship instanceof Relationship)) {
|
||||||
throw new LogicException(
|
throw new LogicException(
|
||||||
'GetApiRelationship handler must return an instance of '.Relationship::class
|
'GetApiRelationship handler must return an instance of '.Relationship::class
|
||||||
@@ -280,4 +313,27 @@ abstract class AbstractSerializer extends BaseAbstractSerializer
|
|||||||
{
|
{
|
||||||
static::$container = $container;
|
static::$container = $container;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $serializerClass
|
||||||
|
* @param callable $mutator
|
||||||
|
*/
|
||||||
|
public static function addMutator(string $serializerClass, callable $mutator)
|
||||||
|
{
|
||||||
|
if (! isset(static::$mutators[$serializerClass])) {
|
||||||
|
static::$mutators[$serializerClass] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
static::$mutators[$serializerClass][] = $mutator;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $serializerClass
|
||||||
|
* @param string $relation
|
||||||
|
* @param callable $callback
|
||||||
|
*/
|
||||||
|
public static function setRelationship(string $serializerClass, string $relation, callable $callback)
|
||||||
|
{
|
||||||
|
static::$customRelations[$serializerClass][$relation] = $callback;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -21,6 +21,8 @@ use Flarum\Api\Serializer\AbstractSerializer;
|
|||||||
* @see AbstractSerializer::hasOne()
|
* @see AbstractSerializer::hasOne()
|
||||||
* @see AbstractSerializer::hasMany()
|
* @see AbstractSerializer::hasMany()
|
||||||
* @see https://github.com/tobscure/json-api
|
* @see https://github.com/tobscure/json-api
|
||||||
|
*
|
||||||
|
* @deprecated in beta 15, removed in beta 16
|
||||||
*/
|
*/
|
||||||
class GetApiRelationship
|
class GetApiRelationship
|
||||||
{
|
{
|
||||||
|
162
src/Extend/ApiSerializer.php
Normal file
162
src/Extend/ApiSerializer.php
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
<?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\Extend;
|
||||||
|
|
||||||
|
use Flarum\Api\Serializer\AbstractSerializer;
|
||||||
|
use Flarum\Extension\Extension;
|
||||||
|
use Flarum\Foundation\ContainerUtil;
|
||||||
|
use Illuminate\Contracts\Container\Container;
|
||||||
|
|
||||||
|
class ApiSerializer implements ExtenderInterface
|
||||||
|
{
|
||||||
|
private $serializerClass;
|
||||||
|
private $attributes = [];
|
||||||
|
private $mutators = [];
|
||||||
|
private $relationships = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $serializerClass The ::class attribute of the serializer you are modifying.
|
||||||
|
* This serializer should extend from \Flarum\Api\Serializer\AbstractSerializer.
|
||||||
|
*/
|
||||||
|
public function __construct(string $serializerClass)
|
||||||
|
{
|
||||||
|
$this->serializerClass = $serializerClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $name: The name of the attribute.
|
||||||
|
* @param callable|string $callback
|
||||||
|
*
|
||||||
|
* The callback can be a closure or an invokable class, and should accept:
|
||||||
|
* - $serializer: An instance of this serializer.
|
||||||
|
* - $model: An instance of the model being serialized.
|
||||||
|
* - $attributes: An array of existing attributes.
|
||||||
|
*
|
||||||
|
* The callable should return:
|
||||||
|
* - The value of the attribute.
|
||||||
|
*
|
||||||
|
* @return self
|
||||||
|
*/
|
||||||
|
public function attribute(string $name, $callback)
|
||||||
|
{
|
||||||
|
$this->attributes[$name] = $callback;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add to or modify the attributes array of this serializer.
|
||||||
|
*
|
||||||
|
* @param callable|string $callback
|
||||||
|
*
|
||||||
|
* The callback can be a closure or an invokable class, and should accept:
|
||||||
|
* - $serializer: An instance of this serializer.
|
||||||
|
* - $model: An instance of the model being serialized.
|
||||||
|
* - $attributes: An array of existing attributes.
|
||||||
|
*
|
||||||
|
* The callable should return:
|
||||||
|
* - An array of additional attributes to merge with the existing array.
|
||||||
|
* Or a modified $attributes array.
|
||||||
|
*
|
||||||
|
* @return self
|
||||||
|
*/
|
||||||
|
public function mutate($callback)
|
||||||
|
{
|
||||||
|
$this->mutators[] = $callback;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Establish a simple hasOne relationship from this serializer to another serializer.
|
||||||
|
* This represents a one-to-one relationship.
|
||||||
|
*
|
||||||
|
* @param string $name: The name of the relation. Has to be unique from other relation names.
|
||||||
|
* The relation has to exist in the model handled by this serializer.
|
||||||
|
* @param string $serializerClass: The ::class attribute the serializer that handles this relation.
|
||||||
|
* This serializer should extend from \Flarum\Api\Serializer\AbstractSerializer.
|
||||||
|
* @return self
|
||||||
|
*/
|
||||||
|
public function hasOne(string $name, string $serializerClass)
|
||||||
|
{
|
||||||
|
return $this->relationship($name, function (AbstractSerializer $serializer, $model) use ($serializerClass, $name) {
|
||||||
|
return $serializer->hasOne($model, $serializerClass, $name);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Establish a simple hasMany relationship from this serializer to another serializer.
|
||||||
|
* This represents a one-to-many relationship.
|
||||||
|
*
|
||||||
|
* @param string $name: The name of the relation. Has to be unique from other relation names.
|
||||||
|
* The relation has to exist in the model handled by this serializer.
|
||||||
|
* @param string $serializerClass: The ::class attribute the serializer that handles this relation.
|
||||||
|
* This serializer should extend from \Flarum\Api\Serializer\AbstractSerializer.
|
||||||
|
* @return self
|
||||||
|
*/
|
||||||
|
public function hasMany(string $name, string $serializerClass)
|
||||||
|
{
|
||||||
|
return $this->relationship($name, function (AbstractSerializer $serializer, $model) use ($serializerClass, $name) {
|
||||||
|
return $serializer->hasMany($model, $serializerClass, $name);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a relationship from this serializer to another serializer.
|
||||||
|
*
|
||||||
|
* @param string $name: The name of the relation. Has to be unique from other relation names.
|
||||||
|
* The relation has to exist in the model handled by this serializer.
|
||||||
|
* @param callable|string $callback
|
||||||
|
*
|
||||||
|
* The callable can be a closure or an invokable class, and should accept:
|
||||||
|
* - $serializer: An instance of this serializer.
|
||||||
|
* - $model: An instance of the model being serialized.
|
||||||
|
*
|
||||||
|
* The callable should return:
|
||||||
|
* - $relationship: An instance of \Tobscure\JsonApi\Relationship.
|
||||||
|
*
|
||||||
|
* @return self
|
||||||
|
*/
|
||||||
|
public function relationship(string $name, $callback)
|
||||||
|
{
|
||||||
|
$this->relationships[$this->serializerClass][$name] = $callback;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function extend(Container $container, Extension $extension = null)
|
||||||
|
{
|
||||||
|
if (! empty($this->attributes)) {
|
||||||
|
$this->mutators[] = function ($serializer, $model, $attributes) use ($container) {
|
||||||
|
foreach ($this->attributes as $attributeName => $callback) {
|
||||||
|
$callback = ContainerUtil::wrapCallback($callback, $container);
|
||||||
|
|
||||||
|
$attributes[$attributeName] = $callback($serializer, $model, $attributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $attributes;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($this->mutators as $mutator) {
|
||||||
|
$mutator = ContainerUtil::wrapCallback($mutator, $container);
|
||||||
|
|
||||||
|
AbstractSerializer::addMutator($this->serializerClass, $mutator);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($this->relationships as $serializerClass => $relationships) {
|
||||||
|
foreach ($relationships as $relation => $callback) {
|
||||||
|
$callback = ContainerUtil::wrapCallback($callback, $container);
|
||||||
|
|
||||||
|
AbstractSerializer::setRelationship($serializerClass, $relation, $callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
523
tests/integration/extenders/ApiSerializerTest.php
Normal file
523
tests/integration/extenders/ApiSerializerTest.php
Normal file
@@ -0,0 +1,523 @@
|
|||||||
|
<?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\Serializer\AbstractSerializer;
|
||||||
|
use Flarum\Api\Serializer\BasicUserSerializer;
|
||||||
|
use Flarum\Api\Serializer\DiscussionSerializer;
|
||||||
|
use Flarum\Api\Serializer\ForumSerializer;
|
||||||
|
use Flarum\Api\Serializer\PostSerializer;
|
||||||
|
use Flarum\Api\Serializer\UserSerializer;
|
||||||
|
use Flarum\Discussion\Discussion;
|
||||||
|
use Flarum\Extend;
|
||||||
|
use Flarum\Post\Post;
|
||||||
|
use Flarum\Tests\integration\RetrievesAuthorizedUsers;
|
||||||
|
use Flarum\Tests\integration\TestCase;
|
||||||
|
use Flarum\User\User;
|
||||||
|
|
||||||
|
class ApiSerializerTest extends TestCase
|
||||||
|
{
|
||||||
|
use RetrievesAuthorizedUsers;
|
||||||
|
|
||||||
|
protected function prepDb()
|
||||||
|
{
|
||||||
|
$this->prepareDatabase([
|
||||||
|
'users' => [
|
||||||
|
$this->adminUser(),
|
||||||
|
$this->normalUser()
|
||||||
|
],
|
||||||
|
'discussions' => [
|
||||||
|
['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],
|
||||||
|
],
|
||||||
|
'posts' => [
|
||||||
|
['id' => 1, 'discussion_id' => 3, 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 2, 'type' => 'discussionRenamed', 'content' => '<t><p>can i haz relationz?</p></t>'],
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function prepSettingsDb()
|
||||||
|
{
|
||||||
|
$this->prepareDatabase([
|
||||||
|
'settings' => [
|
||||||
|
['key' => 'customPrefix.customSetting', 'value' => 'customValue']
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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(), true);
|
||||||
|
|
||||||
|
$this->assertArrayNotHasKey('customAttribute', $payload['data']['attributes']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function custom_attributes_exist_if_added()
|
||||||
|
{
|
||||||
|
$this->extend(
|
||||||
|
(new Extend\ApiSerializer(ForumSerializer::class))
|
||||||
|
->mutate(function () {
|
||||||
|
return [
|
||||||
|
'customAttribute' => true
|
||||||
|
];
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->app();
|
||||||
|
|
||||||
|
$response = $this->send(
|
||||||
|
$this->request('GET', '/api', [
|
||||||
|
'authenticatedAs' => 1,
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
|
$payload = json_decode($response->getBody(), true);
|
||||||
|
|
||||||
|
$this->assertArrayHasKey('customAttribute', $payload['data']['attributes']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function custom_attributes_with_invokable_exist_if_added()
|
||||||
|
{
|
||||||
|
$this->extend(
|
||||||
|
(new Extend\ApiSerializer(ForumSerializer::class))
|
||||||
|
->mutate(CustomAttributesInvokableClass::class)
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->app();
|
||||||
|
|
||||||
|
$response = $this->send(
|
||||||
|
$this->request('GET', '/api', [
|
||||||
|
'authenticatedAs' => 1,
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
|
$payload = json_decode($response->getBody(), true);
|
||||||
|
|
||||||
|
$this->assertArrayHasKey('customAttributeFromInvokable', $payload['data']['attributes']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function custom_attributes_exist_if_added_to_parent_class()
|
||||||
|
{
|
||||||
|
$this->extend(
|
||||||
|
(new Extend\ApiSerializer(BasicUserSerializer::class))
|
||||||
|
->mutate(function () {
|
||||||
|
return [
|
||||||
|
'customAttribute' => true
|
||||||
|
];
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->app();
|
||||||
|
|
||||||
|
$response = $this->send(
|
||||||
|
$this->request('GET', '/api/users/2', [
|
||||||
|
'authenticatedAs' => 1,
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
|
$payload = json_decode($response->getBody(), true);
|
||||||
|
|
||||||
|
$this->assertArrayHasKey('customAttribute', $payload['data']['attributes']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function custom_attributes_prioritize_child_classes()
|
||||||
|
{
|
||||||
|
$this->extend(
|
||||||
|
(new Extend\ApiSerializer(BasicUserSerializer::class))
|
||||||
|
->mutate(function () {
|
||||||
|
return [
|
||||||
|
'customAttribute' => 'initialValue'
|
||||||
|
];
|
||||||
|
}),
|
||||||
|
(new Extend\ApiSerializer(UserSerializer::class))
|
||||||
|
->mutate(function () {
|
||||||
|
return [
|
||||||
|
'customAttribute' => 'newValue'
|
||||||
|
];
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->app();
|
||||||
|
|
||||||
|
$response = $this->send(
|
||||||
|
$this->request('GET', '/api/users/2', [
|
||||||
|
'authenticatedAs' => 1,
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
|
$payload = json_decode($response->getBody(), true);
|
||||||
|
|
||||||
|
$this->assertArrayHasKey('customAttribute', $payload['data']['attributes']);
|
||||||
|
$this->assertEquals('newValue', $payload['data']['attributes']['customAttribute']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function custom_single_attribute_exists_if_added()
|
||||||
|
{
|
||||||
|
$this->extend(
|
||||||
|
(new Extend\ApiSerializer(ForumSerializer::class))
|
||||||
|
->attribute('customSingleAttribute', function () {
|
||||||
|
return true;
|
||||||
|
})->attribute('customSingleAttribute_0', function () {
|
||||||
|
return 0;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->app();
|
||||||
|
|
||||||
|
$response = $this->send(
|
||||||
|
$this->request('GET', '/api', [
|
||||||
|
'authenticatedAs' => 1,
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
|
$payload = json_decode($response->getBody(), true);
|
||||||
|
|
||||||
|
$this->assertArrayHasKey('customSingleAttribute', $payload['data']['attributes']);
|
||||||
|
$this->assertArrayHasKey('customSingleAttribute_0', $payload['data']['attributes']);
|
||||||
|
$this->assertEquals(0, $payload['data']['attributes']['customSingleAttribute_0']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function custom_single_attribute_with_invokable_exists_if_added()
|
||||||
|
{
|
||||||
|
$this->extend(
|
||||||
|
(new Extend\ApiSerializer(ForumSerializer::class))
|
||||||
|
->attribute('customSingleAttribute_1', CustomSingleAttributeInvokableClass::class)
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->app();
|
||||||
|
|
||||||
|
$response = $this->send(
|
||||||
|
$this->request('GET', '/api', [
|
||||||
|
'authenticatedAs' => 1,
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
|
$payload = json_decode($response->getBody(), true);
|
||||||
|
|
||||||
|
$this->assertArrayHasKey('customSingleAttribute_1', $payload['data']['attributes']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function custom_single_attribute_exists_if_added_to_parent_class()
|
||||||
|
{
|
||||||
|
$this->extend(
|
||||||
|
(new Extend\ApiSerializer(BasicUserSerializer::class))
|
||||||
|
->attribute('customSingleAttribute_2', function () {
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->app();
|
||||||
|
|
||||||
|
$response = $this->send(
|
||||||
|
$this->request('GET', '/api/users/2', [
|
||||||
|
'authenticatedAs' => 1,
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
|
$payload = json_decode($response->getBody(), true);
|
||||||
|
|
||||||
|
$this->assertArrayHasKey('customSingleAttribute_2', $payload['data']['attributes']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function custom_single_attribute_prioritizes_child_classes()
|
||||||
|
{
|
||||||
|
$this->extend(
|
||||||
|
(new Extend\ApiSerializer(BasicUserSerializer::class))
|
||||||
|
->attribute('customSingleAttribute_3', function () {
|
||||||
|
return 'initialValue';
|
||||||
|
}),
|
||||||
|
(new Extend\ApiSerializer(UserSerializer::class))
|
||||||
|
->attribute('customSingleAttribute_3', function () {
|
||||||
|
return 'newValue';
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->app();
|
||||||
|
|
||||||
|
$response = $this->send(
|
||||||
|
$this->request('GET', '/api/users/2', [
|
||||||
|
'authenticatedAs' => 1,
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
|
$payload = json_decode($response->getBody(), true);
|
||||||
|
|
||||||
|
$this->assertArrayHasKey('customSingleAttribute_3', $payload['data']['attributes']);
|
||||||
|
$this->assertEquals('newValue', $payload['data']['attributes']['customSingleAttribute_3']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function custom_attributes_can_be_overriden()
|
||||||
|
{
|
||||||
|
$this->extend(
|
||||||
|
(new Extend\ApiSerializer(BasicUserSerializer::class))
|
||||||
|
->attribute('someCustomAttribute', function () {
|
||||||
|
return 'newValue';
|
||||||
|
})->mutate(function () {
|
||||||
|
return [
|
||||||
|
'someCustomAttribute' => 'initialValue',
|
||||||
|
'someOtherCustomAttribute' => 'initialValue',
|
||||||
|
];
|
||||||
|
})->attribute('someOtherCustomAttribute', function () {
|
||||||
|
return 'newValue';
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->app();
|
||||||
|
|
||||||
|
$response = $this->send(
|
||||||
|
$this->request('GET', '/api/users/2', [
|
||||||
|
'authenticatedAs' => 1,
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
|
$payload = json_decode($response->getBody(), true);
|
||||||
|
|
||||||
|
$this->assertArrayHasKey('someCustomAttribute', $payload['data']['attributes']);
|
||||||
|
$this->assertEquals('newValue', $payload['data']['attributes']['someCustomAttribute']);
|
||||||
|
$this->assertArrayHasKey('someOtherCustomAttribute', $payload['data']['attributes']);
|
||||||
|
$this->assertEquals('newValue', $payload['data']['attributes']['someOtherCustomAttribute']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function custom_hasMany_relationship_exists_if_added()
|
||||||
|
{
|
||||||
|
$this->extend(
|
||||||
|
(new Extend\Model(User::class))
|
||||||
|
->hasMany('customSerializerRelation', Discussion::class, 'user_id'),
|
||||||
|
(new Extend\ApiSerializer(UserSerializer::class))
|
||||||
|
->hasMany('customSerializerRelation', DiscussionSerializer::class)
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->prepDb();
|
||||||
|
|
||||||
|
$request = $this->request('GET', '/api/users/2', [
|
||||||
|
'authenticatedAs' => 1,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$serializer = $this->app()->getContainer()->make(UserSerializer::class);
|
||||||
|
$serializer->setRequest($request);
|
||||||
|
|
||||||
|
$relationship = $serializer->getRelationship(User::find(2), 'customSerializerRelation');
|
||||||
|
|
||||||
|
$this->assertNotEmpty($relationship);
|
||||||
|
$this->assertCount(3, $relationship->toArray()['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\ApiSerializer(UserSerializer::class))
|
||||||
|
->hasOne('customSerializerRelation', DiscussionSerializer::class)
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->prepDb();
|
||||||
|
|
||||||
|
$request = $this->request('GET', '/api/users/2', [
|
||||||
|
'authenticatedAs' => 1,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$serializer = $this->app()->getContainer()->make(UserSerializer::class);
|
||||||
|
$serializer->setRequest($request);
|
||||||
|
|
||||||
|
$relationship = $serializer->getRelationship(User::find(2), 'customSerializerRelation');
|
||||||
|
|
||||||
|
$this->assertNotEmpty($relationship);
|
||||||
|
$this->assertEquals('discussions', $relationship->toArray()['data']['type']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function custom_relationship_exists_if_added()
|
||||||
|
{
|
||||||
|
$this->extend(
|
||||||
|
(new Extend\Model(User::class))
|
||||||
|
->hasOne('customSerializerRelation', Discussion::class, 'user_id'),
|
||||||
|
(new Extend\ApiSerializer(UserSerializer::class))
|
||||||
|
->relationship('customSerializerRelation', function (AbstractSerializer $serializer, $model) {
|
||||||
|
return $serializer->hasOne($model, DiscussionSerializer::class, 'customSerializerRelation');
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->prepDb();
|
||||||
|
|
||||||
|
$request = $this->request('GET', '/api/users/2', [
|
||||||
|
'authenticatedAs' => 1,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$serializer = $this->app()->getContainer()->make(UserSerializer::class);
|
||||||
|
$serializer->setRequest($request);
|
||||||
|
|
||||||
|
$relationship = $serializer->getRelationship(User::find(2), 'customSerializerRelation');
|
||||||
|
|
||||||
|
$this->assertNotEmpty($relationship);
|
||||||
|
$this->assertEquals('discussions', $relationship->toArray()['data']['type']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function custom_relationship_with_invokable_exists_if_added()
|
||||||
|
{
|
||||||
|
$this->extend(
|
||||||
|
(new Extend\Model(User::class))
|
||||||
|
->hasOne('customSerializerRelation', Discussion::class, 'user_id'),
|
||||||
|
(new Extend\ApiSerializer(UserSerializer::class))
|
||||||
|
->relationship('customSerializerRelation', CustomRelationshipInvokableClass::class)
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->prepDb();
|
||||||
|
|
||||||
|
$request = $this->request('GET', '/api/users/2', [
|
||||||
|
'authenticatedAs' => 1,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$serializer = $this->app()->getContainer()->make(UserSerializer::class);
|
||||||
|
$serializer->setRequest($request);
|
||||||
|
|
||||||
|
$relationship = $serializer->getRelationship(User::find(2), 'customSerializerRelation');
|
||||||
|
|
||||||
|
$this->assertNotEmpty($relationship);
|
||||||
|
$this->assertEquals('discussions', $relationship->toArray()['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\ApiSerializer(BasicUserSerializer::class))
|
||||||
|
->hasMany('anotherCustomRelation', DiscussionSerializer::class)
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->prepDb();
|
||||||
|
|
||||||
|
$request = $this->request('GET', '/api/users/2', [
|
||||||
|
'authenticatedAs' => 1,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$serializer = $this->app()->getContainer()->make(UserSerializer::class);
|
||||||
|
$serializer->setRequest($request);
|
||||||
|
|
||||||
|
$relationship = $serializer->getRelationship(User::find(2), 'anotherCustomRelation');
|
||||||
|
|
||||||
|
$this->assertNotEmpty($relationship);
|
||||||
|
$this->assertCount(3, $relationship->toArray()['data']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function custom_relationship_prioritizes_child_classes()
|
||||||
|
{
|
||||||
|
$this->extend(
|
||||||
|
(new Extend\Model(User::class))
|
||||||
|
->hasOne('postCustomRelation', Post::class, 'user_id'),
|
||||||
|
(new Extend\Model(User::class))
|
||||||
|
->hasOne('discussionCustomRelation', Discussion::class, 'user_id'),
|
||||||
|
(new Extend\ApiSerializer(BasicUserSerializer::class))
|
||||||
|
->hasOne('postCustomRelation', PostSerializer::class),
|
||||||
|
(new Extend\ApiSerializer(UserSerializer::class))
|
||||||
|
->relationship('postCustomRelation', function (AbstractSerializer $serializer, $model) {
|
||||||
|
return $serializer->hasOne($model, DiscussionSerializer::class, 'discussionCustomRelation');
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->prepDb();
|
||||||
|
|
||||||
|
$request = $this->request('GET', '/api/users/2', [
|
||||||
|
'authenticatedAs' => 1,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$serializer = $this->app()->getContainer()->make(UserSerializer::class);
|
||||||
|
$serializer->setRequest($request);
|
||||||
|
|
||||||
|
$relationship = $serializer->getRelationship(User::find(2), 'postCustomRelation');
|
||||||
|
|
||||||
|
$this->assertNotEmpty($relationship);
|
||||||
|
$this->assertEquals('discussions', $relationship->toArray()['data']['type']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CustomAttributesInvokableClass
|
||||||
|
{
|
||||||
|
public function __invoke()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'customAttributeFromInvokable' => true
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CustomSingleAttributeInvokableClass
|
||||||
|
{
|
||||||
|
public function __invoke()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CustomRelationshipInvokableClass
|
||||||
|
{
|
||||||
|
public function __invoke(AbstractSerializer $serializer, $model)
|
||||||
|
{
|
||||||
|
return $serializer->hasOne($model, DiscussionSerializer::class, 'customSerializerRelation');
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user