bigz / halapi
A PHP library to support implementing representations for HAL, REST over JSON web services
Requires
- php: >=5.6
- doctrine/annotations: ~1.0
- doctrine/common: ~2.0
- jms/serializer: ^1.0
- psr/http-message: ^1.0
- symfony/options-resolver: ^4.0
Requires (Dev)
- codeclimate/php-test-reporter: ^0.3.2
- doctrine/orm: ^2.5
- escapestudios/symfony2-coding-standard: ^3.4
- phpunit/phpunit: ~5.6
- squizlabs/php_codesniffer: ^3.3
This package is not auto-updated.
Last update: 2024-10-26 20:09:53 UTC
README
Given some conventions, displaying the HAL representation of any entity becomes very easy.
HAL is a json presentation format of the HATEOAS constraint, which is meant to add relations between objects.
It's whole specification is available here http://stateless.co/hal_specification.html
The work is in progress to make it framework agnostic but actually relies on you using symfony/http-foundation, which will change in a close future to use psr6 (while providing a bridge) For the object manager, you are free to choose the one you like, although only doctrine orm has been implemented at the mement. Relation findings relies also a lot on doctrine's ClassMetadata interface, that we should maybe abstract (you can still use your own implementaion)
Usage
composer req bigz/halapi
Symfony bundle
https://github.com/BigZ/HalapiBundle
Full fledged example using symfony (a good starting point for your api)
https://github.com/BigZ/promote-api
Example
use Halapi\AnnotationReader\AnnotationReaderInterface;
use Halapi\ObjectManager\ObjectManagerInterface;
use Halapi\UrlGenerator\UrlGeneratorInterface;
use Psr\Http\Message\ServerRequestInterface;
class EntityController()
{
/**
* You can provide your own implementations of those interfaces or use the provided ones.
*/
public function __construct(
UrlGeneratorInterface $router,
AnnotationReaderInterface $annotationReader,
ObjectManagerInterface $entityManager,
PagerInterface $pager
) {
$this->router = $router;
$this->annotationReader = $annotationReader;
$this->entityManager = $entityManager;
$this->pager = $pager;
}
/**
* Accessed by the /entities/{id} route
*/
public function getHalFormattedEntity(ServerRequestInterface $request, Entity $entity)
{
$linksRelation = new LinksRelation(
$this->annotationReader,
$this->router,
$this->entityManager,
);
$embeddedRelation = new EmbeddedRelation(
$this->annotationReader,
$request
);
$relationFactory = new RelationFactory([$linksRelation, $embeddedRelation]);
$builder = new HALAPIBuilder($relationFactory);
return $builder->gerSerializer()->serialize($entity);
}
/**
* Accessed by the /entities
*/
public function getHalFormattedCollection(ServerRequestInterface $request, $entityName)
{
$linksRelation = new LinksRelation(
$this->router,
$this->annotationReader,
$this->entityManager,
$request
);
$embeddedRelation = new EmbeddedRelation(
$this->router,
$this->annotationReader,
$this->entityManager,
$request
);
$relationFactory = new RelationFactory([$linksRelation, $embeddedRelation]);
$builder = new HALAPIBuilder($relationFactory);
$paginationFactory = new PaginationFactory(
$this->router,
$this->annotationReader,
$this->entityManager,
$this->pager
);
$paginatedRepresentation = $paginationFactory->getRepresentation($entityName);
return $builder->gerSerializer()->serialize($paginatedRepresentation);
}
}
Resources
List
Pagination
A list will give you a paginated ressource, HAL formatted.
/entities?limit=2&page=2
Filtering
You can filter out results on specific fields.
/entities?filter[id]=5&filteroperator[id]=>
Available operators are >
, <
, >=
, <=
, =
, !=
Default operator is =
Sorting
You can sort the result by any property
/entities?sort=-created,title
Entity
Creating new entities
POST /entities
{ "entity": { "name": "eminem", "slug": "eminem", "bio": "rapper from detroit", "labels": [1, 2] } }
will return
{ "id": 2, "name": "eminem", "slug": "eminem", "bio": "rapper from detroit", "_links": { "self": "/artists/2", "labels": [ "/labels/1", "/labels/2" ] } }
PUT & PATCH works the same way
Embedding
By default, relations are not embeded. You can change this behaviour by specifiying wich embedeed entities you need.
/entities/1?embed[]=gigs&embed[]=labels
To allow an relation to be embedded, you must add an @Embeddable
Annotation to your entity property.
use Halapi\Annotation\Embeddable;
/**
* The Embeddable annoation below is here if you want a custom route for your entity.
* By default, the generator would you "get_'entity's" which is the default behaviour
* of FOSRestBundle.
* @Embeddable("fetch_artists")
*/
class Artist
{
/**
* @var int
*
* @Expose
*/
private $id;
/**
* @var Labels[]
*
* @Embeddable
*/
protected $labels;
}
Roadmap to production readyness
- (MUST) Improve coverage
- (MUST) Implement sparse fieldset
- (MUST) Implement deep resource inclusion
- (SHOULD) support IN filter operator
- (SHOULD) Refactor using propertyinfo component
- (SHOULD) USE doctrine/reflection instead of doctrine/common
- (SHOULD) Be able to serialize to any other hateoas format. Not so easy with jms...