All Projects → nilportugues → php-hal

nilportugues / php-hal

Licence: MIT license
HAL+JSON & HAL+XML API transformer outputting valid (PSR-7) API Responses.

Programming Languages

PHP
23972 projects - #3 most used programming language

Projects that are alternatives of or similar to php-hal

Posthtml
PostHTML is a tool to transform HTML/XML with JS plugins
Stars: ✭ 2,737 (+9023.33%)
Mutual labels:  xml, transformer
php-serializer
Serialize PHP variables, including objects, in any format. Support to unserialize it too.
Stars: ✭ 47 (+56.67%)
Mutual labels:  transformer, hal
laravel5-hal-json
Laravel 5 HAL+JSON API Transformer Package
Stars: ✭ 15 (-50%)
Mutual labels:  transformer, hal
CSV2RDF
Streaming, transforming, SPARQL-based CSV to RDF converter. Apache license.
Stars: ✭ 48 (+60%)
Mutual labels:  transformer
text2keywords
Trained T5 and T5-large model for creating keywords from text
Stars: ✭ 53 (+76.67%)
Mutual labels:  transformer
Learning-Lab-C-Library
This library provides a set of basic functions for different type of deep learning (and other) algorithms in C.This deep learning library will be constantly updated
Stars: ✭ 20 (-33.33%)
Mutual labels:  transformer
blogspot-themes
Blogspot (Blogger) Themes Library
Stars: ✭ 32 (+6.67%)
Mutual labels:  xml
text-style-transfer-benchmark
Text style transfer benchmark
Stars: ✭ 56 (+86.67%)
Mutual labels:  transformer
Transformer tf2.0
Transfromer tensorflow2.0版本实现
Stars: ✭ 23 (-23.33%)
Mutual labels:  transformer
Walk-Transformer
From Random Walks to Transformer for Learning Node Embeddings (ECML-PKDD 2020) (In Pytorch and Tensorflow)
Stars: ✭ 26 (-13.33%)
Mutual labels:  transformer
FragmentVC
Any-to-any voice conversion by end-to-end extracting and fusing fine-grained voice fragments with attention
Stars: ✭ 134 (+346.67%)
Mutual labels:  transformer
RSTNet
RSTNet: Captioning with Adaptive Attention on Visual and Non-Visual Words (CVPR 2021)
Stars: ✭ 71 (+136.67%)
Mutual labels:  transformer
Restormer
[CVPR 2022--Oral] Restormer: Efficient Transformer for High-Resolution Image Restoration. SOTA for motion deblurring, image deraining, denoising (Gaussian/real data), and defocus deblurring.
Stars: ✭ 586 (+1853.33%)
Mutual labels:  transformer
deformer
[ACL 2020] DeFormer: Decomposing Pre-trained Transformers for Faster Question Answering
Stars: ✭ 111 (+270%)
Mutual labels:  transformer
TadTR
End-to-end Temporal Action Detection with Transformer. [Under review for a journal publication]
Stars: ✭ 55 (+83.33%)
Mutual labels:  transformer
transformer-models
Deep Learning Transformer models in MATLAB
Stars: ✭ 90 (+200%)
Mutual labels:  transformer
Embedding
Embedding模型代码和学习笔记总结
Stars: ✭ 25 (-16.67%)
Mutual labels:  transformer
golgotha
Contextualised Embeddings and Language Modelling using BERT and Friends using R
Stars: ✭ 39 (+30%)
Mutual labels:  transformer
enformer-pytorch
Implementation of Enformer, Deepmind's attention network for predicting gene expression, in Pytorch
Stars: ✭ 146 (+386.67%)
Mutual labels:  transformer
VectorDrawable2Svg
Converts Android VectorDrawable .xml files to .svg files
Stars: ✭ 50 (+66.67%)
Mutual labels:  xml

HAL+JSON & HAL+XML API Transformer

Build Status Scrutinizer Code Quality SensioLabsInsight Latest Stable Version Total Downloads License Donate

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

1. Installation

Use Composer to install the package:

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

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

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

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

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

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

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

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

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

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, PSR-2, PSR-4 and PSR-7.

If you notice compliance oversights, please send a patch via Pull Request.

Contribute

Contributions to the package are always welcome!

Support

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

Authors

License

The code base is licensed under the MIT license.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].