一個 PHP 函式庫,支援實作 HATEOAS REST Web 服務的表示。
建議的安裝 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 Web 服務的好方法。 HATEOAS 代表超媒體作為應用程式狀態引擎,並將超媒體連結新增至您的表示(即您的 API 回應)。 HATEOAS 是關於資源操作的可發現性。
例如,假設您有一個 User API,它會傳回單一使用者的表示,如下所示:
{
"user" : {
"id" : 123 ,
"first_name" : " John " ,
"last_name" : " Doe "
}
}
為了告訴您的 API 用戶如何檢索此特定使用者的數據,您必須將第一個連結新增至此表示形式,我們稱之為self
因為它是此特定使用者的 URI:
{
"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。例如,如果您使用 YAML 來設定 Serializer,請使用 YAML 來設定 Hateoas。
嘗試 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
物件是JMSSerializerSerializerInterface
的實例,來自 Serializer 函式庫。 Hateoas 沒有自帶序列化器,它連接到 JMS 序列化器。
預設情況下,Hateoas 使用超文本應用程式語言 (HAL) 進行 JSON 序列化。這指定了回應的結構(例如,「連結」應該位於_links
鍵下):
{
"id" : 42 ,
"first_name" : " Adrien " ,
"last_name" : " Brault " ,
"_links" : {
"self" : {
"href" : " /api/users/42 "
}
}
}
對於 XML,預設使用 Atom Links:
< 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 ;
}
注意:您需要從序列化中排除 manager 屬性,否則序列化程式和 Hateoas 都會對其進行序列化。當管理員為null
時,您也必須排除管理器關係,否則在建立href
連結(在null
上呼叫getId()
)時會發生錯誤。
提示:如果 manager 屬性是已經有_self
連結的對象,您可以重新使用該值作為href
而不是在此處重複。請參閱連結幫助程序。
$ 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 庫也提供了PagerfantaFactory
,可以輕鬆地從 Pagerfanta 實例建立PaginatedRepresentation
。如果您使用 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()) " )]
您可以透過閱讀官方文件:表達式語法來了解有關表達式語法的更多資訊。
本質上,每個表達式中都有一個名為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/components/expression_language/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
類別提供了getLinkHref($object, $rel, $absolute = false)
方法,該方法允許您取得任何給定關係名稱的任何物件的href值。它能夠從任何連結關係產生 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 );
}
}
請注意以下 JSON 內容中post
和relative
關係的href
表達式及其對應值:
{
"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
可讓您在 Twig 範本中使用 LinkHelper,以便您可以在 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 提供了一組序列化器。每個序列化程序都允許您按照特定格式產生 XML 或 JSON 內容,例如 HAL 或 Atom Links。
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 與 JSON 中的 HAL 類似,都描述了link
標籤和resource
標籤。
注意: 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
,它描述了兩種序列化連結和嵌入關係的方法。
憑藉強大且流暢的 API, HateoasBuilder
類別可用於輕鬆設定 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()
,但覆寫現有條目。請閱讀官方序列化器文件以獲取更多詳細資訊。
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()
存取從註解、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: ' ... ' ,
)]
財產 | 必需的 | 內容 | 表達語言 |
---|---|---|---|
姓名 | 是的 | 細繩 | 不 |
連結地址 | 如果未設定嵌入 | 字串/@Route | 是的 |
嵌入式 | 如果沒有設定href | 字串/@Embedded | 是的 |
屬性 | 不 | 大批 | 是的價值觀 |
排除 | 不 | @排除 | 不適用 |
重要提示: attributes
僅用於連結關係(即與href
屬性結合使用,而不是與embedded
屬性結合使用)。
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 產生器(如果您已配置)。
財產 | 必需的 | 內容 | 表達語言 |
---|---|---|---|
姓名 | 是的 | 細繩 | 不 |
參數 | 預設為 array() | 數組/字串 | 是(字串 + 數組值) |
絕對 | 預設為 false | 布林值/字串 | 是的 |
發電機 | 不 | 字串/空 | 不 |
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註解的embedded屬性中定義。如果您需要為嵌入資源配置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 ' ])]
)
)
);
}
}
$this->evaluator
實作CompilableExpressionEvaluatorInterface
用於以可快取並保存以供以後使用的形式解析表達式語言。如果您的關係中不需要表達語言,則不需要此服務。
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
僅用於安全錯誤修復和可能發生的重大問題。
請參閱貢獻文件。
安裝 Composer dev
依賴項:
php composer.phar install --dev
然後,使用 PHPUnit 執行測試套件:
bin/phpunit
Hateoas 是根據 MIT 許可證發布的。有關詳細信息,請參閱捆綁的許可證文件。