Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 26 additions & 25 deletions src/Serializers/CollectionSerializer.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Huntie\JsonApi\Serializers;

use Request;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;

class CollectionSerializer extends JsonApiSerializer
Expand Down Expand Up @@ -62,21 +63,6 @@ public function toResourceCollection()
});
}

/**
* Return a collection of JSON API resource objects for each included
* relationship.
*
* @throws \Huntie\JsonApi\Exceptions\InvalidRelationPathException
*
* @return \Illuminate\Support\Collection
*/
public function getIncludedRecords()
{
return $this->records->map(function ($record) {
return (new ResourceSerializer($record, $this->fields, $this->include))->getIncludedRecords();
})->flatten(1)->unique()->values();
}

/**
* Return primary data for the JSON API document.
*
Expand All @@ -88,13 +74,24 @@ protected function getPrimaryData()
}

/**
* Return any secondary included resource data.
* Return any secondary included resource objects.
*
* @throws \Huntie\JsonApi\Exceptions\InvalidRelationPathException
*
* @return array
* @return \Illuminate\Support\Collection
*/
protected function getIncludedData()
public function getIncluded()
{
return $this->getIncludedRecords()->toArray();
$included = collect();

foreach ($this->records as $record) {
$included = $included->merge(
(new ResourceSerializer($record, $this->fields, $this->include))
->getIncluded()
);
}

return $included->unique();
}

/**
Expand All @@ -104,12 +101,16 @@ protected function getIncludedData()
*/
protected function addPaginationLinks($paginator)
{
$this->addLinks([
'first' => $paginator->url(1),
'last' => $paginator->url($paginator->lastPage()),
'prev' => $paginator->previousPageUrl(),
'next' => $paginator->nextPageUrl(),
]);
$query = array_add(Request::query(), 'page.size', $paginator->perPage());

$this->addLinks(array_map(function ($page) use ($query) {
return '?' . urldecode(http_build_query(array_add($query, 'page.number', $page)));
}, array_filter([
'first' => 1,
'last' => $paginator->lastPage(),
'prev' => $paginator->currentPage() > 1 ? $paginator->currentPage() - 1 : null,
'next' => $paginator->currentPage() < $paginator->lastPage() ? $paginator->currentPage() + 1 : null,
])));

$this->addMeta('total', $paginator->total());
}
Expand Down
88 changes: 54 additions & 34 deletions src/Serializers/JsonApiSerializer.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Huntie\JsonApi\Serializers;

use JsonSerializable;
use Request;

abstract class JsonApiSerializer implements JsonSerializable
{
Expand All @@ -13,27 +14,34 @@ abstract class JsonApiSerializer implements JsonSerializable
*/
const JSON_API_VERSION = '1.0';

/**
* The base URL for links.
*
* @var string
*/
protected $baseUrl;

/**
* Meta information to include.
*
* @var \Illuminate\Support\Collection
* @var array
*/
protected $meta;
protected $meta = [];

/**
* Resource links to include.
* Resource links to include, relative to the base URL.
*
* @var \Illuminate\Support\Collection
* @var array
*/
protected $links;
protected $links = [];

/**
* Create a new JSON API document serializer.
*/
public function __construct()
{
$this->meta = collect([]);
$this->links = collect([]);
$this->baseUrl = Request::url();
$this->addLinks('self', str_replace(Request::url(), '', Request::fullUrl()));
}

/**
Expand All @@ -43,6 +51,36 @@ public function __construct()
*/
abstract protected function getPrimaryData();

/**
* Return any links related to the primary data.
*/
public function getLinks(): array
{
return array_map(function ($path) {
return $this->baseUrl . $path;
}, $this->links);
}

/**
* Return any secondary included resource objects.
*
* @return \Illuminate\Support\Collection
*/
public function getIncluded()
{
return collect();
}

/**
* Set the base URL for document links.
*
* @param string $url
*/
public function setBaseUrl(string $url)
{
$this->baseUrl = preg_replace('/\/$/', '', $url);
}

/**
* Add included meta information.
*
Expand All @@ -51,7 +89,7 @@ abstract protected function getPrimaryData();
*/
public function addMeta($key, $value = null)
{
$this->meta = $this->meta->merge(is_array($key) ? $key : [$key => $value]);
$this->meta = array_merge($this->meta, is_array($key) ? $key : [$key => $value]);
}

/**
Expand All @@ -62,61 +100,43 @@ public function addMeta($key, $value = null)
*/
public function addLinks($key, $value = null)
{
$this->links = $this->links->merge(is_array($key) ? $key : [$key => $value]);
$this->links = array_merge($this->links, is_array($key) ? $key : [$key => $value]);
}

/**
* Serialise JSON API document to an array.
*
* @return array
*/
public function serializeToObject()
public function serializeToObject(): array
{
return array_filter([
'data' => $this->getPrimaryData(),
'links' => $this->links->toArray(),
'meta' => $this->meta->toArray(),
'included' => array_filter($this->getIncludedData()),
'links' => $this->getLinks(),
'meta' => $this->meta,
'included' => $this->getIncluded()->toArray(),
'jsonapi' => $this->getDocumentMeta(),
]);
}

/**
* Convert the object into something JSON serializable.
*
* @return array
*/
public function jsonSerialize()
public function jsonSerialize(): array
{
return $this->serializeToObject();
}

/**
* Serialise JSON API document to a JSON string.
*
* @return array
*/
public function serializeToJson()
public function serializeToJson(): string
{
return json_encode($this->jsonSerialize());
}

/**
* Return any secondary included resource data.
*
* @return array
*/
protected function getIncludedData()
{
return [];
}

/**
* Return JSON API implementation information.
*
* @return array
*/
private function getDocumentMeta()
private function getDocumentMeta(): array
{
return array_filter([
'version' => config('jsonapi.include_version') ? self::JSON_API_VERSION : null,
Expand Down
40 changes: 19 additions & 21 deletions src/Serializers/ResourceSerializer.php
Original file line number Diff line number Diff line change
Expand Up @@ -99,23 +99,6 @@ public function toResourceObject()
]));
}

/**
* Return a collection of JSON API resource objects for each included
* relationship.
*
* @throws \Huntie\JsonApi\Exceptions\InvalidRelationPathException
*
* @return \Illuminate\Support\Collection
*/
public function getIncludedRecords()
{
return collect($this->include)->map(function ($relation) {
$records = (new RelationshipSerializer($this->record, $relation, $this->fields))->toResourceCollection();

return $records instanceof Collection ? $records : [$records];
})->flatten(1)->unique()->values();
}

/**
* Return primary data for the JSON API document.
*
Expand All @@ -127,13 +110,28 @@ protected function getPrimaryData()
}

/**
* Return any secondary included resource data.
* Return any secondary included resource objects.
*
* @return array
* @throws \Huntie\JsonApi\Exceptions\InvalidRelationPathException
*
* @return \Illuminate\Support\Collection
*/
protected function getIncludedData()
public function getIncluded()
{
return $this->getIncludedRecords()->toArray();
$included = collect();

foreach ($this->include as $relation) {
$records = (new RelationshipSerializer($this->record, $relation, $this->fields))
->toResourceCollection();

if ($records instanceof Collection) {
$included = $included->merge($records);
} else if (!empty($records)) {
$included->push($records);
}
}

return $included->unique();
}

/**
Expand Down
12 changes: 6 additions & 6 deletions tests/Serializers/CollectionSerializerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,19 +28,19 @@ public function testPaginatedCollection()
$pageSize = 5;
$users = factory(User::class, 12)->make();
$paginator = new LengthAwarePaginator($users->forPage(1, $pageSize), $users->count(), $pageSize);
$paginator->setPath('http://localhost/users');

$serializer = new CollectionSerializer($paginator);
$serializer->setBaseUrl('http://localhost/users');
$document = $serializer->serializeToObject();

$this->assertCount(5, $document['data'], 'Incorrect number of paginated records returned');

$this->assertArrayHasKey('links', $document);
$this->assertEquals([
'first' => 'http://localhost/users?page=1',
'last' => 'http://localhost/users?page=3',
'prev' => null,
'next' => 'http://localhost/users?page=2',
'first' => 'http://localhost/users?page[size]=5&page[number]=1',
'last' => 'http://localhost/users?page[size]=5&page[number]=3',
'next' => 'http://localhost/users?page[size]=5&page[number]=2',
'self' => 'http://localhost/users'
], $document['links']);

$this->assertArrayHasKey('meta', $document);
Expand All @@ -56,7 +56,7 @@ public function testIncludedRecords()
->states('withPosts')
->make();
$serializer = new CollectionSerializer($users, [], ['posts']);
$included = $serializer->getIncludedRecords();
$included = $serializer->getIncluded();

$this->assertInstanceOf(Collection::class, $included);
$this->assertJsonApiObjectCollection(['data' => $included->toArray()], 6);
Expand Down
13 changes: 8 additions & 5 deletions tests/Serializers/JsonApiSerializerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -62,16 +62,19 @@ public function testAddMeta()
public function testAddLinks()
{
$serializer = new ResourceSerializer(factory(User::class)->make());
$serializer->setBaseUrl('http://localhost/users'); // TODO path joining logic
$serializer->addLinks([
'next' => 'http://localhost/users/3',
'prev' => 'http://localhost/users/1',
'next' => '?page[number]=2',
'posts' => '/posts',
]);
$document = $serializer->serializeToObject();

$this->assertArrayHasKey('links', $document);
$this->assertCount(2, $document['links']);
$this->assertEquals('http://localhost/users/3', $document['links']['next']);
$this->assertEquals('http://localhost/users/1', $document['links']['prev']);
$this->assertEquals([
'self' => 'http://localhost/users',
'next' => 'http://localhost/users?page[number]=2',
'posts' => 'http://localhost/users/posts'
], $document['links']);
}

/**
Expand Down
4 changes: 2 additions & 2 deletions tests/Serializers/ResourceSerializerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ public function testIncludedRecords()
->states('withPosts', 'withComments')
->make();
$serializer = new ResourceSerializer($user, [], ['posts', 'comments']);
$included = $serializer->getIncludedRecords();
$included = $serializer->getIncluded();

$this->assertInstanceOf(Collection::class, $included);
$this->assertJsonApiObjectCollection(['data' => $included->toArray()], 4);
Expand All @@ -114,7 +114,7 @@ public function testScopeIncludedRecords()
->make();
$serializer = new ResourceSerializer($user, [], ['posts', 'comments']);
$serializer->scopeIncludes(['posts']);
$included = $serializer->getIncludedRecords();
$included = $serializer->getIncluded();

$this->assertInstanceOf(Collection::class, $included);
$this->assertCount(2, $included->toArray());
Expand Down