Pustaka PHP untuk mendukung penerapan representasi untuk layanan web HATEOAS REST.
Cara yang disarankan untuk menginstal Hateoas adalah melalui Composer. Memerlukan paket willdurand/hateoas
dengan menjalankan perintah berikut:
composer require willdurand/hateoas
Ini akan menyelesaikan versi stabil terbaru.
Jika tidak, instal perpustakaan dan atur autoloader sendiri.
Jika Anda ingin menggunakan anotasi untuk konfigurasi, Anda perlu menginstal paket doctrine/annotations
:
composer require doctrine/annotations
Jika aplikasi Anda menggunakan PHP 8.1 atau lebih tinggi, disarankan untuk menggunakan atribut asli PHP. Dalam hal ini Anda tidak perlu menginstal paket Doctrine.
Ada bundel untuk itu! Instal BazingaHateoasBundle, dan nikmatilah!
Penting:
Bagi yang menggunakan versi
1.0
, Anda dapat melompat ke halaman dokumentasi ini.Bagi yang menggunakan versi
2.0
, Anda dapat melompat ke halaman dokumentasi ini.Dokumentasi berikut telah ditulis untuk Hateoas 3.0 dan yang lebih baru.
Hateoas memanfaatkan perpustakaan Serializer untuk menyediakan cara yang bagus untuk membangun layanan web HATEOAS REST. HATEOAS adalah singkatan dari Hypermedia sebagai Engine of Application State , dan menambahkan tautan hypermedia ke representasi Anda (yaitu respons API Anda). HATEOAS adalah tentang kemampuan untuk menemukan tindakan pada sumber daya.
Misalnya, Anda memiliki API Pengguna yang mengembalikan representasi satu pengguna sebagai berikut:
{
"user" : {
"id" : 123 ,
"first_name" : " John " ,
"last_name" : " Doe "
}
}
Untuk memberi tahu konsumen API Anda cara mengambil data untuk pengguna khusus ini, Anda harus menambahkan tautan pertama Anda ke representasi ini, sebut saja itu self
karena ini adalah URI untuk pengguna khusus ini:
{
"user" : {
"id" : 123 ,
"first_name" : " John " ,
"last_name" : " Doe " ,
"_links" : {
"self" : { "href" : " http://example.com/api/users/123 " }
}
}
}
Mari gali Hateoas sekarang.
Dalam terminologi Hateoas, tautan dipandang sebagai hubungan yang ditambahkan ke sumber daya. Perlu disebutkan bahwa relasi juga mengacu pada sumber daya yang tertanam , namun topik ini akan dibahas di bagian Sumber Daya yang Disematkan.
Tautan adalah relasi yang diidentifikasi dengan name
(misalnya self
) dan memiliki parameter 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 () {}
}
Pada contoh di atas, kita mengkonfigurasi relasi self
yang berupa link karena parameter href
. Nilainya, yang mungkin terlihat aneh pada pandangan pertama, akan dibahas secara luas di bagian Bahasa Ekspresi. Nilai khusus ini digunakan untuk menghasilkan URI.
Di bagian ini, anotasi/atribut digunakan untuk mengonfigurasi Hateoas. Format XML dan YAML juga didukung. Jika mau, Anda juga bisa menggunakan PHP biasa.
Penting: Anda harus mengkonfigurasi Serializer dan Hateoas dengan cara yang sama. Misalnya jika Anda menggunakan YAML untuk mengkonfigurasi Serializer, gunakan YAML untuk mengkonfigurasi Hateoas.
Cara termudah untuk mencoba HATEOAS adalah dengan HateoasBuilder
. Pembuatnya memiliki banyak metode untuk mengonfigurasi serializer Hateoas, tetapi kami tidak akan membahasnya sekarang (lihat The HateoasBuilder). Semuanya berfungsi dengan baik di luar kotak:
use Hateoas HateoasBuilder ;
$ hateoas = HateoasBuilder:: create ()-> build ();
$ user = new User ( 42 , ' Adrien ' , ' Brault ' );
$ json = $ hateoas -> serialize ( $ user , ' json ' );
$ xml = $ hateoas -> serialize ( $ user , ' xml ' );
Objek $hateoas
adalah turunan dari JMSSerializerSerializerInterface
, yang berasal dari perpustakaan Serializer. Hateoas tidak datang dengan serializernya sendiri, ia terhubung ke JMS Serializer.
Secara default, Hateoas menggunakan Hypertext Application Language (HAL) untuk serialisasi JSON. Ini menentukan struktur respon (misalnya "link" harus berada di bawah kunci _links
):
{
"id" : 42 ,
"first_name" : " Adrien " ,
"last_name" : " Brault " ,
"_links" : {
"self" : {
"href" : " /api/users/42 "
}
}
}
Untuk XML, Atom Links digunakan secara default:
< user id = " 42 " >
< first_name > <![CDATA[ Adrien ]]> </ first_name >
< last_name > <![CDATA[ Brault ]]> </ last_name >
< link rel = " self " href = " /api/users/42 " />
</ user >
Perlu disebutkan bahwa format ini adalah format default , bukan satu-satunya format yang tersedia. Anda dapat menggunakan format berbeda melalui serializer berbeda, dan bahkan menambahkan format Anda sendiri.
Sekarang setelah Anda mengetahui cara menambahkan tautan , mari kita lihat cara menambahkan sumber daya yang disematkan .
Terkadang, lebih efisien untuk menyematkan sumber daya terkait daripada menautkannya, karena hal ini mencegah klien membuat permintaan tambahan untuk mengambil sumber daya tersebut.
Sumber daya tertanam adalah relasi bernama yang berisi data, diwakili oleh parameter 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 ;
}
Catatan: Anda harus mengecualikan properti manajer dari serialisasi, jika tidak, serializer dan Hateoas akan membuat serialisasi. Anda juga harus mengecualikan relasi manager ketika managernya null
, karena jika tidak, kesalahan akan terjadi saat membuat tautan href
(memanggil getId()
pada null
).
Tip: Jika properti manager adalah objek yang sudah memiliki link _self
, Anda dapat menggunakan kembali nilai tersebut untuk href
daripada mengulanginya di sini. Lihat 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 ' );
Untuk json
, representasi HAL menempatkan relasi tertanam ini di dalam kunci _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 "
}
}
}
}
}
Dalam XML, membuat serialisasi relasi embedded
akan membuat elemen baru:
< 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 >
Nama tag sumber daya yang disematkan disimpulkan dari anotasi @XmlRoot
( xml_root_name
di YAML, xml-root-name
di XML) yang berasal dari konfigurasi Serializer.
Perpustakaan menyediakan beberapa kelas di namespace HateoasRepresentation*
untuk membantu Anda dengan tugas-tugas umum. Ini adalah kelas sederhana yang dikonfigurasi dengan anotasi perpustakaan.
Kelas PaginatedRepresentation
, OffsetRepresentation
dan CollectionRepresentation
mungkin adalah kelas yang paling menarik. Ini berguna ketika sumber daya Anda sebenarnya adalah kumpulan sumber daya (misalnya /users
adalah kumpulan pengguna). Ini membantu Anda merepresentasikan koleksi dan menambahkan penomoran halaman dan batasan:
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
menawarkan representasi dasar dari koleksi yang disematkan.
PaginatedRepresentation
dirancang untuk menambahkan tautan self
, first
, dan last
, next
, dan previous
jika memungkinkan.
OffsetRepresentation
berfungsi seperti PaginatedRepresentation
tetapi berguna ketika penomoran halaman dinyatakan dengan offset
, limit
dan total
.
RouteAwareRepresentation
menambahkan relasi self
berdasarkan rute tertentu.
Anda dapat membuat URI absolut dengan menyetel parameter absolute
ke true
di PaginatedRepresentation
dan RouteAwareRepresentation
.
Pustaka Hateoas juga menyediakan PagerfantaFactory
untuk dengan mudah membuat PaginatedRepresentation
dari instans Pagerfanta. Jika Anda menggunakan perpustakaan Pagerfanta, ini adalah cara yang lebih mudah untuk membuat representasi koleksi:
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 ' );
Anda akan mendapatkan konten JSON berikut:
{
"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 }
]
}
}
Dan konten XML berikut:
<? 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 >
Jika Anda ingin mengkustomisasi CollectionRepresentation
sebaris, berikan argumen ketiga sebagai argumen createRepresentation()
metode:
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 ' );
Jika Anda ingin mengubah nama root xml dari koleksi, buat kelas baru dengan root xml dikonfigurasi dan gunakan mekanisme inline:
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 );
Seperti disebutkan di bagian sebelumnya, representasi adalah kelas yang dikonfigurasi dengan anotasi perpustakaan untuk membantu Anda dalam tugas-tugas umum. Representasi koleksi dijelaskan dalam Menangani Koleksi.
VndErrorRepresentation
memungkinkan Anda mendeskripsikan respons kesalahan mengikuti spesifikasi vnd.error
.
$ error = new VndErrorRepresentation (
' Validation failed ' ,
42 ,
' http://.../ ' ,
' http://.../ '
);
Membuat serial representasi seperti itu dalam XML dan JSON akan memberi Anda keluaran berikut:
<? 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://.../ "
}
}
}
Petunjuk: disarankan untuk membuat kelas kesalahan Anda sendiri yang memperluas kelas VndErrorRepresentation
.
Hateoas mengandalkan komponen Symfony ExpressionLanguage yang kuat untuk mengambil nilai seperti tautan, id, atau objek yang akan disematkan.
Setiap kali Anda mengisi nilai (misalnya href
Relasi dalam anotasi atau YAML), Anda dapat meneruskan nilai hardcode atau ekspresi . Untuk menggunakan Bahasa Ekspresi, Anda harus menggunakan notasi 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()) " )]
Anda dapat mempelajari lebih lanjut tentang Sintaks Ekspresi dengan membaca dokumentasi resmi: Sintaks Ekspresi.
Secara alami, variabel khusus bernama object
tersedia di setiap ekspresi, dan mewakili objek saat ini:
expr(object.getId())
Kami menyebut variabel seperti itu sebagai variabel konteks .
Anda dapat menambahkan variabel konteks Anda sendiri ke konteks Bahasa Ekspresi dengan menambahkannya ke evaluator ekspresi.
Dengan menggunakan HateoasBuilder
, panggil metode setExpressionContextVariable()
untuk menambahkan variabel konteks baru:
use Hateoas HateoasBuilder ;
$ hateoas = HateoasBuilder:: create ()
-> setExpressionContextVariable ( ' foo ' , new Foo ())
-> build ();
Variabel foo
sekarang tersedia:
expr(foo !== null)
Untuk informasi lebih lanjut tentang cara menambahkan fungsi ke bahasa ekspresi, silakan merujuk ke https://symfony.com/doc/current/components/expression_lingual/extending.html
Karena Anda dapat menggunakan Bahasa Ekspresi untuk menentukan tautan relasi (kunci href
), Anda dapat melakukan banyak hal secara default. Namun jika Anda menggunakan kerangka kerja, kemungkinan besar Anda ingin menggunakan rute untuk membangun tautan.
Pertama-tama Anda perlu mengonfigurasi UrlGenerator
di pembuatnya. Anda dapat menerapkan HateoasUrlGeneratorUrlGeneratorInterface
, atau menggunakan 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 ()
;
Anda kemudian dapat menggunakan anotasi @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 "
}
}
}
Perhatikan bahwa perpustakaan dilengkapi dengan SymfonyUrlGenerator
. Misalnya, untuk menggunakannya di Silex:
use Hateoas UrlGenerator SymfonyUrlGenerator ;
$ hateoas = HateoasBuilder:: create ()
-> setUrlGenerator ( null , new SymfonyUrlGenerator ( $ app [ ' url_generator ' ]))
-> build ()
;
Hateoas menyediakan serangkaian pembantu untuk memudahkan proses pembuatan API.
Kelas LinkHelper
menyediakan metode getLinkHref($object, $rel, $absolute = false)
yang memungkinkan Anda mendapatkan nilai href dari objek apa pun, untuk nama relasi apa pun. Ia mampu menghasilkan URI (baik absolut atau relatif) dari relasi tautan apa pun:
$ 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
Fungsi Fitur di atas juga tersedia dalam ekspresi Anda (lih. Bahasa Ekspresi) melalui fungsi 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 );
}
}
Perhatikan ekspresi href
untuk post
dan relasi relative
, serta nilainya yang terkait dalam konten JSON berikut:
{
"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 " }
}
}
}
Perlu disebutkan bahwa Anda dapat memaksakan apakah Anda menginginkan URI absolut atau relatif dengan menggunakan argumen ketiga dalam metode getLinkHref()
dan fungsi link
.
Penting: secara default, semua URI akan bersifat relative , bahkan URI yang didefinisikan sebagai absolut dalam konfigurasinya.
$ 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 juga menyediakan satu set ekstensi Ranting.
LinkExtension
memungkinkan Anda untuk menggunakan LinkHelper ke dalam templat Twig Anda, sehingga Anda dapat menghasilkan tautan di templat HTML Anda misalnya.
Ekstensi ini mengekspos metode pembantu getLinkHref()
melalui fungsi link_href
Twig:
{{ 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 menyediakan satu set serializer . Setiap serializer memungkinkan Anda menghasilkan konten XML atau JSON mengikuti format tertentu, seperti HAL, atau Atom Links misalnya.
JsonHalSerializer
memungkinkan Anda menghasilkan relasi yang sesuai dengan HAL di JSON. Ini adalah serializer JSON default di Hateoas.
HAL menyediakan kemampuan penautannya dengan konvensi yang mengatakan bahwa objek sumber daya memiliki properti khusus yang disebut _links
. Properti ini adalah objek yang berisi link. Tautan ini dikunci oleh relasi tautannya.
HAL juga menjelaskan konvensi lain yang mengatakan bahwa suatu sumber daya mungkin memiliki properti cadangan lain bernama _embedded
. Properti ini mirip dengan _links
di mana sumber daya yang tertanam dikunci berdasarkan nama relasi. Perbedaan utamanya adalah bahwa alih-alih menjadi tautan, nilainya adalah objek sumber daya.
{
"message" : " Hello, World! " ,
"_links" : {
"self" : {
"href" : " /notes/0 "
}
},
"_embedded" : {
"associated_events" : [
{
"name" : " SymfonyCon " ,
"date" : " 2013-12-12T00:00:00+0100 "
}
]
}
}
XmlSerializer
memungkinkan Anda membuat Atom Links ke dalam dokumen XML Anda. Ini adalah serializer XML default.
<? 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
memungkinkan Anda menghasilkan relasi yang sesuai dengan HAL dalam XML.
HAL dalam XML mirip dengan HAL di JSON dalam arti menjelaskan tag link
dan tag resource
.
Catatan: relasi self
sebenarnya akan menjadi atribut sumber daya utama dan bukan menjadi tag link
. Tautan lain akan dibuat sebagai tag 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 >
Anda harus mengimplementasikan SerializerInterface
yang menjelaskan dua metode untuk membuat serial link dan relasi yang disematkan .
Kelas HateoasBuilder
digunakan untuk mengonfigurasi Hateoas dengan mudah berkat API yang kuat dan lancar.
use Hateoas HateoasBuilder ;
$ hateoas = HateoasBuilder:: create ()
-> setCacheDir ( ' /path/to/cache/dir ' )
-> setDebug ( $ trueOrFalse )
-> setDefaultXmlSerializer ()
. . .
-> build ();
Semua metode di bawah ini mengembalikan pembuat saat ini, sehingga Anda dapat merangkainya.
setXmlSerializer(SerializerInterface $xmlSerializer)
: mengatur serializer XML yang akan digunakan. Standarnya adalah: XmlSerializer
;setDefaultXmlSerializer()
: menyetel serializer XML default ( XmlSerializer
). setJsonSerializer(SerializerInterface $jsonSerializer)
: mengatur serializer JSON yang akan digunakan. Standarnya adalah: JsonHalSerializer
;setDefaultJsonSerializer()
: menyetel serializer JSON default ( JsonHalSerializer
). setUrlGenerator($name = null, UrlGeneratorInterface $urlGenerator)
: menambahkan generator URL bernama baru. Jika $name
adalah null
, pembuat URL akan menjadi yang default. setExpressionContextVariable($name, $value)
: menambahkan variabel konteks ekspresi baru;setExpressionLanguage(ExpressionLanguage $expressionLanguage)
; includeInterfaceMetadata($include)
: apakah akan menyertakan metadata dari antarmuka;setMetadataDirs(array $namespacePrefixToDirMap)
: menyetel peta awalan namespace ke direktori. Metode ini mengesampingkan direktori yang ditentukan sebelumnya;addMetadataDir($dir, $namespacePrefix = '')
: menambahkan direktori tempat serializer akan mencari metadata kelas;addMetadataDirs(array $namespacePrefixToDirMap)
: menambahkan peta awalan namespace ke direktori;replaceMetadataDir($dir, $namespacePrefix = '')
: mirip dengan addMetadataDir()
, tetapi menggantikan entri yang sudah ada.Silakan baca dokumentasi resmi Serializer untuk lebih jelasnya.
setDebug($debug)
: mengaktifkan atau menonaktifkan mode debug;setCacheDir($dir)
: mengatur direktori cache.Baik serializer maupun pustaka Hateoas mengumpulkan metadata tentang objek Anda dari berbagai sumber seperti YML, XML, atau anotasi. Untuk membuat proses ini seefisien mungkin, Anda disarankan untuk mengizinkan perpustakaan Hateoas untuk menyimpan informasi ini dalam cache. Untuk melakukannya, konfigurasikan direktori cache:
$ builder = Hateoas HateoasBuilder:: create ();
$ hateoas = $ builder
-> setCacheDir ( $ someWritableDir )
-> build ();
Hateoas mendukung beberapa sumber metadata. Secara default, ini menggunakan anotasi Doktrin (PHP < 8.1) atau atribut asli PHP (PHP >= 8.1), namun Anda juga dapat menyimpan metadata dalam file XML atau YAML. Untuk yang terakhir, perlu mengkonfigurasi direktori metadata tempat file-file tersebut berada:
$ hateoas = Hateoas HateoasBuilder:: create ()
-> addMetadataDir ( $ someDir )
-> build ();
Hateoas mengharapkan file metadata diberi nama seperti nama kelas yang sepenuhnya memenuhi syarat di mana semua diganti dengan
.
. Jika kelas Anda akan diberi nama VendorPackageFoo
file metadata harus ditempatkan di $someDir/Vendor.Package.Foo.(xml|yml)
.
Hateoas memungkinkan kerangka kerja untuk secara dinamis menambahkan relasi ke kelas dengan menyediakan titik ekstensi pada tingkat konfigurasi. Fitur ini dapat berguna bagi mereka yang ingin membuat layer baru di atas Hateoas, atau menambahkan relasi "global" daripada menyalin konfigurasi yang sama pada setiap kelas.
Untuk memanfaatkan mekanisme ini, antarmuka ConfigurationExtensionInterface
harus diimplementasikan:
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 ' ,
' / '
)
);
}
}
}
Anda dapat mengakses relasi yang ada yang dimuat dari Anotasi, XML, atau YAML dengan $classMetadata->getRelations()
.
Jika $classMetadata
memiliki relasi, atau jika Anda menambahkan relasi ke dalamnya, relasinya akan di-cache. Jadi jika Anda membaca file konfigurasi (Anotasi, XML, atau YAML), pastikan untuk mereferensikannya pada metadata kelas:
$ 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 >
Lihat file hateoas.xsd
untuk lebih jelasnya.
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())" ]
Anotasi ini dapat didefinisikan di kelas.
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: ' ... ' ,
)]
Milik | Diperlukan | Isi | Bahasa ekspresi |
---|---|---|---|
nama | Ya | rangkaian | TIDAK |
href | Jika tertanam tidak disetel | string / @Rute | Ya |
tertanam | Jika href tidak disetel | string / @Tertanam | Ya |
atribut | TIDAK | susunan | Ya tentang nilai |
pengecualian | TIDAK | @Pengecualian | T/A |
Penting: attributes
hanya digunakan pada relasi tautan (yaitu digabungkan dengan properti href
, bukan dengan properti 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 ' ,
),
)]
Anotasi ini dapat ditentukan di properti href dari anotasi @Relation. Ini memungkinkan Anda menggunakan generator URL Anda, jika Anda telah mengonfigurasinya.
Milik | Diperlukan | Isi | Bahasa ekspresi |
---|---|---|---|
nama | Ya | rangkaian | TIDAK |
parameter | Defaultnya adalah array() | susunan/string | Ya (nilai string + array) |
mutlak | Defaultnya salah | boolean / string | Ya |
generator | TIDAK | string / nol | TIDAK |
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 ' ,
),
)]
Anotasi ini dapat ditentukan dalam properti anotasi @Relation yang disematkan . Hal ini berguna jika Anda perlu mengonfigurasi opsi exclusion
atau xmlElementName
untuk sumber daya yang disematkan.
Milik | Diperlukan | Isi | Bahasa ekspresi |
---|---|---|---|
isi | Ya | string/array | Ya (tali) |
pengecualian | Defaultnya adalah array() | @Pengecualian | T/A |
xmlElementName | Defaultnya adalah array() | rangkaian | TIDAK |
Anotasi ini dapat ditentukan dalam properti pengecualian anotasi @Relation dan @Embedded.
Milik | Diperlukan | Isi | Bahasa ekspresi |
---|---|---|---|
kelompok | TIDAK | susunan | TIDAK |
sejakVersion | TIDAK | rangkaian | TIDAK |
sampai Versi | TIDAK | rangkaian | TIDAK |
kedalaman maksimal | TIDAK | bilangan bulat | TIDAK |
kecualikanJika | TIDAK | string / boolean | Ya |
Semua nilai kecuali excludeIf
bertindak dengan cara yang sama seperti ketika digunakan secara langsung pada properti reguler dengan serializer.
excludeIf
mengharapkan boolean dan berguna ketika ekspresi lain gagal dalam keadaan tertentu. Dalam contoh ini, jika metode getManager
adalah null
, Anda harus mengecualikannya untuk mencegah kegagalan pembuatan 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 {}
}
Anotasi ini dapat didefinisikan di kelas. Ini berguna jika Anda ingin membuat serialisasi beberapa hubungan (tautan). Sebagai contoh:
{
"_links": {
"relation_name": [
{"href": "link1"},
{"href": "link2"},
{"href": "link3"}
]
}
}
Milik | Diperlukan | Isi | Bahasa ekspresi |
---|---|---|---|
nama | Ya | rangkaian | Ya |
Itu bisa berupa "nama":
my_func
MyClass::getExtraRelations
expr(service('user.rel_provider').getExtraRelations())
Berikut dan contoh penggunaan bahasa ekspresi:
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
{
...
}
Di sini kelas 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
yang mengimplementasikan CompilableExpressionEvaluatorInterface
digunakan untuk mengurai bahasa ekspresi dalam bentuk yang dapat di-cache dan disimpan untuk digunakan nanti. Jika Anda tidak memerlukan bahasa ekspresi dalam relasi Anda, maka layanan ini tidak diperlukan.
Layanan user.rel_provider
didefinisikan sebagai:
user.rel_provider :
class : UserRelPrvider
arguments :
- ' @jms_serializer.expression_evaluator '
Dalam hal ini jms_serializer.expression_evaluator
adalah layanan yang mengimplementasikan CompilableExpressionEvaluatorInterface
.
Bagian ini mengacu pada internal Hateoas, yang menyediakan dokumentasi tentang bagian tersembunyi dari perpustakaan ini. Hal ini tidak selalu relevan bagi pengguna akhir, namun menarik bagi pengembang atau orang yang tertarik mempelajari cara kerja berbagai hal.
willdurand/hateoas
mengikuti Versi Semantik.
Mulai Oktober 2013, versi 1.x
dan 0.x
secara resmi tidak didukung lagi (perhatikan bahwa 1.x
tidak pernah dirilis).
Versi 3.x
adalah versi stabil utama saat ini.
Versi 2.x
dipertahankan hanya untuk perbaikan bug keamanan dan masalah besar yang mungkin terjadi.
Lihat BERKONTRIBUSI file.
Instal dependensi dev
Composer:
php composer.phar install --dev
Kemudian, jalankan test suite menggunakan PHPUnit:
bin/phpunit
Hateoas dirilis di bawah Lisensi MIT. Lihat file LISENSI yang dibundel untuk detailnya.