Skip to content
This repository was archived by the owner on Jan 2, 2023. It is now read-only.

Latest commit

 

History

History
300 lines (208 loc) · 9.4 KB

File metadata and controls

300 lines (208 loc) · 9.4 KB

PHP JSON-API

Build Status Coverage Status Quality Score Pre Release License

JSON-API responses in PHP.

Works with version 1.0 of the spec.

Install

via Composer:

composer require tobscure/json-api

Usage

use Tobscure\JsonApi\Document;

$resource = new PostResource($post);

$document = Document::fromData($resource);

$document->setInclude(['author', 'comments']);
$document->setFields(['posts' => ['title', 'body']]);

$document->setMetaItem('total', count($posts));
$document->setSelfLink('http://example.com/api/posts/1');

header('Content-Type: ' . $document::MEDIA_TYPE);
echo json_encode($document);

Resources

Resources are used to create JSON-API resource objects. They must implement Tobscure\JsonApi\ResourceInterface. An AbstractResource class is provided with some basic functionality. Subclasses must specify the resource $type and implement the getId() method:

use Tobscure\JsonApi\AbstractResource;

class PostResource extends AbstractResource
{
    protected $type = 'posts';

    protected $post;

    public function __construct(Post $post)
    {
        $this->post = $post;
    }

    public function getId()
    {
        return $this->post->id;
    }
}

A JSON-API document can then be created from an instantiated resource:

$resource = new PostResource($post);

$document = Document::fromData($resource);

To output a collection of resource objects, map your data to an array of resources:

$resources = array_map(function (Post $post) {
    return new PostResource($post);
}, $posts);

$document = Document::fromData($resources);

Attributes & Sparse Fieldsets

To add attributes to your resource objects, you may implement the getAttributes() method in your resource:

    public function getAttributes(array $fields = null)
    {
        return [
            'title' => $this->post->title,
            'body'  => $this->post->body,
            'date'  => $this->post->date
        ];
    }

To output resource objects with a sparse fieldset, pass in an array of fields (attributes and relationships), organised by resource type:

$document->setFields(['posts' => ['title', 'body']]);

The attributes returned by your resources will automatically be filtered according to the sparse fieldset for the resource type. However, if some attributes are expensive to calculate, then you can use the $fields argument provided to getAttributes(). This will be an array of fields, or null if no sparse fieldset has been specified.

    public function getAttributes(array $fields = null)
    {
        // Calculate the "expensive" attribute only if this field will show up
        // in the final output
        if ($fields === null || in_array('expensive', $fields)) {
            $attributes['expensive'] = $this->getExpensiveAttribute();
        }

        return $attributes;
    }

Relationships

You can include related resources alongside the document's primary data. First you must define the available relationships on your resource. The AbstractResource base class allows you to define a method for each relationship. Relationship methods should return a Tobscure\JsonApi\Relationship instance, containing the related resource(s).

    protected function getAuthorRelationship()
    {
        $resource = new UserResource($this->post->author);

        return Relationship::fromData($resource);
    }

You can then specify which relationship paths should be included on the document:

$document->setInclude(['author', 'comments', 'comments.author']);

By default, the AbstractResource implementation will convert included relationship names from kebab-case and snake_case into a getCamelCaseRelationship method name. If you wish to customize this behaviour, you may override the getRelationship method:

    public function getRelationship($name)
    {
        // resolve Relationship for $name
    }

Meta Information & Links

The Document, AbstractResource, and Relationship classes allow you to add meta information:

$document->setMeta(['key' => 'value']);
$document->setMetaItem('key', 'value');
$document->removeMetaItem('key');

They also allow you to add links. A link's value may be a string, or a Tobscure\JsonApi\Link instance.

use Tobscure\JsonApi\Link;

$resource->setSelfLink('url');
$relationship->setRelatedLink(new Link('url', ['some' => 'metadata']));

You can also easily generate pagination links on Document and Relationship instances:

$document->setPaginationLinks(
    'url', // The base URL for the links
    $_GET, // The query params provided in the request
    40,    // The current offset
    20,    // The current limit
    100    // The total number of results
);

To define meta information and/or links globally for a resource type, call the appropriate methods in the constructor:

use Tobscure\JsonApi\AbstractResource;

class PostResource extends AbstractResource
{    
    public function __construct(Post $post)
    {
        $this->post = $post;

        $this->setSelfLink('/posts/' . $post->id);
        $this->setMetaItem('some', 'metadata for ' . $post->id);
    }

    // ...
}

Parameters

The Tobscure\JsonApi\Parameters class allows you to easily parse and validate query parameters in accordance with the specification.

use Tobscure\JsonApi\Parameters;

$parameters = new Parameters($_GET);

getInclude

Get the relationships requested for inclusion. Provide an array of available relationship paths; if anything else is present, an InvalidParameterException will be thrown.

// GET /api?include=author,comments
$include = $parameters->getInclude(['author', 'comments', 'comments.author']); // ['author', 'comments']

$document->setInclude($include);

getFields

Get the sparse fieldsets requested for inclusion, keyed by resource type.

// GET /api?fields[articles]=title,body
$fields = $parameters->getFields(); // ['articles' => ['title', 'body']]

$document->setFields($fields);

getSort

Get the requested sort fields. Provide an array of available fields that can be sorted by; if anything else is present, an InvalidParameterException will be thrown.

// GET /api?sort=-created,title
$sort = $parameters->getSort(['title', 'created']); // ['created' => 'desc', 'title' => 'asc']

getLimit and getOffset

Get the offset number and the number of resources to display using a page- or offset-based strategy. getLimit accepts an optional maximum. If the calculated offset is below zero, an InvalidParameterException will be thrown.

// GET /api?page[number]=5&page[size]=20
$limit = $parameters->getLimit(100); // 20
$offset = $parameters->getOffset($limit); // 80

// GET /api?page[offset]=20&page[limit]=200
$limit = $parameters->getLimit(100); // 100
$offset = $parameters->getOffset(); // 20

getFilter

Get the contents of the filter query parameter.

// GET /api?filter[author]=toby
$filter = $parameters->getFilter(); // ['author' => 'toby']

Errors

You can create a Document containing error objects using Tobscure\JsonApi\Error instances:

use Tobscure\JsonApi\Error;

$error = new Error;

$error->setId('1');
$error->setAboutLink('url');
$error->setMeta('key', 'value');
$error->setStatus(400);
$error->setCode('123');
$error->setTitle('Something Went Wrong');
$error->setDetail('You dun goofed!');
$error->setSourcePointer('/data/attributes/body');
$error->setSourceParameter('include');

$document = Document::fromErrors([$error]);

Examples

  • Flarum is forum software that uses tobscure/json-api to power its API.

Contributing

Feel free to send pull requests or create issues if you come across problems or have great ideas. Any input is appreciated!

Running Tests

$ vendor/bin/phpunit

License

This code is published under the The MIT License. This means you can do almost anything with it, as long as the copyright notice and the accompanying license file is left intact.