2017 © Pedro Peláez
 

library haljson

HAL+JSON & HAL+XML API transformer outputting valid API responses.

image

nilportugues/haljson

HAL+JSON & HAL+XML API transformer outputting valid API responses.

  • Saturday, June 11, 2016
  • by nilportugues
  • Repository
  • 3 Watchers
  • 17 Stars
  • 552 Installations
  • PHP
  • 0 Dependents
  • 0 Suggesters
  • 2 Forks
  • 4 Open issues
  • 17 Versions
  • 0 % Grown

The README.md

HAL+JSON & HAL+XML API Transformer

Build Status Scrutinizer Code Quality SensioLabsInsight Latest Stable Version Total Downloads License Donate, (*1)

  1. Installation
  2. Mapping
  3. HAL Serialization
  4. HAL Paginated Resource
  5. PSR-7 Response objects

1. Installation

Use Composer to install the package:, (*2)

$ composer require nilportugues/hal

2. Mapping

Given a PHP Object, and a series of mappings, the HAL+JSON and HAL+XML API transformer will represent the given data following the https://tools.ietf.org/html/draft-kelly-json-hal-07 specification draft., (*3)

For instance, given the following piece of code, defining a Blog Post and some comments:, (*4)

$post = new Post(
  new PostId(9),
  'Hello World',
  'Your first post',
  new User(
      new UserId(1),
      'Post Author'
  ),
  [
      new Comment(
          new CommentId(1000),
          'Have no fear, sers, your king is safe.',
          new User(new UserId(2), 'Barristan Selmy'),
          [
              'created_at' => (new DateTime('2015/07/18 12:13:00'))->format('c'),
              'accepted_at' => (new DateTime('2015/07/19 00:00:00'))->format('c'),
          ]
      ),
  ]
);

We will have to map all the involved classes. This can be done as one single array, or a series of Mapping classes., (*5)

2.1 Mapping with arrays

Mapping involved classes using arrays is done as follows:, (*6)

use NilPortugues\Api\Mapping\Mapper;

$mappings = [
    [
        'class' => Post::class,
        'alias' => 'Message',
        'aliased_properties' => [
            'author' => 'author',
            'title' => 'headline',
            'content' => 'body',
        ],
        'hide_properties' => [

        ],
        'id_properties' => [
            'postId',
        ],
        'urls' => [
            // Mandatory
            'self' => 'http://example.com/posts/{postId}',
             // Optional
            'comments' => 'http://example.com/posts/{postId}/comments'
        ],
        'curies' => [
            'name' => 'example',
            'href' => "http://example.com/docs/rels/{rel}",
        ]
    ],
    [
        'class' => User::class,
        'alias' => '',
        'aliased_properties' => [],
        'hide_properties' => [],
        'id_properties' => [
            'userId',
        ],
        'urls' => [
            'self' => 'http://example.com/users/{userId}',
        ],
        'curies' => [
            'name' => 'example',
            'href' => "http://example.com/docs/rels/{rel}",
        ]
    ],
    [
        'class' => Comment::class,
        'alias' => '',
        'aliased_properties' => [],
        'hide_properties' => [],
        'id_properties' => [
            'commentId',
        ],
        'urls' => [
            'self' => 'http://example.com/comments/{commentId}',
        ],
        'curies' => [
            'name' => 'example',
            'href' => "http://example.com/docs/rels/{rel}",
        ]
    ]
];

$mapper = new Mapper($mappings);

2.2 Mapping with Mapping class

In order to map with Mapping class, you need to create a new class for each involved class., (*7)

This mapping fashion scales way better than using an array., (*8)

All Mapping classes will extend the \NilPortugues\Api\Mappings\HalMapping interface., (*9)


// PostMapping.php class PostMapping implements \NilPortugues\Api\Mappings\HalMapping { public function getClass() { return Post::class; } public function getAlias() { return 'Message'; } public function getAliasedProperties() { return [ 'author' => 'author', 'title' => 'headline', 'content' => 'body', ]; } public function getHideProperties() { return []; } public function getIdProperties() { return ['postId']; } public function getUrls() { return [ 'self' => 'http://example.com/posts/{postId}', // Mandatory 'comments' => 'http://example.com/posts/{postId}/comments' // Optional ]; } public function getCuries() { return [ 'name' => 'example', 'href' => "http://example.com/docs/rels/{rel}", ]; } } // UserMapping.php class UserMapping implements \NilPortugues\Api\Mappings\HalMapping { public function getClass() { return User::class; } public function getAlias() { return ''; } public function getAliasedProperties() { return []; } public function getHideProperties() { return []; } public function getIdProperties() { return ['postId']; } public function getUrls() { return [ 'self' => 'http://example.com/users/{userId}' ]; } public function getCuries() { return [ 'name' => 'example', 'href' => "http://example.com/docs/rels/{rel}", ]; } } // CommentMapping.php class CommentMapping implements \NilPortugues\Api\Mappings\HalMapping { public function getClass() { return Comment::class; } public function getAlias() { return ''; } public function getAliasedProperties() { return []; } public function getHideProperties() { return []; } public function getIdProperties() { return ['commentId']; } public function getUrls() { return [ 'self' => 'http://example.com/comments/{commentId}', ]; } public function getCuries() { return [ 'name' => 'example', 'href' => "http://example.com/docs/rels/{rel}", ]; } } $mappings = [ PostMapping::class, UserMapping::class, CommentMapping::class, ]; $mapper = new Mapper($mappings);

3. HAL Serialization

Calling the transformer will output a valid HAL response using the correct formatting:, (*10)

use NilPortugues\Api\Hal\JsonTransformer; 
use NilPortugues\Api\Hal\HalSerializer;
use NilPortugues\Api\Hal\Http\Message\Response;

$transformer = new JsonTransformer($mapper);
//For XML: $transformer = new XmlTransformer($mapper);

//Output transformation
$serializer = new HalSerializer($transformer);
$output = $serializer->serialize($post);

//PSR7 Response with headers and content.
$response = new Response($output);

header(
    sprintf(
        'HTTP/%s %s %s',
        $response->getProtocolVersion(),
        $response->getStatusCode(),
        $response->getReasonPhrase()
    )
);

foreach($response->getHeaders() as $header => $values) {
    header(sprintf("%s:%s\n", $header, implode(', ', $values)));
}

echo $response->getBody();

3.1 HAL+JSON

HTTP/1.1 200 OK
Cache-Control: private, max-age=0, must-revalidate
Content-type: application/hal+json

Output:, (*11)

{
    "post_id": 9,
    "headline": "Hello World",
    "body": "Your first post",
    "_embedded": {
        "author": {
            "user_id": 1,
            "name": "Post Author",
            "_links": {
                "self": {
                    "href": "http://example.com/users/1"
                },
                "example:friends": {
                    "href": "http://example.com/users/1/friends"
                },
                "example:comments": {
                    "href": "http://example.com/users/1/comments"
                }
            }
        },
        "comments": [
            {
                "comment_id": 1000,
                "dates": {
                    "created_at": "2015-08-13T22:47:45+02:00",
                    "accepted_at": "2015-08-13T23:22:45+02:00"
                },
                "comment": "Have no fear, sers, your king is safe.",
                "_embedded": {
                    "user": {
                        "user_id": 2,
                        "name": "Barristan Selmy",
                        "_links": {
                            "self": {
                                "href": "http://example.com/users/2"
                            },
                            "example:friends": {
                                "href": "http://example.com/users/2/friends"
                            },
                            "example:comments": {
                                "href": "http://example.com/users/2/comments"
                            }
                        }
                    }
                },
                "_links": {
                    "example:user": {
                        "href": "http://example.com/users/2"
                    },
                    "self": {
                        "href": "http://example.com/comments/1000"
                    }
                }
            }
        ]
    },
    "_links": {
        "curies": [
            {
                "name": "example",
                "href": "http://example.com/docs/rels/{rel}",
                "templated": true
            }
        ],
        "self": {
            "href": "http://example.com/posts/9"
        },
        "example:author": {
            "href": "http://example.com/users/1"
        },
        "example:comments": {
            "href": "http://example.com/posts/9/comments"
        }
    }
}

3.2 HAL+XML

For XML output use the sample code but using the XML transformer instead:, (*12)

$transformer = new XmlTransformer($mapper);

Output:, (*13)

HTTP/1.1 200 OK
Cache-Control: private, max-age=0, must-revalidate
Content-type: application/hal+xml
<?xml version="1.0" encoding="UTF-8"?>
<resource href="http://example.com/posts/9">
  <post_id><![CDATA[9]]></post_id>
  <headline><![CDATA[Hello World]]></headline>
  <body><![CDATA[Your first post]]></body>
  <embedded>
    <resource href="http://example.com/users/1" rel="author">
      <user_id><![CDATA[1]]></user_id>
      <name><![CDATA[Post Author]]></name>
      <links>
        <link rel="self" href="http://example.com/users/1"/>
        <link rel="example:friends" href="http://example.com/users/1/friends"/>
        <link rel="example:comments" href="http://example.com/users/1/comments"/>
      </links>
    </resource>
    <comments>
      <resource href="http://example.com/comments/1000">
        <comment_id><![CDATA[1000]]></comment_id>
        <dates>
          <created_at><![CDATA[2015-07-18T12:13:00+00:00]]></created_at>
          <accepted_at><![CDATA[2015-07-19T00:00:00+00:00]]></accepted_at>
        </dates>
        <comment><![CDATA[Have no fear, sers, your king is safe.]]></comment>
        <embedded>
          <resource href="http://example.com/users/2" rel="user">
            <user_id><![CDATA[2]]></user_id>
            <name><![CDATA[Barristan Selmy]]></name>
            <links>
              <link rel="self" href="http://example.com/users/2"/>
              <link rel="example:friends" href="http://example.com/users/2/friends"/>
              <link rel="example:comments" href="http://example.com/users/2/comments"/>
            </links>
          </resource>
        </embedded>
        <links>
          <link rel="example:user" href="http://example.com/users/2"/>
          <link rel="self" href="http://example.com/comments/1000"/>
        </links>
      </resource>
    </comments>
  </embedded>
  <links>
    <curies>
      <link rel="resource" href="http://example.com/docs/rels/{rel}">
        <name><![CDATA[example]]></name>
        <templated><![CDATA[true]]></templated>
      </link>
    </curies>
    <link rel="self" href="http://example.com/posts/9"/>
    <link rel="example:author" href="http://example.com/users/1"/>
    <link rel="example:comments" href="http://example.com/posts/9/comments"/>
  </links>
</resource>

4. HAL Paginated Resource

A pagination object to easy the usage of this package is provided., (*14)

For both XML and JSON output, use the HalPagination object to build your paginated representation of the current resource., (*15)

Methods provided by HalPagination are as follows:, (*16)

  • setSelf($self)
  • setFirst($first)
  • setPrev($prev)
  • setNext($next)
  • setLast($last)
  • setCount($count)
  • setTotal($total)
  • setEmbedded(array $embedded)

In order to use it, create a new HalPagination instance, use the setters and pass the instance to the serialize($value) method of the serializer., (*17)

Everything else will be handled by serializer itself. Easy as that!, (*18)

use NilPortugues\Api\Hal\HalPagination; 
use NilPortugues\Api\Hal\HalSerializer; 
use NilPortugues\Api\Hal\JsonTransformer; 

// ...
//$objects is an array of objects, such as Post::class.
// ...

$page = new HalPagination();

//set the amounts
$page->setTotal(20);
$page->setCount(10);

//set the objects
$page->setEmbedded($objects);

//set up the pagination links
$page->setSelf('/post?page=1');
$page->setPrev('/post?page=1');
$page->setFirst('/post?page=1');
$page->setLast('/post?page=1');

$output = $serializer->serialize($page);

5. Response objects

The following PSR-7 Response objects providing the right headers and HTTP status codes are available., (*19)

Its use is optional and are provided as a starting point., (*20)

  • NilPortugues\Api\Hal\Http\Message\ErrorResponse($body)
  • NilPortugues\Api\Hal\Http\Message\ResourceCreatedResponse($body)
  • NilPortugues\Api\Hal\Http\Message\ResourceDeletedResponse($body)
  • NilPortugues\Api\Hal\Http\Message\ResourceNotFoundResponse($body)
  • NilPortugues\Api\Hal\Http\Message\ResourcePatchErrorResponse($body)
  • NilPortugues\Api\Hal\Http\Message\ResourcePostErrorResponse($body)
  • NilPortugues\Api\Hal\Http\Message\ResourceProcessingResponse($body)
  • NilPortugues\Api\Hal\Http\Message\ResourceUpdatedResponse($body)
  • NilPortugues\Api\Hal\Http\Message\Response($body)
  • NilPortugues\Api\Hal\Http\Message\UnsupportedActionResponse($body)

Quality

To run the PHPUnit tests at the command line, go to the tests directory and issue phpunit., (*21)

This library attempts to comply with PSR-1, PSR-2, PSR-4 and PSR-7., (*22)

If you notice compliance oversights, please send a patch via Pull Request., (*23)

Contribute

Contributions to the package are always welcome!, (*24)

Support

Get in touch with me using one of the following means:, (*25)

Authors

License

The code base is licensed under the MIT license., (*26)

The Versions

11/06 2016

dev-master

9999999-dev http://nilportugues.com

HAL+JSON & HAL+XML API transformer outputting valid API responses.

  Sources   Download

MIT

The Requires

 

The Development Requires

api json serializer psr7 hal response transformer

11/06 2016

2.0.3

2.0.3.0 http://nilportugues.com

HAL+JSON & HAL+XML API transformer outputting valid API responses.

  Sources   Download

MIT

The Requires

 

The Development Requires

api json serializer psr7 hal response transformer

11/06 2016

2.0.2

2.0.2.0 http://nilportugues.com

HAL+JSON & HAL+XML API transformer outputting valid API responses.

  Sources   Download

MIT

The Requires

 

The Development Requires

api json serializer psr7 hal response transformer

26/12 2015

2.0.1

2.0.1.0 http://nilportugues.com

HAL+JSON & HAL+XML API transformer outputting valid API responses.

  Sources   Download

MIT

The Requires

 

The Development Requires

api json serializer psr7 hal response transformer

26/12 2015

2.0.0

2.0.0.0 http://nilportugues.com

HAL+JSON & HAL+XML API transformer outputting valid API responses.

  Sources   Download

MIT

The Requires

 

The Development Requires

api json serializer psr7 hal response transformer

24/11 2015

1.0.11

1.0.11.0 http://nilportugues.com

HAL+JSON API transformer outputting valid API responses.

  Sources   Download

MIT

The Requires

 

The Development Requires

api json serializer psr7 hal response transformer

02/11 2015

1.0.10

1.0.10.0 http://nilportugues.com

HAL+JSON API transformer outputting valid API responses.

  Sources   Download

MIT

The Requires

 

The Development Requires

api json serializer psr7 hal response transformer

02/11 2015

1.0.9

1.0.9.0 http://nilportugues.com

HAL+JSON API transformer outputting valid API responses.

  Sources   Download

MIT

The Requires

 

The Development Requires

api json serializer psr7 hal response transformer

20/10 2015

1.0.8

1.0.8.0 http://nilportugues.com

HAL+JSON API transformer outputting valid API responses.

  Sources   Download

MIT

The Requires

 

The Development Requires

api json serializer psr7 hal response transformer

17/10 2015

1.0.7

1.0.7.0 http://nilportugues.com

HAL+JSON API transformer outputting valid API responses.

  Sources   Download

MIT

The Requires

 

The Development Requires

api json serializer psr7 hal response transformer

09/10 2015

1.0.6

1.0.6.0 http://nilportugues.com

HAL+JSON API transformer outputting valid API responses.

  Sources   Download

MIT

The Requires

 

The Development Requires

api json serializer psr7 hal response transformer

29/08 2015

1.0.5

1.0.5.0 http://nilportugues.com

HAL+JSON API transformer outputting valid API responses.

  Sources   Download

MIT

The Requires

 

The Development Requires

api json serializer psr7 hal response transformer

28/08 2015

1.0.4

1.0.4.0 http://nilportugues.com

HAL+JSON API transformer outputting valid API responses.

  Sources   Download

MIT

The Requires

 

The Development Requires

api json serializer psr7 hal response transformer

23/08 2015

1.0.3

1.0.3.0 http://nilportugues.com

HAL+JSON API transformer outputting valid API responses.

  Sources   Download

MIT

The Requires

 

The Development Requires

api json serializer psr7 hal response transformer

16/08 2015

1.0.2

1.0.2.0 http://nilportugues.com

HAL+JSON API transformer outputting valid API responses.

  Sources   Download

MIT

The Requires

 

The Development Requires

api json serializer psr7 hal response transformer