HATEOAS REST 웹 서비스에 대한 표현 구현을 지원하는 PHP 라이브러리입니다.
Hateoas를 설치하는 데 권장되는 방법은 Composer를 이용하는 것입니다. 다음 명령을 실행하여 willdurand/hateoas
패키지가 필요합니다:
composer require willdurand/hateoas
이렇게 하면 최신 안정 버전이 해결됩니다.
그렇지 않은 경우 라이브러리를 설치하고 자동 로더를 직접 설정하십시오.
구성에 주석을 사용하려면 doctrine/annotations
패키지를 설치해야 합니다:
composer require doctrine/annotations
앱이 PHP 8.1 이상을 사용하는 경우 기본 PHP 속성을 사용하는 것이 좋습니다. 이 경우 Doctrine 패키지를 설치할 필요가 없습니다.
이를 위한 번들이 있습니다! BazingaHateoasBundle을 설치하고 즐겨보세요!
중요한:
1.0
버전을 사용하는 경우 이 설명서 페이지로 이동할 수 있습니다.
2.0
버전을 사용하는 경우 이 설명서 페이지로 이동할 수 있습니다.다음 문서는 Hateoas 3.0 이상을 위해 작성되었습니다.
Hateoas는 Serializer 라이브러리를 활용하여 HATEOAS REST 웹 서비스를 구축하는 좋은 방법을 제공합니다. HATEOAS는 Hypermedia as the Engine of Application State를 나타내며 표현 (예: API 응답)에 하이퍼미디어 링크를 추가합니다. HATEOAS는 리소스에 대한 작업 검색 가능성에 관한 것입니다.
예를 들어, 다음과 같이 단일 사용자 의 표현을 반환하는 User API가 있다고 가정해 보겠습니다.
{
"user" : {
"id" : 123 ,
"first_name" : " John " ,
"last_name" : " Doe "
}
}
API 소비자에게 이 특정 사용자에 대한 데이터를 검색하는 방법을 알려주려면 이 표현에 대한 첫 번째 링크를 추가해야 합니다. 이 특정 사용자에 대한 URI이므로 self
으로 호출하겠습니다.
{
"user" : {
"id" : 123 ,
"first_name" : " John " ,
"last_name" : " Doe " ,
"_links" : {
"self" : { "href" : " http://example.com/api/users/123 " }
}
}
}
이제 Hateoas를 파헤쳐 보겠습니다.
Hateoas 용어에서 링크는 리소스에 추가된 관계 로 간주됩니다. 관계가 포함된 리소스 도 참조한다는 점은 언급할 가치가 있지만 이 주제는 포함 리소스 섹션에서 다룰 것입니다.
링크는 name
(예: self
)으로 식별되고 href
매개변수가 있는 관계입니다.
use JMS Serializer Annotation as Serializer ;
use Hateoas Configuration Annotation as Hateoas ;
/ * *
* @ Serializer XmlRoot ( "user" )
*
* @ Hateoas Relation ( "self" , href = "expr('/api/users/' ~ object.getId())" )
* /
class User
{
/ * * @ Serializer XmlAttribute * /
private $ id ;
private $ firstName ;
private $ lastName ;
public function getId () {}
}
use JMS Serializer Annotation as Serializer ;
use Hateoas Configuration Annotation as Hateoas ;
#[ Serializer XmlRoot( ' user ' )]
#[ Hateoas Relation( ' self ' , href: " expr('/api/users/' ~ object.getId()) " )]
class User
{
#[ Serializer XmlAttribute]
private $ id ;
private $ firstName ;
private $ lastName ;
public function getId () {}
}
위의 예에서는 href
매개변수로 인해 링크인 self
관계를 구성합니다. 언뜻 이상해 보일 수 있는 그 값은 표현 언어 섹션에서 광범위하게 다루어질 것입니다. 이 특수 값은 URI를 생성하는 데 사용됩니다.
이 섹션에서는 Hateoas를 구성하는 데 주석/속성을 사용합니다. XML 및 YAML 형식도 지원됩니다. 원한다면 일반 PHP도 사용할 수 있습니다.
중요: Serializer와 Hateoas를 모두 동일한 방식으로 구성해야 합니다. 예를 들어 Serializer 구성에 YAML을 사용하는 경우 Hateoas 구성에 YAML을 사용하세요.
HATEOAS를 시도하는 가장 쉬운 방법은 HateoasBuilder
를 사용하는 것입니다. 빌더에는 Hateoas 직렬 변환기를 구성하는 다양한 방법이 있지만 지금은 이에 대해 자세히 알아보지 않겠습니다(HateoasBuilder 참조). 모든 것이 기본적으로 잘 작동합니다.
use Hateoas HateoasBuilder ;
$ hateoas = HateoasBuilder:: create ()-> build ();
$ user = new User ( 42 , ' Adrien ' , ' Brault ' );
$ json = $ hateoas -> serialize ( $ user , ' json ' );
$ xml = $ hateoas -> serialize ( $ user , ' xml ' );
$hateoas
객체는 Serializer 라이브러리에서 오는 JMSSerializerSerializerInterface
인스턴스입니다. Hateoas에는 자체 직렬 변환기가 제공되지 않으며 JMS 직렬 변환기에 연결됩니다.
기본적으로 Hateoas는 JSON 직렬화를 위해 HAL(Hypertext Application Language)을 사용합니다. 이는 응답의 구조를 지정합니다(예: "링크"가 _links
키 아래에 있어야 함).
{
"id" : 42 ,
"first_name" : " Adrien " ,
"last_name" : " Brault " ,
"_links" : {
"self" : {
"href" : " /api/users/42 "
}
}
}
XML의 경우 기본적으로 Atom 링크가 사용됩니다.
< user id = " 42 " >
< first_name > <![CDATA[ Adrien ]]> </ first_name >
< last_name > <![CDATA[ Brault ]]> </ last_name >
< link rel = " self " href = " /api/users/42 " />
</ user >
이러한 형식은 사용 가능한 유일한 형식이 아니라 기본 형식 이라는 점을 언급할 가치가 있습니다. 다양한 직렬 변환기를 통해 다양한 형식을 사용할 수 있으며 자신만의 형식을 추가할 수도 있습니다.
이제 링크를 추가하는 방법을 알았으니 삽입된 리소스를 추가하는 방법을 살펴보겠습니다.
때로는 관련 리소스를 연결하는 것보다 포함하는 것이 클라이언트가 해당 리소스를 가져오기 위해 추가 요청을 하지 않아도 되므로 더 효율적입니다.
포함된 리소스 는 embedded
매개변수로 표시되는 데이터를 포함하는 명명된 관계 입니다.
use JMS Serializer Annotation as Serializer ;
use Hateoas Configuration Annotation as Hateoas ;
/ * *
* ...
*
* @ Hateoas Relation (
* "manager" ,
* href = "expr('/api/users/' ~ object.getManager().getId())" ,
* embedded = "expr(object.getManager())" ,
* exclusion = @ Hateoas Exclusion ( excludeIf = "expr(object.getManager() === null)" )
* )
* /
class User
{
...
/ * * @ Serializer Exclude * /
private $ manager ;
}
use JMS Serializer Annotation as Serializer ;
use Hateoas Configuration Annotation as Hateoas ;
#[ Hateoas Relation(
' manager ' ,
href: " expr('/api/users/' ~ object.getManager().getId()) " ,
embedded: " expr(object.getManager()) " ,
exclusion: new Hateoas Exclusion (excludeif: " expr(object.getManager() === null) " ),
)]
class User
{
...
#[ Serializer Exclude]
private $ manager ;
}
참고: 직렬화에서 관리자 속성을 제외해야 합니다. 그렇지 않으면 직렬 변환기와 Hateoas가 모두 이를 직렬화합니다. 또한 관리자가 null
인 경우 관리자 관계를 제외해야 합니다. 그렇지 않으면 href
링크를 만들 때( null
에서 getId()
호출) 오류가 발생하기 때문입니다.
팁: 관리자 속성이 이미 _self
링크가 있는 개체인 경우 여기에서 반복하는 대신 href
에 해당 값을 다시 사용할 수 있습니다. LinkHelper를 참조하세요.
$ hateoas = HateoasBuilder:: create ()-> build ();
$ user = new User ( 42 , ' Adrien ' , ' Brault ' , new User ( 23 , ' Will ' , ' Durand ' ));
$ json = $ hateoas -> serialize ( $ user , ' json ' );
$ xml = $ hateoas -> serialize ( $ user , ' xml ' );
json
의 경우 HAL 표현은 다음과 같은 포함된 관계를 _embedded
키 내에 배치합니다.
{
"id" : 42 ,
"first_name" : " Adrien " ,
"last_name" : " Brault " ,
"_links" : {
"self" : {
"href" : " /api/users/42 "
},
"manager" : {
"href" : " /api/users/23 "
}
},
"_embedded" : {
"manager" : {
"id" : 23 ,
"first_name" : " Will " ,
"last_name" : " Durand " ,
"_links" : {
"self" : {
"href" : " /api/users/23 "
}
}
}
}
}
XML에서 embedded
관계를 직렬화하면 새 요소가 생성됩니다.
< user id = " 42 " >
< first_name > <![CDATA[ Adrien ]]> </ first_name >
< last_name > <![CDATA[ Brault ]]> </ last_name >
< link rel = " self " href = " /api/users/42 " />
< link rel = " manager " href = " /api/users/23 " />
< manager rel = " manager " id = " 23 " >
< first_name > <![CDATA[ Will ]]> </ first_name >
< last_name > <![CDATA[ Durand ]]> </ last_name >
< link rel = " self " href = " /api/users/23 " />
</ manager >
</ user >
포함된 리소스의 태그 이름은 Serializer 구성에서 나오는 @XmlRoot
주석(YAML의 xml_root_name
, XML의 xml-root-name
)에서 유추됩니다.
라이브러리는 일반적인 작업에 도움이 되는 HateoasRepresentation*
네임스페이스에 여러 클래스를 제공합니다. 이는 라이브러리의 주석으로 구성된 간단한 클래스입니다.
PaginatedRepresentation
, OffsetRepresentation
및 CollectionRepresentation
클래스는 아마도 가장 흥미로운 클래스일 것입니다. 이는 리소스가 실제로 리소스 모음일 때 유용합니다(예: /users
는 사용자 모음). 이는 컬렉션을 표현하고 페이지 매김 및 제한을 추가하는 데 도움이 됩니다.
use Hateoas Representation PaginatedRepresentation ;
use Hateoas Representation CollectionRepresentation ;
$ paginatedCollection = new PaginatedRepresentation (
new CollectionRepresentation ( array ( $ user1 , $ user2 , ...)),
' user_list ' , // route
array (), // route parameters
1 , // page number
20 , // limit
4 , // total pages
' page ' , // page route parameter name , optional , defaults to 'page'
' limit ' , // limit route parameter name , optional , defaults to 'limit'
false , // generate relative URIs , optional , defaults to `false`
75 // total collection size , optional , defaults to `null`
);
$ json = $ hateoas -> serialize ( $ paginatedCollection , ' json ' );
$ xml = $ hateoas -> serialize ( $ paginatedCollection , ' xml ' );
CollectionRepresentation
포함된 컬렉션의 기본 표현을 제공합니다.
PaginatedRepresentation
은 self
, first
및 가능한 경우 last
, next
및 previous
링크를 추가하도록 설계되었습니다.
OffsetRepresentation
은 PaginatedRepresentation
과 동일하게 작동하지만 페이지 매김이 offset
, limit
및 total
로 표현될 때 유용합니다.
RouteAwareRepresentation
주어진 경로를 기반으로 self
관계를 추가합니다.
PaginatedRepresentation
및 RouteAwareRepresentation
모두에서 absolute
매개변수를 true
로 설정하여 절대 URI를 생성할 수 있습니다.
Hateoas 라이브러리는 Pagerfanta 인스턴스에서 PaginatedRepresentation
쉽게 빌드할 수 있는 PagerfantaFactory
도 제공합니다. Pagerfanta 라이브러리를 사용하는 경우 컬렉션 표현을 생성하는 더 쉬운 방법입니다.
use Hateoas Configuration Route ;
use Hateoas Representation Factory PagerfantaFactory ;
$ pagerfantaFactory = new PagerfantaFactory (); // you can pass the page ,
// and limit parameters name
$ paginatedCollection = $ pagerfantaFactory -> createRepresentation (
$ pager ,
new Route ( ' user_list ' , array ())
);
$ json = $ hateoas -> serialize ( $ paginatedCollection , ' json ' );
$ xml = $ hateoas -> serialize ( $ paginatedCollection , ' xml ' );
다음 JSON 콘텐츠를 얻게 됩니다.
{
"page" : 1 ,
"limit" : 10 ,
"pages" : 1 ,
"_links" : {
"self" : {
"href" : " /api/users?page=1&limit=10 "
},
"first" : {
"href" : " /api/users?page=1&limit=10 "
},
"last" : {
"href" : " /api/users?page=1&limit=10 "
}
},
"_embedded" : {
"items" : [
{ "id" : 123 },
{ "id" : 456 }
]
}
}
다음 XML 콘텐츠는 다음과 같습니다.
<? xml version = " 1.0 " encoding = " UTF-8 " ?>
< collection page = " 1 " limit = " 10 " pages = " 1 " >
< entry id = " 123 " ></ entry >
< entry id = " 456 " ></ entry >
< link rel = " self " href = " /api/users?page=1 & limit=10 " />
< link rel = " first " href = " /api/users?page=1 & limit=10 " />
< link rel = " last " href = " /api/users?page=1 & limit=10 " />
</ collection >
인라인된 CollectionRepresentation
사용자 정의하려면 createRepresentation()
메서드의 세 번째 인수로 하나를 전달하세요.
use Hateoas Representation Factory PagerfantaFactory ;
$ pagerfantaFactory = new PagerfantaFactory (); // you can pass the page and limit parameters name
$ paginatedCollection = $ pagerfantaFactory -> createRepresentation (
$ pager ,
new Route ( ' user_list ' , array ()),
new CollectionRepresentation ( $ pager -> getCurrentPageResults ())
);
$ json = $ hateoas -> serialize ( $ paginatedCollection , ' json ' );
$ xml = $ hateoas -> serialize ( $ paginatedCollection , ' xml ' );
컬렉션의 xml 루트 이름을 변경하려면 xml 루트가 구성된 새 클래스를 만들고 인라인 메커니즘을 사용하세요.
use JMS Serializer Annotation as Serializer ;
/ * *
* @ Serializer XmlRoot ( "users" )
* /
class UsersRepresentation
{
/ * *
* @ Serializer Inline
* /
private $ inline ;
public function __construct ( $ inline )
{
$ this -> inline = $ inline ;
}
}
$ paginatedCollection = . . . ;
$ paginatedCollection = new UsersRepresentation ( $ paginatedCollection );
use JMS Serializer Annotation as Serializer ;
#[ Serializer XmlRoot( ' users ' )]
class UsersRepresentation
{
#[ Serializer Inline]
private $ inline ;
public function __construct ( $ inline )
{
$ this -> inline = $ inline ;
}
}
$ paginatedCollection = . . . ;
$ paginatedCollection = new UsersRepresentation ( $ paginatedCollection );
이전 섹션에서 언급했듯이 표현은 일반적인 작업을 돕기 위해 라이브러리의 주석으로 구성된 클래스입니다. 컬렉션 표현은 컬렉션 처리에 설명되어 있습니다.
VndErrorRepresentation
을 사용하면 vnd.error
사양에 따른 오류 응답을 설명할 수 있습니다.
$ error = new VndErrorRepresentation (
' Validation failed ' ,
42 ,
' http://.../ ' ,
' http://.../ '
);
이러한 표현을 XML 및 JSON으로 직렬화하면 다음과 같은 출력이 제공됩니다.
<? xml version = " 1.0 " encoding = " UTF-8 " ?>
< resource logref = " 42 " >
< message > <![CDATA[ Validation failed ]]> </ message >
< link rel = " help " href = " http://.../ " />
< link rel = " describes " href = " http://.../ " />
</ resource >
{
"message" : " Validation failed " ,
"logref" : 42 ,
"_links" : {
"help" : {
"href" : " http://.../ "
},
"describes" : {
"href" : " http://.../ "
}
}
}
힌트: VndErrorRepresentation
클래스를 확장하는 자체 오류 클래스를 만드는 것이 좋습니다.
Hateoas는 강력한 Symfony ExpressionLanguage 구성 요소를 사용하여 삽입할 링크, ID 또는 개체와 같은 값을 검색합니다.
값(예: 주석 또는 YAML의 Relation href
)을 채울 때마다 하드코딩된 값 이나 표현식을 전달할 수 있습니다. 표현 언어를 사용하려면 expr()
표기법을 사용해야 합니다.
use Hateoas Configuration Annotation as Hateoas ;
/ * *
* @ Hateoas Relation ( "self" , href = "expr('/api/users/' ~ object.getId())" )
* /
use Hateoas Configuration Annotation as Hateoas ;
#[ Hateoas Relation( ' self ' , href: " expr('/api/users/' ~ object.getId()) " )]
공식 문서인 The Expression Syntax를 읽어서 Expression Syntax에 대해 자세히 알아볼 수 있습니다.
기본적으로 object
라는 특수 변수는 각 표현식에서 사용할 수 있으며 현재 개체를 나타냅니다.
expr(object.getId())
우리는 이러한 변수를 컨텍스트 변수라고 부릅니다.
사용자 고유의 컨텍스트 변수를 표현식 평가기에 추가하여 표현식 언어 컨텍스트에 추가할 수 있습니다.
HateoasBuilder
사용하여 setExpressionContextVariable()
메서드를 호출하여 새 컨텍스트 변수를 추가합니다.
use Hateoas HateoasBuilder ;
$ hateoas = HateoasBuilder:: create ()
-> setExpressionContextVariable ( ' foo ' , new Foo ())
-> build ();
이제 foo
변수를 사용할 수 있습니다:
expr(foo !== null)
표현식 언어에 함수를 추가하는 방법에 대한 자세한 내용은 https://symfony.com/doc/current/comComponents/expression_언어/extending.html을 참조하세요.
표현 언어를 사용하여 관계 링크( href
키)를 정의할 수 있으므로 기본적으로 많은 작업을 수행할 수 있습니다. 그러나 프레임워크를 사용하는 경우 경로를 사용하여 링크를 구축할 가능성이 있습니다.
먼저 빌더에서 UrlGenerator
를 구성해야 합니다. HateoasUrlGeneratorUrlGeneratorInterface
구현하거나 HateoasUrlGeneratorCallableUrlGenerator
사용할 수 있습니다.
use Hateoas UrlGenerator CallableUrlGenerator ;
$ hateoas = HateoasBuilder:: create ()
-> setUrlGenerator (
null , // By default all links uses the generator configured with the null name
new CallableUrlGenerator ( function ( $ route , array $ parameters , $ absolute ) use ( $ myFramework ) {
return $ myFramework -> generateTheUrl ( $ route , $ parameters , $ absolute );
})
)
-> build ()
;
그러면 @Route 주석을 사용할 수 있습니다.
use Hateoas Configuration Annotation as Hateoas ;
/ * *
* @ Hateoas Relation (
* "self" ,
* href = @ Hateoas Route (
* "user_get" ,
* parameters = {
* "id" = "expr(object.getId())"
* }
* )
* )
* /
class User
use Hateoas Configuration Annotation as Hateoas ;
#[ Hateoas Relation(
' self ' ,
href: new Hateoas Route (
' user_get ' ,
parameters: [
' id ' => ' expr(object.getId()) ' ,
],
)
)]
class User
{
"id" : 42 ,
"first_name" : " Adrien " ,
"last_name" : " Brault " ,
"_links" : {
"self" : {
"href" : " /api/users/42 "
}
}
}
라이브러리에는 SymfonyUrlGenerator
가 함께 제공됩니다. 예를 들어 Silex에서 사용하려면 다음과 같이 하세요.
use Hateoas UrlGenerator SymfonyUrlGenerator ;
$ hateoas = HateoasBuilder:: create ()
-> setUrlGenerator ( null , new SymfonyUrlGenerator ( $ app [ ' url_generator ' ]))
-> build ()
;
Hateoas는 API 구축 프로세스를 쉽게 하기 위한 도우미 세트를 제공합니다.
LinkHelper
클래스는 주어진 관계 이름에 대해 모든 개체의 href 값을 가져올 수 있는 getLinkHref($object, $rel, $absolute = false)
메서드를 제공합니다. 모든 링크 관계에서 URI(절대 또는 상대)를 생성할 수 있습니다.
$ user = new User ( 123 , ' William ' , ' Durand ' );
$ linkHelper -> getLinkHref ( $ user , ' self ' );
// / api / users / 123
$ linkHelper -> getLinkHref ( $ user , ' self ' , true );
// http : // example . com / api / users / 123
link
기능 위 기능은 link(object, rel, absolute)
함수를 통해 표현식(표현 언어 참조)에서도 사용할 수 있습니다.
/ * *
* @ Hateoas Relation (
* "self" ,
* href = @ Hateoas Route ( "post_get" , parameters = { "id" = "expr(object.getId())" })
* )
* /
class Post {}
/ * *
* @ Hateoas Relation (
* "self" ,
* href = @ Hateoas Route ( "user_get" , parameters = { "id" = "expr(object.getId())" })
* )
* @ Hateoas Relation (
* "post" ,
* href = "expr(link(object.getPost(), 'self', true))"
* )
* @ Hateoas Relation (
* "relative" ,
* href = "expr(link(object.getRelativePost(), 'self'))"
* )
* /
class User
{
...
public function getPost ()
{
return new Post ( 456 );
}
public function getRelativePost ()
{
return new Post ( 789 );
}
}
#[ Hateoas Relation(
' self ' ,
href: new Hateoas Route (
' post_get ' ,
parameters: [
' id ' => ' expr(object.getId()) ' ,
],
),
)]
class Post {}
#[ Hateoas Relation(
' self ' ,
href: new Hateoas Route (
' user_get ' ,
parameters: [
' id ' => ' expr(object.getId()) ' ,
],
),
)]
#[ Hateoas Relation(
' post ' ,
href: " expr(link(object.getPost(), 'self', true)) " ,
)]
#[ Hateoas Relation(
' relative ' ,
href: " expr(link(object.getRelativePost(), 'self')) " ,
)]
class User
{
...
public function getPost ()
{
return new Post ( 456 );
}
public function getRelativePost ()
{
return new Post ( 789 );
}
}
post
및 relative
관계에 대한 href
표현식과 다음 JSON 콘텐츠의 해당 값에 주의하세요.
{
"user" : {
"id" : 123 ,
"first_name" : " William " ,
"last_name" : " Durand " ,
"_links" : {
"self" : { "href" : " http://example.com/api/users/123 " },
"post" : { "href" : " http://example.com/api/posts/456 " },
"relative" : { "href" : " /api/posts/789 " }
}
}
}
getLinkHref()
메서드와 link
함수 모두에서 세 번째 인수를 사용하여 절대 또는 상대 URI를 원하는지 여부를 강제 할 수 있다는 점은 언급할 가치가 있습니다.
중요: 기본적으로 모든 URI는 상대 URI이며 구성에서 절대로 정의된 URI도 마찬가지입니다.
$ linkHelper -> getLinkHref ( $ user , ' post ' );
// / api / posts / 456
$ linkHelper -> getLinkHref ( $ user , ' post ' , true );
// http : // example . com / api / posts / 456
$ linkHelper -> getLinkHref ( $ user , ' relative ' );
// / api / posts / 789
$ linkHelper -> getLinkHref ( $ user , ' relative ' , true );
// http : // example . com / api / posts / 789
Hateoas는 또한 Twig 확장 세트를 제공합니다.
LinkExtension
하면 LinkHelper를 Twig 템플릿에 사용할 수 있으므로 HTML 템플릿에서 링크를 생성할 수 있습니다.
이 확장은 link_href
Twig 함수를 통해 getLinkHref()
도우미의 메서드를 노출합니다.
{{ link_href(user, 'self') }}
{# will generate: /users/123 #}
{{ link_href(will, 'self', false) }}
{# will generate: /users/123 #}
{{ link_href(will, 'self', true) }}
{# will generate: http://example.com/users/123 #}
Hateoas는 일련의 직렬 변환기를 제공합니다. 각 직렬 변환기를 사용하면 HAL 또는 Atom 링크와 같은 특정 형식 에 따라 XML 또는 JSON 콘텐츠를 생성할 수 있습니다.
JsonHalSerializer
사용하면 JSON에서 HAL 호환 관계를 생성할 수 있습니다. Hateoas의 기본 JSON 직렬 변환기입니다.
HAL은 리소스 개체에 _links
라는 예약된 속성이 있다는 규칙을 통해 연결 기능을 제공합니다. 이 속성은 링크를 포함하는 개체입니다. 이러한 링크는 링크 관계에 따라 키가 지정됩니다.
HAL은 또한 리소스에 _embedded
라는 또 다른 예약된 속성이 있을 수 있다는 또 다른 규칙을 설명합니다. 이 속성은 포함된 리소스가 관계 이름으로 입력된다는 점에서 _links
와 유사합니다. 주요 차이점은 값이 링크가 아니라 리소스 개체라는 것입니다.
{
"message" : " Hello, World! " ,
"_links" : {
"self" : {
"href" : " /notes/0 "
}
},
"_embedded" : {
"associated_events" : [
{
"name" : " SymfonyCon " ,
"date" : " 2013-12-12T00:00:00+0100 "
}
]
}
}
XmlSerializer
사용하면 XML 문서에 Atom 링크를 생성할 수 있습니다. 이는 기본 XML 직렬 변환기입니다.
<? xml version = " 1.0 " encoding = " UTF-8 " ?>
< note >
< message > <![CDATA[ Hello, World! ]]> </ message >
< link rel = " self " href = " /notes/0 " />
< events rel = " associated_events " >
< event >
< name > <![CDATA[ SymfonyCon ]]> </ name >
< date > <![CDATA[ 2013-12-12T00:00:00+0100 ]]> </ date >
</ event >
</ events >
</ note >
XmlHalSerializer
사용하면 XML에서 HAL 호환 관계를 생성할 수 있습니다.
XML의 HAL은 link
태그와 resource
태그를 설명한다는 점에서 JSON의 HAL과 유사합니다.
참고: self
관계는 실제로 link
태그가 아닌 기본 리소스의 속성이 됩니다. 다른 링크는 link
태그로 생성됩니다.
<? xml version = " 1.0 " encoding = " UTF-8 " ?>
< note href = " /notes/0 " >
< message > <![CDATA[ Hello, World! ]]> </ message >
< resource rel = " associated_events " >
< name > <![CDATA[ SymfonyCon ]]> </ name >
< date > <![CDATA[ 2013-12-12T00:00:00+0100 ]]> </ date >
</ resource >
</ note >
링크 와 포함된 관계를 직렬화하는 두 가지 방법을 설명하는 SerializerInterface
구현해야 합니다.
HateoasBuilder
클래스는 강력하고 유연한 API 덕분에 Hateoas를 쉽게 구성하는 데 사용됩니다.
use Hateoas HateoasBuilder ;
$ hateoas = HateoasBuilder:: create ()
-> setCacheDir ( ' /path/to/cache/dir ' )
-> setDebug ( $ trueOrFalse )
-> setDefaultXmlSerializer ()
. . .
-> build ();
아래의 모든 메서드는 현재 빌더를 반환하므로 이를 연결할 수 있습니다.
setXmlSerializer(SerializerInterface $xmlSerializer)
: 사용할 XML 직렬 변환기를 설정합니다. 기본값은 XmlSerializer
입니다.setDefaultXmlSerializer()
: 기본 XML 직렬 변환기( XmlSerializer
)를 설정합니다. setJsonSerializer(SerializerInterface $jsonSerializer)
: 사용할 JSON 직렬 변환기를 설정합니다. 기본값은 JsonHalSerializer
입니다.setDefaultJsonSerializer()
: 기본 JSON 직렬 변환기( JsonHalSerializer
)를 설정합니다. setUrlGenerator($name = null, UrlGeneratorInterface $urlGenerator)
: 새로운 이름의 URL 생성기를 추가합니다. $name
이 null
이면 URL 생성기가 기본 생성기가 됩니다. setExpressionContextVariable($name, $value)
: 새 표현식 컨텍스트 변수를 추가합니다.setExpressionLanguage(ExpressionLanguage $expressionLanguage)
; includeInterfaceMetadata($include)
: 인터페이스의 메타데이터를 포함할지 여부.setMetadataDirs(array $namespacePrefixToDirMap)
: 디렉터리에 대한 네임스페이스 접두사 맵을 설정합니다. 이 방법은 이전에 정의된 디렉터리를 재정의합니다.addMetadataDir($dir, $namespacePrefix = '')
: 직렬 변환기가 클래스 메타데이터를 찾을 디렉터리를 추가합니다.addMetadataDirs(array $namespacePrefixToDirMap)
: 디렉토리에 네임스페이스 접두사 맵을 추가합니다.replaceMetadataDir($dir, $namespacePrefix = '')
: addMetadataDir()
과 유사하지만 기존 항목을 재정의합니다.자세한 내용은 공식 Serializer 설명서를 읽어보세요.
setDebug($debug)
: 디버그 모드를 활성화 또는 비활성화합니다.setCacheDir($dir)
: 캐시 디렉토리를 설정합니다.직렬 변환기와 Hateoas 라이브러리는 모두 YML, XML 또는 주석과 같은 다양한 소스에서 개체에 대한 메타데이터를 수집합니다. 이 프로세스를 최대한 효율적으로 만들려면 Hateoas 라이브러리에서 이 정보를 캐시하도록 허용하는 것이 좋습니다. 그렇게 하려면 캐시 디렉터리를 구성합니다.
$ builder = Hateoas HateoasBuilder:: create ();
$ hateoas = $ builder
-> setCacheDir ( $ someWritableDir )
-> build ();
Hateoas는 여러 메타데이터 소스를 지원합니다. 기본적으로 Doctrine 주석(PHP < 8.1) 또는 기본 PHP 속성(PHP >= 8.1)을 사용하지만 메타데이터를 XML 또는 YAML 파일로 저장할 수도 있습니다. 후자의 경우 해당 파일이 있는 메타데이터 디렉터리를 구성해야 합니다.
$ hateoas = Hateoas HateoasBuilder:: create ()
-> addMetadataDir ( $ someDir )
-> build ();
Hateoas는 메타데이터 파일의 이름이 모든 가
.
. 클래스 이름이 VendorPackageFoo
인 경우 메타데이터 파일은 $someDir/Vendor.Package.Foo.(xml|yml)
에 있어야 합니다.
Hateoas를 사용하면 프레임워크가 구성 수준에서 확장 지점을 제공하여 클래스에 관계를 동적으로 추가할 수 있습니다. 이 기능은 Hateoas 위에 새 레이어를 생성하거나 각 클래스에 동일한 구성을 복사하는 대신 "글로벌" 관계를 추가하려는 사용자에게 유용할 수 있습니다.
이 메커니즘을 활용하려면 ConfigurationExtensionInterface
인터페이스를 구현해야 합니다.
use Hateoas Configuration Metadata ConfigurationExtensionInterface ;
use Hateoas Configuration Metadata ClassMetadataInterface ;
use Hateoas Configuration Relation ;
class AcmeFooConfigurationExtension implements ConfigurationExtensionInterface
{
/ * *
* {@ inheritDoc }
* /
public function decorate ( ClassMetadataInterface $ classMetadata ): void
{
if ( 0 === strpos ( ' AcmeFooModel ' , $ classMetadata -> getName ())) {
// Add a "root" relation to all classes in the `AcmeFooModel` namespace
$ classMetadata -> addRelation (
new Relation (
' root ' ,
' / '
)
);
}
}
}
$classMetadata->getRelations()
사용하여 Annotations, XML 또는 YAML에서 로드된 기존 관계에 액세스할 수 있습니다.
$classMetadata
에 관계가 있거나 여기에 관계를 추가하면 해당 관계가 캐시됩니다. 따라서 구성 파일(주석, XML 또는 YAML)을 읽는 경우 클래스 메타데이터에서 해당 파일을 참조해야 합니다.
$ classMetadata -> fileResources [] = $ file ;
<? xml version = " 1.0 " encoding = " UTF-8 " ?>
< serializer >
< class name = " AcmeDemoRepresentationUser " h : providers = " Class::getRelations expr(sevice('foo').getMyAdditionalRelations()) " xmlns : h = " https://github.com/willdurand/Hateoas " >
< h : relation rel = " self " >
< h : href uri = " http://acme.com/foo/1 " />
</ h : relation >
< h : relation rel = " friends " >
< h : href route = " user_friends " generator = " my_custom_generator " >
< h : parameter name = " id " value = " expr(object.getId()) " />
< h : parameter name = " page " value = " 1 " />
</ h : ref >
< h : embedded xml-element-name = " users " >
< h : content >expr(object.getFriends())</ h : content >
< h : exclusion ... />
</ h : embedded >
< h : exclusion groups = " Default, user_full " since-version = " 1.0 " until-version = " 2.2 " exclude-if = " expr(object.getFriends() === null) " />
</ h : relation >
</ class >
</ serializer >
자세한 내용은 hateoas.xsd
파일을 참조하세요.
AcmeDemoRepresentationUser :
relations :
-
rel : self
href : http://acme.com/foo/1
-
rel : friends
href :
route : user_friends
parameters :
id : expr(object.getId())
page : 1
generator : my_custom_generator
absolute : false
embedded :
content : expr(object.getFriends())
xmlElementName : users
exclusion : ...
exclusion :
groups : [Default, user_full]
since_version : 1.0
until_version : 2.2
exclude_if : expr(object.getFriends() === null)
relation_providers : [ "Class::getRelations", "expr(sevice('foo').getMyAdditionalRelations())" ]
이 주석은 클래스에 정의될 수 있습니다.
use Hateoas Configuration Annotation as Hateoas ;
/ * *
* @ Hateoas Relation (
* name = "self" ,
* href = "http://hello" ,
* embedded = "expr(object.getHello())" ,
* attributes = { "foo" = "bar" },
* exclusion = ...,
* )
* /
use Hateoas Configuration Annotation as Hateoas ;
#[ Hateoas Relation(
name: ' self ' ,
href: ' http://hello ' ,
embedded: ' expr(object.getHello()) ' ,
attributes: [ ' foo ' => ' bar ' ],
exclusion: ' ... ' ,
)]
재산 | 필수의 | 콘텐츠 | 표현 언어 |
---|---|---|---|
이름 | 예 | 끈 | 아니요 |
href | 임베디드가 설정되지 않은 경우 | 문자열 / @Route | 예 |
임베디드 | href가 설정되지 않은 경우 | 문자열 / @Embedded | 예 |
속성 | 아니요 | 정렬 | 가치관에 있어서는 그렇습니다 |
제외 | 아니요 | @제외 | 해당 없음 |
중요: attributes
링크 관계 에만 사용됩니다(즉, embedded
속성이 아닌 href
속성과 결합됨).
use Hateoas Configuration Annotation as Hateoas ;
/ * *
* @ Hateoas Relation (
* name = "self" ,
* href = @ Hateoas Route (
* "user_get" ,
* parameters = { "id" = "expr(object.getId())" },
* absolute = true ,
* generator = "my_custom_generator"
* )
* )
* /
use Hateoas Configuration Annotation as Hateoas ;
#[ Hateoas Relation(
name: ' self ' ,
href: new Hateoas Route (
' user_get ' ,
parameters: [ ' id ' = ' expr (object. getId ())'],
absolute: true ,
generator: ' my_custom_generator ' ,
),
)]
이 주석은 @Relation 주석의 href 속성에서 정의할 수 있습니다. URL 생성기를 구성한 경우 이를 통해 URL 생성기를 사용할 수 있습니다.
재산 | 필수의 | 콘텐츠 | 표현 언어 |
---|---|---|---|
이름 | 예 | 끈 | 아니요 |
매개변수 | 기본값은 array() | 배열/문자열 | 예(문자열 + 배열 값) |
순수한 | 기본값은 거짓 | 부울 / 문자열 | 예 |
발전기 | 아니요 | 문자열 / 널 | 아니요 |
use Hateoas Configuration Annotation as Hateoas ;
/ * *
* @ Hateoas Relation (
* name = "friends" ,
* embedded = @ Hateoas Embedded (
* "expr(object.getFriends())" ,
* exclusion = ...,
* xmlElementName = "users"
* )
* )
* /
use Hateoas Configuration Annotation as Hateoas ;
#[ Hateoas Relation(
name: ' friends ' ,
embedded: new Hateoas Embedded (
' expr(object.getFriends()) ' ,
exclusion: ' ... ' ,
xmlElementName: ' users ' ,
),
)]
이 주석은 @Relation 주석의 포함된 속성에 정의될 수 있습니다. 포함된 리소스에 대한 exclusion
또는 xmlElementName
옵션을 구성해야 하는 경우 유용합니다.
재산 | 필수의 | 콘텐츠 | 표현 언어 |
---|---|---|---|
콘텐츠 | 예 | 문자열/배열 | 예(문자열) |
제외 | 기본값은 array() | @제외 | 해당 없음 |
xml요소이름 | 기본값은 array() | 끈 | 아니요 |
이 주석은 @Relation 및 @Embedded 주석 모두의 제외 속성에서 정의할 수 있습니다.
재산 | 필수의 | 콘텐츠 | 표현 언어 |
---|---|---|---|
여러 떼 | 아니요 | 정렬 | 아니요 |
버전 이후 | 아니요 | 끈 | 아니요 |
버전까지 | 아니요 | 끈 | 아니요 |
최대 깊이 | 아니요 | 정수 | 아니요 |
제외하는 경우 | 아니요 | 문자열 / 부울 | 예 |
excludeIf
제외한 모든 값은 직렬 변환기를 사용하여 일반 속성에 직접 사용될 때와 동일한 방식으로 작동합니다.
excludeIf
부울을 기대하며 어떤 상황에서 다른 표현식이 실패할 때 유용합니다. 이 예에서 getManager
메소드가 null
인 경우 이를 제외하여 URL 생성이 실패하는 것을 방지해야 합니다.
use Hateoas Configuration Annotation as Hateoas ;
/ * *
* @ Hateoas Relation (
* "manager" ,
* href = @ Hateoas Route (
* "user_get" ,
* parameters = { "id" = "expr(object.getManager().getId())" }
* ),
* exclusion = @ Hateoas Exclusion ( excludeIf = "expr(null === object.getManager())" )
* )
* /
class User
{
public function getId () {}
/ * *
* @ return User | null
* /
public function getManager () {}
}
use Hateoas Configuration Annotation as Hateoas ;
#[ Hateoas Relation(
name: ' manager ' ,
href: new Hateoas Route (
' user_get ' ,
parameters: [ ' id ' => ' expr(object.getManager().getId()) ' ],
),
exclusion: new Hateoas Exclusion (excludeIf: ' expr(null === object.getManager()) ' )
)]
class User
{
public function getId () {}
public function getManager (): ? User {}
}
이 주석은 클래스에 정의될 수 있습니다. 다중 관계(링크)를 직렬화하려는 경우 유용합니다. 예를 들면:
{
"_links": {
"relation_name": [
{"href": "link1"},
{"href": "link2"},
{"href": "link3"}
]
}
}
재산 | 필수의 | 콘텐츠 | 표현 언어 |
---|---|---|---|
이름 | 예 | 끈 | 예 |
"이름"일 수 있습니다.
my_func
MyClass::getExtraRelations
expr(service('user.rel_provider').getExtraRelations())
표현식 언어를 사용한 예는 다음과 같습니다.
use Hateoas Configuration Annotation as Hateoas ;
/ * *
* @ Hateoas RelationProvider ( "expr(service('user.rel_provider').getExtraRelations())" )
* /
class User
{
...
}
use Hateoas Configuration Annotation as Hateoas ;
#[ Hateoas RelationProvider( " expr(service('user.rel_provider').getExtraRelations()) " )]
class User
{
...
}
UserRelPrvider
클래스는 다음과 같습니다.
use Hateoas Configuration Relation ;
use Hateoas Configuration Route ;
class UserRelPrvider
{
private $ evaluator ;
public function __construct ( CompilableExpressionEvaluatorInterface $ evaluator )
{
$ this -> evaluator = $ evaluator ;
}
/ * *
* @ return Relation []
* /
public function getExtraRelations (): array
{
// You need to return the relations
return array (
new Relation (
' self ' ,
new Route (
' foo_get ' ,
[ ' id ' => $ this -> evaluator -> parse ( ' object.getId() ' , [ ' object ' ])]
)
)
);
}
}
CompilableExpressionEvaluatorInterface
구현하는 $this->evaluator
나중에 사용하기 위해 캐시하고 저장할 수 있는 형식으로 표현 언어를 구문 분석하는 데 사용됩니다. 귀하의 관계에서 표현 언어가 필요하지 않은 경우 이 서비스는 필요하지 않습니다.
user.rel_provider
서비스는 다음과 같이 정의됩니다.
user.rel_provider :
class : UserRelPrvider
arguments :
- ' @jms_serializer.expression_evaluator '
이 경우 jms_serializer.expression_evaluator
CompilableExpressionEvaluatorInterface
구현하는 서비스입니다.
이 섹션은 이 라이브러리의 숨겨진 부분에 대한 문서를 제공하는 Hateoas 내부를 참조합니다. 이는 최종 사용자에게 항상 관련이 있는 것은 아니지만, 내부적으로 작동하는 방식을 배우는 데 관심이 있는 개발자나 사람들에게는 흥미로울 수 있습니다.
willdurand/hateoas
의미적 버전 관리를 따릅니다.
2013년 10월 현재 버전 1.x
및 0.x
는 더 이상 공식적으로 지원되지 않습니다( 1.x
출시되지 않았습니다).
버전 3.x
현재 주요 안정 버전입니다.
버전 2.x
보안 버그 수정 및 발생할 수 있는 주요 문제에 대해서만 유지 관리됩니다.
CONTRIBUTING 파일을 참조하세요.
Composer dev
종속성을 설치합니다.
php composer.phar install --dev
그런 다음 PHPUnit을 사용하여 테스트 스위트를 실행하십시오.
bin/phpunit
Hateoas는 MIT 라이센스에 따라 출시됩니다. 자세한 내용은 번들로 제공되는 LICENSE 파일을 참조하세요.