Giter VIP home page Giter VIP logo

hal's Introduction

Nocarrier\Hal

Build Status Scrutinizer Code Quality

This is a library for creating documents in the application/hal+json and application/hal+xml hypermedia formats

It requires PHP 5.4 or later.

<?php
require_once 'vendor/autoload.php';

use Nocarrier\Hal;

$hal = new Hal('/orders');
$hal->addLink('next', '/orders?page=2');
$hal->addLink('search', '/orders?id={order_id}');

$resource = new Hal(
    '/orders/123',
    array(
        'total' => 30.00,
        'currency' => 'USD',
    )
);

$resource->addLink('customer', '/customer/bob', array('title' => 'Bob Jones <[email protected]>'));
$hal->addResource('order', $resource);
echo $hal->asJson();
echo $hal->asXml();

Installation

The preferred method of installation is via packagist as this provides the PSR-0 autoloader functionality. The following command will download and install the latest version of the Hal library into your project.

php composer.phar require nocarrier/hal

Alternatively, clone the project and install into your project manually.

License

Nocarrier\Hal is licensed under the MIT license.

Usage

Creating Hal Resources

A Hal resource can be created with no values set:

$hal = new \Nocarrier\Hal();

with a URI for the resource:

$hal = new \Nocarrier\Hal('/orders');

and also with an array of data:

$hal = new \Nocarrier\Hal('/orders', ['customerId' => 'CUS1234']);

Hal resources can also be created from existing XML or JSON documents:

$hal = \Nocarrier\Hal::fromJson($jsonString);
$hal = \Nocarrier\Hal::fromXml($xmlString);
$hal = \Nocarrier\Hal::fromXml($simpleXMLElement);

The depth of embedded resources parsed with both these methods is controlled by a second argument, which defaults to 0:

$hal = \Nocarrier\Hal::fromJson($jsonString, 5);

Getting Representations

The Hal resource can be formatted as JSON or XML:

$hal = new \Nocarrier\Hal('/orders', ['customerId' => 'CUS1234']);
$hal->asJson();

which with a first argument of true for pretty printing:

$hal = new \Nocarrier\Hal('/orders', ['customerId' => 'CUS1234']);
$hal->asJson(true);

gives:

{
    "customerId": "CUS1234",
    "_links": {
        "self": {"href": "/orders"}
    }
}

and

$hal = new \Nocarrier\Hal('/orders', ['customerId' => 'CUS1234']);
$hal->asXml(true);

gives:

<?xml version="1.0"?>
<resource href="/orders">
    <customerId>CUS1234</customerId>
</resource>

Data

The data can be set through setData and read with getData:

$hal = new \Nocarrier\Hal('/orders');
$hal->setData(['customerId' => 'CUS1234']);
$hal->getData();

Using array keys in the data for the XML representation can be done by prefixing the key with @:

$hal = new \Nocarrier\Hal('/orders');
$hal->setData(['customerId' => ['CUS1234', '@type' => 'legacy']]);

gives:

<?xml version="1.0"?>
<resource href="/orders">
    <customerId value="CUS1234" type="legacy"/>
</resource>

The @ is ignored if JSON is rendered:

{
    "customerId": {
        "value": "CUS1234",
        "type":" legacy"
    },
    "_links": {
        "self": {"href": "/orders"}
    }
}

Links

Links can be added to the resource by providing the rel identifying them and a URI:

$hal = new \Nocarrier\Hal('/orders', ['customerId' => 'CUS1234']);
$hal->addLink('next', '/orders?page=2');
$hal->addLink('search', '/orders?id={order_id}');

gives:

{
    "customerId": "CUS1234",
    "_links": {
        "self": {
            "href": "/orders"
        },
        "next": {
            "href": "/orders?page=2"
        },
        "search": {
            "href": "/orders?id={order_id}"
        }
    }
}

If a Hal object has been created from a response returned from elsewhere it can be helpful to retrieve the links from it.

$json = '{
     "customerId": "CUS1234",
     "_links": {
         "self": {
             "href": "/orders"
         },
         "next": {
             "href": "/orders?page=2"
         },
         "search": {
             "href": "/orders?id={order_id}"
         }
     }
 }';

$hal = \Nocarrier\Hal::fromJson($json);

foreach($hal->getLinks() as $rel => $links) {
    echo $rel."\n";
    foreach($links as $link) {
        echo (string) $link."\n";
    }
}
next
/orders?page=2
search
/orders?id={order_id}

and

$json = '{
     "customerId": "CUS1234",
     "_links": {
         "self": {
             "href": "/orders"
         },
         "next": {
             "href": "/orders?page=2"
         },
         "search": {
             "href": "/orders?id={order_id}"
         }
     }
 }';
$hal = \Nocarrier\Hal::fromJson($json);
foreach($hal->getLink('next') as $link) {
    echo (string) $link."\n";
}

outputs:

/orders?page=2

Embedded Resources

As well as linking to resources so that the client can fetch them they can be directly embedded in the resource.

$hal = new \Nocarrier\Hal('/orders', ['customerId' => 'CUS1234']);

$resource = new \Nocarrier\Hal(
    '/orders/123',
    array(
        'total' => 30.00,
        'currency' => 'USD',
    )
);

$resource->addLink('customer', '/customer/bob', array('title' => 'Bob Jones <[email protected]>'));
$hal->addResource('order', $resource);

outputs:

{
    "customerId": "CUS1234",
    "_links": {
        "self": {
            "href": "/orders"
        }
    },
    "_embedded": {
        "order": [
            {
                "total": 30,
                "currency": "USD",
                "_links": {
                    "self": {
                        "href": "/orders/123"
                    },
                    "customer": {
                        "href": "/customer/bob",
                        "title": "Bob Jones <[email protected]>"
                    }
                }
            }
        ]
    }
}

hal's People

Contributors

becw avatar chrisramakers avatar dave1010 avatar davedevelopment avatar dazz avatar echernyavskiy avatar f21 avatar hanneskod avatar jacobkiers avatar jsor avatar kuma-guy avatar luads avatar mattchannelgrabber avatar mcuadros avatar nickpeirson avatar nyholm avatar ramsey avatar reiz avatar richardmiller-zz avatar robbertnoordzij avatar rowan-m avatar scrutinizer-auto-fixer avatar wodor avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

hal's Issues

Any plans for unserialising?

I've not a lot of time at the minute, but if nobody else has it on their roadmap, I'll try and start in the next month or so.

I'd really like to do sub requests to get some of my embedded resources:

$resource = new Resource();
$resource->addResource($client->get("/some/sub/resource"));

Where client goes off, does a request by whatever means and returns a Nocarrier\Hal.

XML rendering is flawed

Unit test is committed for this and is failing.

Given this code:

        $resource = new Hal(
            '/orders/123',
            array(
                'tests' => array(
                    array(
                        'total' => 30.00,
                        'currency' => 'USD'
                    ),
                    array(
                        'total' => 40.00,
                        'currency' => 'GBP'
                    )
                )
            )
        );

I would expect the following XML

<resource href="/orders">
  <link rel="next" href="/orders?page=2"/>
  <link rel="search" href="/orders?id={order_id}"/>
  <resource rel="order" href="/orders/123">
    <link rel="customer" href="/customer/bob" title="Bob Jones &lt;[email protected]&gt;"/>
    <tests>
      <total>30</total>
      <currency>USD</currency>
    </tests>
    <tests>
      <total>40</total>
      <currency>GBP</currency>
    </tests>
  </resource>
</resource>

But what I actually get is

    <tests>
      <total>30</total>
      <currency>USD</currency>
      <total>40</total>
      <currency>GBP</currency>
    </tests>

Link attributes not handled by fromJson() and fromXml()

I'm trying to write functional tests for my application to ensure my HAL is valid, so I'm trying to parse my own output from this library with this library. However, it appears that fromJson() and fromXml() ignore link attributes, such as templated.

I'm assuming this is a bug, since it seems logical for Hal to be able to fully round-trip its own output.

Provide way to omit nulls from payload

Sometimes the HAL's payload comes direct from data source and it could be useful to ignore nullable values within the asJson method instead of iterating the payload and checking for nulls..

Nested resource cannot be json-ized

Nested resources which contain only one sub resource cannot be rendered as json.

Testing with only ONE line uncommented:

$hal = new \Nocarrier\Hal();
// following options do NOT work
// $hal->setResource(
//     'foo',
//     (new \Nocarrier\Hal())
//         ->setResource('bar', new \Nocarrier\Hal())
// );
// $hal->setResource(
//     'foo',
//     (new \Nocarrier\Hal())
//         ->addResource('bar', new \Nocarrier\Hal(), false)
// );
// $hal
//     ->setResource('foo1', (new \Nocarrier\Hal())->setResource('bar', new \Nocarrier\Hal()))
//     ->setResource('foo2', new \Nocarrier\Hal());
// $hal->setResource(
//     'foo',
//     (new \Nocarrier\Hal())
//         ->setResource('bar1', new \Nocarrier\Hal())
//         ->setResource('bar2', new \Nocarrier\Hal())
// );

// following options DO work
// $hal->setResource('foo', new \Nocarrier\Hal());
// $hal
//     ->setResource('foo1', new \Nocarrier\Hal())
//     ->setResource('foo2', new \Nocarrier\Hal());
// $hal->addResource(
//     'foo',
//     (new \Nocarrier\Hal())->setResource('bar', new \Nocarrier\Hal()),
//     false
// );
// $hal->addResource(
//     'foo',
//     (new \Nocarrier\Hal())->addResource('bar', new \Nocarrier\Hal(), false),
//     false
// );

echo ($hal->asJson(true));

Non-working options throw:
ErrorException in HalJsonRenderer.php line 164: Undefined offset: 0

Seems to relate to the test count($embedded) === 1 in https://github.com/blongden/hal/blob/master/src/Nocarrier/HalJsonRenderer.php#L159

I am using current version 0.9.11

Extra array element when resources are added without URI

Hello,

I have this following code,

$locations = array (
                0 =>
                    array (
                        'id' => 1,
                        'location' => 'Gainesville',
                        'location_short_code' => 'GA',
                    ),
                1 =>
                    array (
                        'id' => 2,
                        'location' => 'JacksonVile',
                        'location_short_code' => 'JV',
                    ),
                2 =>
                    array (
                        'id' => 3,
                        'location' => 'Tampa',
                        'location_short_code' => 'TM',
                    ),
                3 =>
                    array (
                        'id' => 4,
                        'location' => 'North Pinellas',
                        'location_short_code' => 'NP',
                    ),
                4 =>
                    array (
                        'id' => 5,
                        'location' => 'South Orlando',
                        'location_short_code' => 'SO',
                    ),
                5 =>
                    array (
                        'id' => 6,
                        'location' => 'West Cobbga',
                        'location_short_code' => 'NE',
                    ),
                6 =>
                    array (
                        'id' => 7,
                        'location' => 'Test Location',
                        'location_short_code' => 'TL',
                    ),
            );

            $hal = new Hal(null, [
                'response' => [
                    'kind' => 'locations'
                ]
            ]);

            $resource = new Hal(null, $locations);
            $hal->addResource('locations', $resource);
            $a = $hal->asJson();
            dump($a);

I am expecting a JSON like below:

{
    "response": {
        "kind": "locations"
    },
    "_embedded": {
        "locations": [
            {
                "id": 1,
                "location": "Gainesville",
                "location_short_code": "GA"
            },
            {
                "id": 2,
                "location": "JacksonVile",
                "location_short_code": "JV"
            },
            {
                "id": 3,
                "location": "Tampa",
                "location_short_code": "TM"
            },
            {
                "id": 4,
                "location": "North Pinellas",
                "location_short_code": "NP"
            },
            {
                "id": 5,
                "location": "South Orlando",
                "location_short_code": "SO"
            },
            {
                "id": 6,
                "location": "West Cobbga",
                "location_short_code": "NE"
            },
            {
                "id": 7,
                "location": "Test Location",
                "location_short_code": "TL"
            }
        ]
    }
}

Instead I am getting,

{
    "response": {
        "kind": "locations"
    },
    "_embedded": {
        "locations": [
            [
                {
                    "id": 1,
                    "location": "Gainesville",
                    "location_short_code": "GA"
                },
                {
                    "id": 2,
                    "location": "JacksonVile",
                    "location_short_code": "JV"
                },
                {
                    "id": 3,
                    "location": "Tampa",
                    "location_short_code": "TM"
                },
                {
                    "id": 4,
                    "location": "North Pinellas",
                    "location_short_code": "NP"
                },
                {
                    "id": 5,
                    "location": "South Orlando",
                    "location_short_code": "SO"
                },
                {
                    "id": 6,
                    "location": "West Cobbga",
                    "location_short_code": "NE"
                },
                {
                    "id": 7,
                    "location": "Test Location",
                    "location_short_code": "TL"
                }
            ]
        ]
    }
}

I believe, this is happening in the absence of 'URI' parameter. Whenever I am adding URI to some string, the extra array element goes. Based on HAL resource representation, I don't think we should expect an extra array?

Please let me know your thoughts.

Regards,
Aneek

Cutting a new tag for PHP 7.4 support

Hello! Some of the latest commits improve PHP 7.4 support but haven't made it into a release: would it be possible to get a new tag cut for them?

Unable to add data to root object

Classes need to be consolidated as data should be able to be added at any level (including the root object) rather than only within embedded objects.

setResource is broken when the resource contains only 1 data element

$resource = new Nocarrier\Hal('/post/1',[/*some data here*/]);
$author = new Nocarrier\Hal('/user/1', [
    'name' => 'John',
]);
$resource->setResource('user', $author);

$resource->asJson();

Will fail with Notice: Undefined offset: 0 here:

if (count($embedded) === 1 && !in_array($rel, $resource->getArrayResourceRels())) {
    $embedded = $embedded[0];
}

This fails because:

  • $author only contains 1 field, which triggers the condition count($embedded) === 1
  • setResource does not register refs in arrayResourceRels, which triggers the condition !in_array($rel, $resource->getArrayResourceRels())

I don't fully understand the logic here, so I'm not sure what the solution is, but it was extremely confusing to find out that having a resource with a single field was not supported. I had to add a second (unnecessary) field to make it work.

"value" is reserved as a magic key when decoding XML

This appears to be by design as there is a test for this but it caused my some serious pain.

When serialising an array to XML:

array('param' => array('name' => '{something that would be invalid as an XML tag name}', 'value' => 'hello'));

The following is produced:

<param>hello</param>

Not:

<param>
    <name>{something that would be invalid as an XML tag name}</name>
    <value>hello</value>
</param>

Which is what I expected.

I can sort of see why this has been done and I expect that in most circumstances a standard key value pair would be sufficient. Only when one uses a key name that would be considered invalid for an XML node name is a problem introduced, which in my case was the problem. I have overcome the issue by using "data-name", "data-value"... not the best solution but I suppose it works and provides what I am after.

Can we have this as an option or something? I can fork and add this option if you like.

Let me know
Chris

P.S. Hal is awesome and I thank you for it.

Provide built-in support for UriTemplate-based links

The point is that current structure of adding link with UriTemplate doesn't allow (in a clear way) to encapsulate common logics. For example, I want to add links with paging. At the moment, I have this:

$resource->addLink('orders', 'http://example.com/orders{?limit}', array('templated' => true));

URL and additional param (tempalted in this case) are kind of split, so if I want to put it into separate method I will have to do it in the following way:

addLinkWithUriTemplate($resource, 'orders', 'http://example.com/orders{?limit}');

where addLinkWithUriTemplate will do all the logics. But it looks really ugly.

I was thinking, maybe addLink can check whether the url contains {*} and then adds the templated automatically?

_embedded for empty collections

I am using the _embedded property to return a collection of things. This is done using addResource('mycollection', $hal).

Sometimes, due to filtering or some other reason, the collection is empty. Even then, I would like to have an empty collection like this:

"_embedded":{
   "collection": []
}

Is there a way for the library to do this? If now, can this be added to the library (perhaps it might be as simple as changing the signature of addResource() to addResource($rel, $hal = null)?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.