Skip to content

Commit

Permalink
Merge branch '2.1.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
lode committed Mar 3, 2019
2 parents c80ec0b + 8b19908 commit 623b0aa
Show file tree
Hide file tree
Showing 73 changed files with 2,663 additions and 184 deletions.
16 changes: 11 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

A simple and human-friendly library for api servers (php serving json).

It allows you to generate json output according to the [JSON:API](https://jsonapi.org/) standard,
It allows you to generate json output according to the [JSON:API v1.1](https://jsonapi.org/) standard,
while being easy to understand for people without knowledge of the jsonapi standard.

The JSON:API standard makes it easy for clients to fetch multiple resources in one call and understand the relations between them.
Expand Down Expand Up @@ -41,7 +41,7 @@ Which will result in:
```json
{
"jsonapi": {
"version": "1.0"
"version": "1.1"
},
"data": {
"type": "user",
Expand Down Expand Up @@ -71,7 +71,7 @@ Which will result in:
```json
{
"jsonapi": {
"version": "1.0"
"version": "1.1"
},
"data": [
{
Expand Down Expand Up @@ -115,7 +115,7 @@ Which will result in:
```json
{
"jsonapi": {
"version": "1.0"
"version": "1.1"
},
"errors": [
{
Expand All @@ -139,14 +139,20 @@ Examples for all kind of responses are in the [/examples](/examples) directory.

## Features

This library supports [v1.0 of the JSON:API specification](https://jsonapi.org/format/1.0/).
This library supports [v1.1 of the JSON:API specification](https://jsonapi.org/format/1.1/).

It has support for generating & sending documents with:

- single resources
- resource collections
- to-one and to-many relationships
- errors (easily turning exceptions into jsonapi output)
- v1.1 extensions via profiles
- v1.1 @-members for JSON-LD and others

Next to custom extensions, the following [official extensions](https://jsonapi.org/extensions/) are included:

- Cursor Pagination ([example code](/examples/cursor_pagination_profile.php), [specification](https://jsonapi.org/profiles/ethanresnick/cursor-pagination/))

Plans for the future include:

Expand Down
8 changes: 4 additions & 4 deletions UPGRADE_1_TO_2.md
Original file line number Diff line number Diff line change
Expand Up @@ -553,7 +553,7 @@ Every document by defaults get a jsonapi object in the output:
```json
{
"jsonapi": {
"version": "1.0"
"version": "1.1"
}
}
```
Expand All @@ -564,15 +564,15 @@ The object can be changed:

```php
$jsonapiObject = new JsonapiObject();
$jsonapiObject->setVersion(Document::JSONAPI_VERSION_1_1);
$jsonapiObject->setVersion(Document::JSONAPI_VERSION_1_0);
$jsonapiObject->addMeta('foo', 'bar');
$document->setJsonapiObject($jsonapiObject);
```

```json
{
"jsonapi": {
"version": "1.1",
"version": "1.0",
"meta": {
"foo": "bar"
}
Expand Down Expand Up @@ -600,7 +600,7 @@ $document->addMeta('foo', 'jsonapi', $level=Document::LEVEL_JSONAPI);
```json
{
"jsonapi": {
"version": "1.0",
"version": "1.1",
"meta": {
"foo": "jsonapi"
}
Expand Down
33 changes: 33 additions & 0 deletions examples/bootstrap_examples.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
<?php

use alsvanzelf\jsonapi\Document;
use alsvanzelf\jsonapi\ResourceDocument;
use alsvanzelf\jsonapi\helpers\ProfileAliasManager;
use alsvanzelf\jsonapi\interfaces\ProfileInterface;
use alsvanzelf\jsonapi\interfaces\ResourceInterface;

ini_set('display_errors', 1);
error_reporting(-1);

Expand Down Expand Up @@ -95,3 +101,30 @@ function getCurrentLocation() {
return 'Earth';
}
}

class ExampleVersionProfile extends ProfileAliasManager implements ProfileInterface {
/**
* the required methods (next to extending ProfileAliasManager)
*/

public function getOfficialLink() {
return 'https://jsonapi.org/format/1.1/#profile-keywords-and-aliases';
}

public function getOfficialKeywords() {
return ['version'];
}

/**
* optionally helpers for the specific profile
*/

public function setVersion(ResourceInterface $resource, $version) {
if ($resource instanceof ResourceDocument) {
$resource->addMeta($this->getKeyword('version'), $version, $level=Document::LEVEL_RESOURCE);
}
else {
$resource->addMeta($this->getKeyword('version'), $version);
}
}
}
36 changes: 36 additions & 0 deletions examples/cursor_pagination_profile.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

use alsvanzelf\jsonapi\CollectionDocument;
use alsvanzelf\jsonapi\objects\ResourceObject;
use alsvanzelf\jsonapi\profiles\CursorPaginationProfile;

require 'bootstrap_examples.php';

/**
* use the cursor pagination profile as extension to the document
*/

$profile = new CursorPaginationProfile();

$user1 = new ResourceObject('user', 1);
$user2 = new ResourceObject('user', 2);
$user42 = new ResourceObject('user', 42);

$profile->setCursor($user1, 'ford');
$profile->setCursor($user2, 'arthur');
$profile->setCursor($user42, 'zaphod');

$document = CollectionDocument::fromResources($user1, $user2, $user42);
$document->applyProfile($profile);

$profile->setCount($document, $exactTotal=3, $bestGuessTotal=10);
$profile->setLinksFirstPage($document, $currentUrl='/users?sort=42&page[size]=10', $lastCursor='zaphod');

/**
* get the json
*/

$options = [
'prettyPrint' => true,
];
echo '<pre>'.$document->toJson($options);
3 changes: 2 additions & 1 deletion examples/errors_all_options.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
* setting all options
*/

$errorHumanApi = new ErrorObject($genericCode='Invalid input', $genericTitle='Too much options', $specificDetails='Please, choose a bit less. Consult your ...', $specificAboutLink='https://www.example.com/explanation.html');
$errorHumanApi = new ErrorObject($genericCode='Invalid input', $genericTitle='Too much options', $specificDetails='Please, choose a bit less. Consult your ...', $specificAboutLink='https://www.example.com/explanation.html', $genericTypeLink='https://www.example.com/documentation.html');

$errorSpecApi = new ErrorObject();

Expand Down Expand Up @@ -38,6 +38,7 @@
$errorSpecApi->setHumanTitle($genericTitle='Too much options');
$errorSpecApi->setHumanDetails($specificDetails='Please, choose a bit less. Consult your ...');
$errorSpecApi->setAboutLink($specificAboutLink='https://www.example.com/explanation.html', ['foo'=>'bar']);
$errorSpecApi->appendTypeLink($genericTypeLink='https://www.example.com/documentation.html', ['foo'=>'bar']);

/**
* prepare multiple error objects for the errors response
Expand Down
32 changes: 32 additions & 0 deletions examples/example_profile.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

use alsvanzelf\jsonapi\ResourceDocument;

require 'bootstrap_examples.php';

/**
* use a profile as extension to the document
*
* allowing to define aliases for the keywords to solve conflicts between different profiles
*/

$profile = new ExampleVersionProfile(['version' => 'ref']);

$document = new ResourceDocument('user', 42);
$document->applyProfile($profile);

/**
* you can apply the rules of the profile manually
* or use methods of the profile if provided
*/

$profile->setVersion($document, '2019');

/**
* get the json
*/

$options = [
'prettyPrint' => true,
];
echo '<pre>'.$document->toJson($options);
2 changes: 2 additions & 0 deletions examples/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ <h3>Misc</h3>
<li><a href="null_values.php">Null values if explicitly not available</a></li>
<li><a href="meta_only.php">Meta-only use-cases</a></li>
<li><a href="status_only.php">Status-only</a></li>
<li><a href="example_profile.php">Example profile</a></li>
<li><a href="cursor_pagination_profile.php">Cursor pagination profile</a></li>
<li><a href="output.php">Different ways to output</a></li>
</ul>

Expand Down
8 changes: 3 additions & 5 deletions src/CollectionDocument.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use alsvanzelf\jsonapi\DataDocument;
use alsvanzelf\jsonapi\Document;
use alsvanzelf\jsonapi\exceptions\InputException;
use alsvanzelf\jsonapi\interfaces\PaginableInterface;
use alsvanzelf\jsonapi\interfaces\RecursiveResourceContainerInterface;
use alsvanzelf\jsonapi\interfaces\ResourceContainerInterface;
use alsvanzelf\jsonapi\interfaces\ResourceInterface;
Expand All @@ -15,7 +16,7 @@
* this document is a set of Resources
* this document should be used if there could be multiple, also if only one or even none is returned
*/
class CollectionDocument extends DataDocument implements ResourceContainerInterface {
class CollectionDocument extends DataDocument implements PaginableInterface, ResourceContainerInterface {
/** @var ResourceInterface[] */
protected $resources = [];
/** @var array */
Expand Down Expand Up @@ -63,10 +64,7 @@ public function add($type, $id, array $attributes=[]) {
}

/**
* @param string $previousHref optional
* @param string $nextHref optional
* @param string $firstHref optional
* @param string $lastHref optional
* @inheritDoc
*/
public function setPaginationLinks($previousHref=null, $nextHref=null, $firstHref=null, $lastHref=null) {
if ($previousHref !== null) {
Expand Down
65 changes: 40 additions & 25 deletions src/Document.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,28 @@

use alsvanzelf\jsonapi\exceptions\Exception;
use alsvanzelf\jsonapi\exceptions\InputException;
use alsvanzelf\jsonapi\helpers\AtMemberManager;
use alsvanzelf\jsonapi\helpers\Converter;
use alsvanzelf\jsonapi\helpers\HttpStatusCodeManager;
use alsvanzelf\jsonapi\helpers\LinksManager;
use alsvanzelf\jsonapi\helpers\Validator;
use alsvanzelf\jsonapi\interfaces\DocumentInterface;
use alsvanzelf\jsonapi\interfaces\ProfileInterface;
use alsvanzelf\jsonapi\objects\JsonapiObject;
use alsvanzelf\jsonapi\objects\LinkObject;
use alsvanzelf\jsonapi\objects\LinksObject;
use alsvanzelf\jsonapi\objects\MetaObject;
use alsvanzelf\jsonapi\objects\ProfileLinkObject;

/**
* @see ResourceDocument, CollectionDocument, ErrorsDocument or MetaDocument
*/
abstract class Document implements DocumentInterface, \JsonSerializable {
use HttpStatusCodeManager;
use AtMemberManager, HttpStatusCodeManager, LinksManager;

const JSONAPI_VERSION_1_0 = '1.0';
const JSONAPI_VERSION_1_1 = '1.1';
const JSONAPI_VERSION_LATEST = Document::JSONAPI_VERSION_1_0;
const JSONAPI_VERSION_LATEST = Document::JSONAPI_VERSION_1_1;

const CONTENT_TYPE_OFFICIAL = 'application/vnd.api+json';
const CONTENT_TYPE_DEBUG = 'application/json';
Expand All @@ -30,12 +35,12 @@ abstract class Document implements DocumentInterface, \JsonSerializable {
const LEVEL_JSONAPI = 'jsonapi';
const LEVEL_RESOURCE = 'resource';

/** @var LinksObject */
protected $links;
/** @var MetaObject */
protected $meta;
/** @var JsonapiObject */
protected $jsonapi;
/** @var ProfileInterface[] */
protected $profiles = [];
/** @var array */
protected static $defaults = [
/**
Expand Down Expand Up @@ -152,25 +157,6 @@ public function addMeta($key, $value, $level=Document::LEVEL_ROOT) {
* spec api
*/

/**
* @param string $key
* @param LinkObject $linkObject
*/
public function addLinkObject($key, LinkObject $linkObject) {
if ($this->links === null) {
$this->setLinksObject(new LinksObject());
}

$this->links->addLinkObject($key, $linkObject);
}

/**
* @param LinksObject $linksObject
*/
public function setLinksObject(LinksObject $linksObject) {
$this->links = $linksObject;
}

/**
* @param MetaObject $metaObject
*/
Expand All @@ -192,6 +178,33 @@ public function unsetJsonapiObject() {
$this->jsonapi = null;
}

/**
* apply a profile which adds the link and sets a correct content-type
*
* note that the rules from the profile are not automatically enforced
* applying the rules, and applying them correctly, is manual
* however the $profile could have custom methods to help
*
* @see https://jsonapi.org/format/1.1/#profiles
*
* @param ProfileInterface $profile
*/
public function applyProfile(ProfileInterface $profile) {
$this->profiles[] = $profile;

if ($this->links === null) {
$this->setLinksObject(new LinksObject());
}

$link = $profile->getAliasedLink();
if ($link instanceof LinkObject) {
$this->links->appendLinkObject('profile', $link);
}
else {
$this->links->append('profile', $link);
}
}

/**
* DocumentInterface
*/
Expand All @@ -200,7 +213,7 @@ public function unsetJsonapiObject() {
* @inheritDoc
*/
public function toArray() {
$array = [];
$array = $this->getAtMembers();

if ($this->jsonapi !== null && $this->jsonapi->isEmpty() === false) {
$array['jsonapi'] = $this->jsonapi->toArray();
Expand Down Expand Up @@ -253,7 +266,9 @@ public function sendResponse(array $options=[]) {
$json = ($options['json'] !== null) ? $options['json'] : $this->toJson($options);

http_response_code($this->httpStatusCode);
header('Content-Type: '.$options['contentType']);

$contentType = Converter::mergeProfilesInContentType($options['contentType'], $this->profiles);
header('Content-Type: '.$contentType);

echo $json;
}
Expand Down
Loading

0 comments on commit 623b0aa

Please sign in to comment.