JSON API library of the PHP Ride framework.
Check http://jsonapi.org for a full reference of the standard.
The JsonApi class is the starting point for your implementation. It's the container for the resource adapters and a factory for other instances of the library. You should register your resource adapters before doing anything else with the API.
The only interface in this library is the JsonApiResourceAdapter interface. The implementations of this interface converts data entries from your data model into JSON API resources. You need an instance of this interface for each data type you want to expose with your API.
The JsonApiResource class is the data container for a resource, eg a data entry from your data model. The instances of this class are set to the responding document of an API request, either as a single instance, or in an array for a collection of resources.
The JsonApiDocument is the container of the responding document of an API request. You can set your resource(s) or error(s) as content to this document. The content of your document can even be strictly meta.
The JsonApiDocument instance holds your JsonApi instance and a JsonApiQuery instance. It's passed on to the resource adapter when converting entries into resources. Using the JsonApi instance and the JsonApiQuery, the JsonApiResourceAdapter can create the JsonApiResource instance as the client requested.
Create a JsonApiQuery instance from your incoming query parameters. The JsonApiQuery instance will give you easy access to the requested resources and pagination, sort and filter values.
A controller for your API could be something like this:
<?php
include ride\library\http\jsonapi\JsonApiQuery;
include ride\library\http\jsonapi\JsonApi;
/**
* Controller for the blog post API
*/
class BlogPostController {
/**
* Constructs a new controller
* @param ride\library\http\jsonapi\JsonApi $api
*/
public function __construct(JsonApi $api) {
$this->api = $api;
}
/**
* Action to fetch a collection of blog posts
*/
public function indexAction() {
$query = $this->api->createQuery($_GET);
$document = $this->api->createDocument($query);
$blogPosts = $this->getBlogPosts($query, $total);
$document->setResourceCollection(BlogPostResourceAdapter::TYPE, $blogPosts);
$document->setMeta('total', $total);
http_status_code($document->getStatusCode());
if ($document->hasContent()) {
header('Content-Type: ' . JsonApi::CONTENT_TYPE);
echo json_encode($document);
}
}
/**
* Gets the blog posts from the data source
* @param ride\library\http\jsonapi\JsonApiQuery $query Requested query
* @param integer $total Total results before pagination
* @return array
*/
private function getBlogPosts(JsonApiQuery $query, &$total) {
// static data as an example
$blogPosts = array(
1 => array(
'id' => 1,
'title' => 'Lorum Ipsum',
'author' => array(
'id' => 1,
'name' => 'John Doe',
),
'tags' => array(
1 => array(
'id' => 1,
'name' => 'lorum',
),
2 => array(
'id' => 2,
'name' => 'ipsum',
),
),
),
// ...
);
// count the total
$total = count($blogPosts);
// apply pagination
$blogPosts = array_slice($blogPosts, $query->getOffset(), $query->getLimit(10, 100));
// return the result
return $blogPosts;
}
}
The resource adapter for the blog posts in the previous example could be something like this:
<?php
include ride\library\http\jsonapi\JsonApiDocument;
include ride\library\http\jsonapi\JsonApiResourceAdapter;
/**
* Resource adapter for a blog post
*/
class BlogPostResourceAdapter implements JsonApiResourceAdapter {
/**
* Type of this resource
* @var string
*/
const TYPE = 'blog-posts';
/**
* Gets a resource instance for the provided model data
* @param mixed $data Data to adapt
* @param ride\library\http\jsonapi\JsonApiDocument $document Requested document
* @param string $relationshipPath dot-separated list of relationship names
* @return ride\library\http\jsonapi\JsonApiResource
*/
public function getResource($data, JsonApiDocument $document, $relationshipPath = null) {
$api = $document->getApi();
$query = $document->getQuery();
// create the resource for the entry
$resource = $api->createResource(self::TYPE, $data['id']);
$resource->setLink('self', 'http://your-resource-url');
// check for an attribute and set when requested
if ($query->isFieldRequested(self::TYPE, 'title')) {
$resource->setAttribute('title', $data['title']);
}
// check for a relationship and set when requested
if ($query->isFieldRequested(self::TYPE, 'author') && $query->isIncluded($relationshipPath)) {
// append the field to the relationship path
$fieldRelationshipPath = ($relationshipPath ? $relationshipPath . '.' : '') . 'author';
// retrieve the resource
$peopleResourceAdapter = $api->getResourceAdapter('people');
$author = $peopleResourceAdapter->getResource($data['author'], $document, $fieldRelationshipPath);
// create the relationship
$relationship = $api->createRelationship();
$relationship->setResource($author);
$relationship->setLink('self', 'http://your-relationship-url');
$relationship->setLink('related', 'http://your-related-url');
// add the relationship to your resource
$resource->setRelationship('author', $relationship);
}
// set a relationship collection value
if ($query->isFieldRequested(self::TYPE, 'tags') && $query->isIncluded($relationshipPath)) {
$fieldRelationshipPath = ($relationshipPath ? $relationshipPath . '.' : '') . 'tags';
$tagResourceAdapter = $api->getResourceAdapter('tags');
$tags = $data['tags'];
foreach ($tags as $tag) {
$tags[$tagIndex] = $tagResourceAdapter->getResource($tag, $document);
}
$relationship = $api->createRelationship();
$relationship->setResourceCollection($tags);
$relationship->setLink('self', 'http://your-relationship-url');
$relationship->setLink('related', 'http://your-related-url');
$resource->setRelationship('tags', $relationship);
}
// return the resource
return $resource;
}
}
For more examples, you can check the following implementations of this library:
You can use Composer to install this library.
composer require ride/lib-http-jsonapi