PHPackages                             nilportugues/haljson - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [API Development](/categories/api)
4. /
5. nilportugues/haljson

Abandoned → [nilportugues/hal](/?search=nilportugues%2Fhal)Library[API Development](/categories/api)

nilportugues/haljson
====================

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

2.1.0(9y ago)295684[3 issues](https://github.com/nilportugues/hal-transformer/issues)MITPHPPHP &gt;=5.5.0

Since Aug 15Pushed 7y ago2 watchersCompare

[ Source](https://github.com/nilportugues/hal-transformer)[ Packagist](https://packagist.org/packages/nilportugues/haljson)[ Docs](http://nilportugues.com)[ RSS](/packages/nilportugues-haljson/feed)WikiDiscussions master Synced 1mo ago

READMEChangelog (10)Dependencies (3)Versions (20)Used By (0)

HAL+JSON &amp; HAL+XML API Transformer
======================================

[](#haljson--halxml-api-transformer)

[![Build Status](https://camo.githubusercontent.com/c4e66e6df467f987aee6fe61aec241ed6a8bb9ceb6f9e61912793e524775ff70/68747470733a2f2f7472617669732d63692e6f72672f6e696c706f727475677565732f7068702d68616c2e737667)](https://travis-ci.org/nilportugues/php-hal)[![Scrutinizer Code Quality](https://camo.githubusercontent.com/8e279207c769a38b804164671a9c8670f673cdfd87dbd8c57721a711a0291368/68747470733a2f2f7363727574696e697a65722d63692e636f6d2f672f6e696c706f727475677565732f68616c2d7472616e73666f726d65722f6261646765732f7175616c6974792d73636f72652e706e673f623d6d6173746572)](https://scrutinizer-ci.com/g/nilportugues/hal-transformer/?branch=master)[![SensioLabsInsight](https://camo.githubusercontent.com/13adf772c863cb2cf9394317e51b0ae01652326f74a2ed04a7f739c46c6b5946/68747470733a2f2f696e73696768742e73656e73696f6c6162732e636f6d2f70726f6a656374732f65326135613362322d373039372d343738332d393931322d3063626161646430656430652f6d696e692e706e67)](https://insight.sensiolabs.com/projects/e2a5a3b2-7097-4783-9912-0cbaadd0ed0e)[![Latest Stable Version](https://camo.githubusercontent.com/ea2138f4f9c9e00ecc20cccbfd334c37f7430221bd416d5822fa00932fcd4830/68747470733a2f2f706f7365722e707567782e6f72672f6e696c706f727475677565732f68616c2f762f737461626c65)](https://packagist.org/packages/nilportugues/hal)[![Total Downloads](https://camo.githubusercontent.com/98e16683c085c3b3c2b9e36e8ea095971b617f68430c821221359680c7568c55/68747470733a2f2f706f7365722e707567782e6f72672f6e696c706f727475677565732f68616c2f646f776e6c6f616473)](https://packagist.org/packages/nilportugues/hal)[![License](https://camo.githubusercontent.com/43b33e5d368ae36352c0957662385dc2e3fa1732615dfc900cb5905cd9d9ec03/68747470733a2f2f706f7365722e707567782e6f72672f6e696c706f727475677565732f68616c2f6c6963656e7365)](https://packagist.org/packages/nilportugues/hal)[![Donate](https://camo.githubusercontent.com/7b6de155df30b37b25eb5fec52f9213680c3dbf067dfb7d7e2850ac4096c7d05/68747470733a2f2f7777772e70617970616c6f626a656374732e636f6d2f656e5f55532f692f62746e2f62746e5f646f6e6174655f534d2e676966)](https://paypal.me/nilportugues)

1. [Installation](#1-installation)
2. [Mapping](#2-mapping)
    - 2.1 [Mapping with arrays](#21-mapping-with-arrays)
    - 2.2 [Mapping with Mapping class](#22-mapping-with-mapping-class)
3. [HAL Serialization](#3-hal-serialization)
    - 3.1 [HAL+JSON](#31-haljson)
    - 3.2 [HAL+XML](#32-halxml)
4. [HAL Paginated Resource](#4-hal-paginated-resource)
5. [PSR-7 Response objects](#5-response-objects)

1. Installation
---------------

[](#1-installation)

Use [Composer](https://getcomposer.org) to install the package:

```
$ composer require nilportugues/hal
```

2. Mapping
----------

[](#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.

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

```
$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.

### 2.1 Mapping with arrays

[](#21-mapping-with-arrays)

Mapping involved classes using arrays is done as follows:

```
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

[](#22-mapping-with-mapping-class)

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

This mapping fashion scales way better than using an array.

All Mapping classes will extend the `\NilPortugues\Api\Mappings\HalMapping` interface.

```
// 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
--------------------

[](#3-hal-serialization)

Calling the transformer will output a **valid HAL response** using the correct formatting:

```
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

[](#31-haljson)

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

```

Output:

```
{
    "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

[](#32-halxml)

For XML output use the sample code but using the XML transformer instead:

```
$transformer = new XmlTransformer($mapper);

```

Output:

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

```

```

  9]]>
  Hello World]]>
  Your first post]]>

      1]]>
      Post Author]]>

        1000]]>

          2015-07-18T12:13:00+00:00]]>
          2015-07-19T00:00:00+00:00]]>

        Have no fear, sers, your king is safe.]]>

            2]]>
            Barristan Selmy]]>

        example]]>
        true]]>

```

4. HAL Paginated Resource
-------------------------

[](#4-hal-paginated-resource)

A pagination object to easy the usage of this package is provided.

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

Methods provided by `HalPagination` are as follows:

- `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.

Everything else will be handled by serializer itself. Easy as that!

```
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
-------------------

[](#5-response-objects)

The following PSR-7 Response objects providing the right headers and HTTP status codes are available.

Its use is optional and are provided as a starting point.

- `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
-------

[](#quality)

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

This library attempts to comply with [PSR-1](http://www.php-fig.org/psr/psr-1/), [PSR-2](http://www.php-fig.org/psr/psr-2/), [PSR-4](http://www.php-fig.org/psr/psr-4/) and [PSR-7](http://www.php-fig.org/psr/psr-7/).

If you notice compliance oversights, please send a patch via [Pull Request](https://github.com/nilportugues/hal-transformer/pulls).

Contribute
----------

[](#contribute)

Contributions to the package are always welcome!

- Report any bugs or issues you find on the [issue tracker](https://github.com/nilportugues/php-hal/issues/new).
- You can grab the source code at the package's [Git repository](https://github.com/nilportugues/php-hal).

Support
-------

[](#support)

Get in touch with me using one of the following means:

- Emailing me at
- Opening an [Issue](https://github.com/nilportugues/php-hal/issues/new)

Authors
-------

[](#authors)

- [Nil Portugués Calderó](http://nilportugues.com)
- [The Community Contributors](https://github.com/nilportugues/php-hal/graphs/contributors)

License
-------

[](#license)

The code base is licensed under the [MIT license](LICENSE).

###  Health Score

34

—

LowBetter than 77% of packages

Maintenance18

Infrequent updates — may be unmaintained

Popularity24

Limited adoption so far

Community12

Small or concentrated contributor base

Maturity67

Established project with proven stability

 Bus Factor1

Top contributor holds 97% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~36 days

Recently: every ~113 days

Total

17

Last Release

3346d ago

Major Versions

1.0.11 → 2.0.02015-12-26

### Community

Maintainers

![](https://www.gravatar.com/avatar/beba5d66ff48128da92c91850e69b9294768718bcdc12e61cc9ee8453d0d6b69?d=identicon)[nilportugues](/maintainers/nilportugues)

---

Top Contributors

[![nilportugues](https://avatars.githubusercontent.com/u/550948?v=4)](https://github.com/nilportugues "nilportugues (65 commits)")[![ordinateu](https://avatars.githubusercontent.com/u/40049007?v=4)](https://github.com/ordinateu "ordinateu (2 commits)")

---

Tags

apihalhal-jsonjsonmicroservicemicroservicesphpphp7transformerxmlresponsejsonapipsr7serializerhaltransformer

###  Code Quality

TestsPHPUnit

Code StylePHP CS Fixer

### Embed Badge

![Health badge](/badges/nilportugues-haljson/health.svg)

```
[![Health](https://phpackages.com/badges/nilportugues-haljson/health.svg)](https://phpackages.com/packages/nilportugues-haljson)
```

###  Alternatives

[nilportugues/json-api

Serializer transformers outputting valid API responses in JSON, JSON API and HAL+JSON API formats.

70106.2k3](/packages/nilportugues-json-api)[nilportugues/laravel5-json-api

Laravel 5 JSON API Transformer Package

31232.4k1](/packages/nilportugues-laravel5-json-api)[nilportugues/hal

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

317.7k2](/packages/nilportugues-hal)[nilportugues/jsonapi-bundle

Symfony 2 &amp; 3 JSON API Transformer Package

11446.0k](/packages/nilportugues-jsonapi-bundle)[nilportugues/laravel5-haljson

Laravel 5 HAL+JSON API Transformer Package

151.0k](/packages/nilportugues-laravel5-haljson)[nilportugues/laravel5-json-api-dingo

Laravel5 JSONAPI and Dingo together to build APIs fast

311.5k](/packages/nilportugues-laravel5-json-api-dingo)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
