RESTful Services made easy with ZF2
by Rob Allen and Matthew Weier O’Phinney January 2013
RESTful Services made easy with ZF2 by Rob Allen and Matthew Weier - - PowerPoint PPT Presentation
RESTful Services made easy with ZF2 by Rob Allen and Matthew Weier OPhinney January 2013 About us Rob Allen ZF community team @akrabat . Matthew Weier O'Phinney ZF project lead @mwop Agenda RESTful fundamentals
by Rob Allen and Matthew Weier O’Phinney January 2013
Rob Allen
. Matthew Weier O'Phinney
REpresentational State Transfer
addressed
captures the current or intended state of a resource
transition to a new state
DELETE, etc.
allowing them to be portable.
allowing scalability.
This means no sessions!
cache responses.
to caching.
directly to the server, or to an intermediary proxy.
representations
(HATEOAS)
http://status.dev:8080/api/status/matthew
the resource
for allowed state transitions)
unsupported
states
JSON does not have a defined way of providing hypermedia links Options:
L i n k : < u r l > ; r e l = " r e l a t i o n " [ , . . . ]
See http://amundsen.com/media-types/collection/format/
{ " c o l l e c t i o n " : { " l i n k s " : [ { " h r e f " : " < u r i > " , " r e l " : " r e l a t i o n " } ] } }
See http://tools.ietf.org/html/draft-kelly-json-hal-03
{ " _ l i n k s " : { " r e l a t i o n " : { " h r e f " : " < u r i > " } } }
See http://tools.ietf.org/html/draft-nottingham-http-problem-02
{ " d e s c r i b e d B y " : " < u r l > " , " t i t l e " : " g e n e r i c t i t l e o f e r r o r t y p e " , " h t t p S t a t u s " : < s t a t u s c o d e > , " d e t a i l " : " s p e c i f i c m e s s a g e d e t a i l i n g e r r o r " }
form my request? What modifiers might be available?
HTTP methods allowed via the A l l o w header.
OPTIONS” by D. Keith Casey at REST Fest 2012)
pointing to documentation. See http://www.mnot.net/blog/2012/10/29/NO_OPTIONS
L i n k : < h t t p : / / s t a t u s . d e v / a p i / s t a t u s / d o c s . m d > ; \ r e l = " d e s c r i b e d b y "
The next generation of Zend Framework
5.5)
response
Publish and listen to events
triggers events.
u s e Z e n d \ E v e n t M a n a g e r \ E v e n t M a n a g e r , Z e n d \ E v e n t M a n a g e r \ E v e n t ; $ c a l l b a c k = f u n c t i o n ( $ e v e n t ) { e c h o " A n e v e n t h a s h a p p e n e d ! \ n " ; v a r _ d u m p ( $ e v e n t - > g e t N a m e ( ) ) ; } ; $ e v e n t s = n e w E v e n t M a n a g e r ( ) ; $ e v e n t s - > a t t a c h ( ' e v e n t N a m e ' , $ c a l l b a c k ) ; e c h o " \ n R a i s e a n e v e n t \ n " ; $ e v e n t s - > t r i g g e r ( ' e v e n t N a m e ' ) ;
Just a function (Any callback)
$ c a l l b a c k = f u n c t i o n ( $ e v e n t ) { e c h o " A n e v e n t h a s h a p p e n e d ! \ n " ; v a r _ d u m p ( $ e v e n t - > g e t N a m e ( ) ) ; v a r _ d u m p ( $ e v e n t - > g e t T a r g e t ( ) ) ; v a r _ d u m p ( $ e v e n t - > g e t P a r a m s ( ) ) ; } ; $ e v e n t s = $ s o m e O b j e c t - > g e t E v e n t M a n a g e r ( ) ; $ e v e n t s - > a t t a c h ( ' e v e n t N a m e ' , $ c a l l b a c k ) ;
Compose an EventManager within a class…
u s e Z e n d \ E v e n t M a n a g e r \ E v e n t M a n a g e r , Z e n d \ E v e n t M a n a g e r \ E v e n t ; c l a s s M y T a r g e t { p u b l i c $ e v e n t s ; p u b l i c f u n c t i o n _ _ c o n s t r u c t ( ) { $ t h i s - > e v e n t s = n e w E v e n t M a n a g e r ( ) ; } / / . . .
… and trigger actions within methods.
p u b l i c f u n c t i o n d o I t ( ) { $ e v e n t = n e w E v e n t ( ) ; $ e v e n t - > s e t T a r g e t ( $ t h i s ) ; $ e v e n t - > s e t P a r a m ( ' o n e ' , 1 ) ; $ t h i s - > e v e n t s - > t r i g g e r ( ' d o I t . p r e ' , $ e v e n t ) ; / / d o s o m e t h i n g h e r e $ t h i s - > e v e n t s - > t r i g g e r ( ' d o I t . p o s t ' , $ e v e n t ) ; }
$ c a l l b a c k = f u n c t i o n ( $ e v e n t ) { e c h o " R e s p o n d i n g t o d o I t . p r e ! \ n " ; v a r _ d u m p ( g e t _ c l a s s ( $ e v e n t - > g e t T a r g e t ( ) ) ) ; v a r _ d u m p ( $ e v e n t - > g e t N a m e ( ) ) ; v a r _ d u m p ( $ e v e n t - > g e t P a r a m s ( ) ) ; } ; $ t a r g e t = n e w M y T a r g e t ( ) ; $ t a r g e t - > e v e n t s - > a t t a c h ( ' d o I t . p r e ' , $ c a l l b a c k ) ; $ t a r g e t - > d o I t ( ) ;
Attach a listener to another class’ event manager
$ s h a r e d = $ e v e n t s - > g e t S h a r e d M a n a g e r ( ) ; / / o r $ s h a r e d = S t a t i c E v e n t M a n a g e r : : g e t I n s t a n c e ( ) ; $ s h a r e d - > a t t a c h ( ' G a l l e r y \ \ M a p p e r \ \ P h o t o ' , ' f i n d B y I d . p r e ' , f u n c t i o n ( E v e n t $ e ) { $ i d = $ e - > g e t P a r a m ( ' i d ' ) ; $ m e s s a g e = " R e t r i e v i n g p h o t o : $ i d " ; M y L o g g e r : : l o g ( $ m e s s a g e ) ; } ) ;
p u b l i c f u n c t i o n d o I t ( ) { $ e v e n t s = $ t h i s - > e v e n t s ; $ r e s u l t s = $ e v e n t s - > t r i g g e r ( ' d o I t ' , $ t h i s ) ; f o r e a c h ( $ r e s u l t s a s $ r e s u l t ) { v a r _ d u m p ( $ r e s u l t ) ; } }
$ r e s u l t s are in reverse order
(most recently triggered event first)
$ p a r a m s = a r r a y ( ' i d ' = > 1 ) ; $ r e s u l t s = $ t h i s - > e v e n t s - > t r i g g e r ( ' d o I t . p r e ' , $ t h i s , $ p a r a m s , f u n c t i o n ( $ r e s u l t ) { i f ( $ r e s u l t i n s t a n c e o f R e s u l t S e t ) { r e t u r n t r u e ; } r e t u r n f a l s e ; } ) ; i f ( $ r e s u l t s - > s t o p p e d ( ) ) { / / W e e n d e d e a r l y }
$ e v e n t s - > a t t a c h ( ' d o I t . p r e ' , $ c b , $ p r i o r i t y ) ;
It (lazily) instantiates and holds objects.
$ c o n t r o l l e r = $ s m - > g e t ( ' G a l l e r y \ M a p p e r \ P h o t o ' ) ;
(a b s t r a c t _ f a c t o r i e s )
/ / p r o g r a m a t i c a l l y $ s m - > s e t S e r v i c e ( ' f o o ' , $ f o o I n s t a n c e ) ; / / c o n f i g u r a t i o n a r r a y ( ' s e r v i c e s ' = > a r r a y ( ' f o o ' = > n e w F o o ( ) , ) ) ;
/ / p r o g r a m a t i c a l l y $ s m - > s e t I n v o k a b l e C l a s s ( ' f o o ' , ' B a r \ F o o ' ) ; / / c o n f i g u r a t i o n a r r a y ( ' i n v o k a b l e s ' = > a r r a y ( ' f o o ' = > ' B a r \ F o o ' , ) ) ;
/ / p r o g r a m a t i c a l l y $ s m - > s e t F a c t o r y ( ' f o o ' , f u n c t i o n ( $ s m ) { $ d e p e n d e n c y = $ s m - > g e t ( ' D e p e n d e n c y ' ) r e t u r n n e w F o o ( $ d e p e n d e n c y ) ; } ) ; / / c o n f i g u r a t i o n a r r a y ( ' f a c t o r i e s ' = > a r r a y ( ' f o o ' = > f u n c t i o n ( $ s m ) { / / . . } , ' b a r ' = > ' S o m e \ S t a t i c : : m e t h o d ' , ' b a z ' = > ' C l a s s \ I m p l e m e n t i n g \ F a c t o r y I n t e r f a c e ' , ' b a t ' = > ' C l a s s \ I m p l e m e n t i n g \ I n v o k e ' , ) ) ;
/ / p r o g r a m a t i c a l l y $ s m - > s e t A l i a s ( ' f o o _ d b ' , ' d b _ a d a p t e r ' ) ; / / c o n f i g u r a t i o n a r r a y ( ' f a c t o r i e s ' = > a r r a y ( ' f o o _ d b ' , ' d b _ a d a p t e r ' , / / a l i a s o f a s e r v i c e ' b a r _ d b ' , ' f o o _ d b ' , / / a l i a s o f a n a l i a s ) ) ; / / A l l t h e s a m e i n s t a n c e $ d b = $ s m - > g e t ( ' d b _ a d a p t e r ' ) ; $ d b = $ s m - > g e t ( ' f o o _ d b ' ) ; $ d b = $ s m - > g e t ( ' b a r _ d b ' ) ;
/ / p r o g r a m a t i c a l l y $ s m - > a d d I n i t i a l i z e r ( $ c a l l b a c k ) ; / / c o n f i g u r a t i o n a r r a y ( ' i n i t i a l i z e r s ' = > a r r a y ( $ i n s t a n c e , $ c a l l b a c k , ' C l a s s \ I m p l e m e n t i n g \ I n i t i a l i z e r I n t e r f a c e ' , ' C l a s s \ I m p l e m e n t i n g \ I n v o k e ' , ) ) ;
f u n c t i o n ( $ i n s t a n c e , $ s m ) { i f ( $ i n s t a n c e i n s t a n c e o f F o o A w a r e I n t e r f a c e ) { r e t u r n ; } $ i n s t a n c e - > s e t F o o ( $ s m - > g e t ( ' f o o ' ) ) ; } ,
Factory capable of handling multiple services
/ / p r o g r a m a t i c a l l y $ s m - > a d d A b s t r a c t F a c t o r y ( $ a b s t r a c t F a c t o r y I n s t a n c e ) ; $ s m - > a d d A b s t r a c t F a c t o r y ( ' F o o F a c t o r y ' ) ; / / c o n f i g u r a t i o n a r r a y ( ' a b s t r a c t _ f a c t o r i e s ' = > a r r a y ( ' C l a s s \ I m p l e m e n t i n g \ A b s t r a c t F a c t o r y I n t e r f a c e ' , $ s o m e A b s t r a c t F a c t o r y I n s t a n c e , ) ;
c l a s s A F a c t o r y i m p l e m e n t s A b s t r a c t F a c t o r y I n t e r f a c e { p u b l i c f u n c t i o n c a n C r e a t e S e r v i c e W i t h N a m e ( S e r v i c e L o c a t o r I n t e r f a c e $ s e r v i c e s , $ n a m e , $ r e q u e s t e d N a m e ) { r e t u r n i n _ a r r a y ( $ n a m e , a r r a y ( ' f o o ' , ' b a r ' ) ; } p u b l i c f u n c t i o n c r e a t e S e r v i c e W i t h N a m e ( / * s i g * / ) { r e t u r n $ n a m e = = ' f o o ' ? n e w F o o : n e w B a r ; } }
a l i a s e s , i n i t i a l i z e r s , a b s t r a c t _ f a c t o r i e s
Re-usable pieces of functionality for constructing a more complex application.
Provide your application with:
Reusable between applications - “plug & play”!
Anything!
< ? p h p n a m e s p a c e M y M o d u l e ; c l a s s M o d u l e { }
That’s it.
n a m e s p a c e E d p M a r k d o w n ; c l a s s M o d u l e e x t e n d s \ Z e n d \ V i e w \ H e l p e r \ A b s t r a c t H e l p e r { p u b l i c f u n c t i o n g e t V i e w H e l p e r C o n f i g ( ) { r e t u r n a r r a y ( ' s e r v i c e s ' = > a r r a y ( ' m a r k d o w n ' = > $ t h i s ) ) ; } p u b l i c f u n c t i o n _ _ i n v o k e ( $ s t r i n g = n u l l ) { r e q u i r e _ o n c e _ _ D I R _ _ . ' m a r k d o w n . p h p ' ; r e t u r n M a r k d o w n ( $ s t r i n g ) ; } }
M o d u l e class
then register with the service manager.
modules are loaded.
are merged together then merged with the result of steps 1 and 2.
n a m e s p a c e M y ; c l a s s M o d u l e { p u b l i c f u n c t i o n g e t A u t o l o a d e r C o n f i g ( ) { / / r e t u r n c o n f i g f o r a u t o l o a d e r f a c t o r y } p u b l i c f u n c t i o n g e t C o n f i g ( ) { r e t u r n i n c l u d e _ _ D I R _ _ . ' / c o n f i g / m o d u l e . c o n f i g . p h p ' ; } p u b l i c f u n c t i o n o n B o o t s t r a p ( $ e ) { / / d o i n i t i a l i z a t i o n } }
M a r k d o w n ).
Putting REST & ZF2 together
implementation
methods for a given resource
' s t a t u s ' = > a r r a y ( ' t y p e ' = > ' S e g m e n t ' , ' o p t i o n s ' = > a r r a y ( ' r o u t e ' = > ' / a p i / s t a t u s [ / : i d ] ' , ' d e f a u l t s ' = > a r r a y ( ' c o n t r o l l e r ' = > ' S t a t u s C o n t r o l l e r ' , ) , ' c o n s t r a i n t s ' = > a r r a y ( ' i d ' = > ' [ a - f 0 - 9 ] { 4 0 } ' , ) , ) , ) ,
(a p p l i c a t i o n / w w w - f o r m - u r l e n c o d e d and JSON bodies will be parsed and provided as $ d a t a )
$ c r i t e r i a = a r r a y ( ' Z e n d \ V i e w \ M o d e l \ J s o n M o d e l ' = > a r r a y ( ' \ * / j s o n ' , ) , ) ; $ m o d e l = $ t h i s - > a c c e p t a b l e V i e w M o d e l ( $ c r i t e r i a ) ;
$ s h a r e d E v e n t s - > a t t a c h ( ' Z e n d \ M v c \ C o n t r o l l e r \ A b s t r a c t R e s t f u l C o n t r o l l e r ' , ' d i s p a t c h ' , $ l i s t e n e r
) ;
f u n c t i o n ( M v c E v e n t $ e ) { $ r e s u l t = $ e - > g e t R e s u l t ( ) ; i f ( ! $ r e s u l t i n s t a n c e o f J s o n M o d e l ) { r e t u r n ; } $ a p p = $ e - > g e t A p p l i c a t i o n ( ) ; $ s e r v i c e s = $ a p p - > g e t S e r v i c e M a n a g e r ( ) ; $ s t r a t e g y = $ s e r v i c e s - > g e t ( ' V i e w J s o n S t r a t e g y ' ) ; $ v i e w = $ s e r v i c e s - > g e t ( ' V i e w ' ) ; $ v i e w - > a t t a c h ( $ s t r a t e g y , 1 0 0 ) ; } ,
$ h e a d e r s = $ r e q u e s t - > g e t H e a d e r s ( ) ; i f ( ! $ h e a d e r s - > h a s ( ' A c c e p t ' ) ) { / / n o A c c e p t h e a d e r ; d o d e f a u l t r e t u r n ; } $ a c c e p t = $ h e a d e r s - > g e t ( ' A c c e p t ' ) ; i f ( $ a c c e p t - > m a t c h ( $ m e d i a T y p e ) ) { / / w e h a v e a m a t c h ! r e t u r n ; }
server, and port if necessary
p r e v relations
generate the path if a route is known.
scheme/server/port combination
pagination relations
$ p a t h = $ u r l H e l p e r - > f r o m R o u t e ( $ r o u t e , a r r a y ( ' i d ' = > $ i d , ) ) ; $ u r l = $ s e r v e r U r l H e l p e r - > _ _ i n v o k e ( $ p a t h ) ;
/ / $ p a g e i s t h e c u r r e n t p a g e / / $ c o u n t i s t h e t o t a l n u m b e r o f p a g e s / / $ b a s e i s t h e b a s e U R L t o t h e r e s o u r c e $ n e x t = ( $ p a g e = = $ c o u n t ) ? f a l s e : $ p a g e + 1 ; $ p r e v = ( $ p a g e = = 1 ) ? f a l s e : $ p a g e - 1 ; $ l i n k s = a r r a y ( ' s e l f ' = > $ b a s e . ( 1 = = $ p a g e ? ' ' : ' ? p = ' . $ p a g e ) , ) ; i f ( $ p a g e ! = 1 ) { $ l i n k s [ ' f i r s t ' ] = $ b a s e ; }
i f ( $ c o u n t ! = 1 ) { $ l i n k s [ ' l a s t ' ] = $ b a s e . ' ? p = ' . $ c o u n t ; } i f ( $ p r e v ) { $ l i n k s [ ' p r e v ' ] = $ b a s e . ( ( 1 = = $ p r e v ) ? ' ' : ' ? p = ' . $ p r e v ; } i f ( $ n e x t ) { $ l i n k s [ ' n e x t ' ] = $ b a s e . ' ? p = ' . $ n e x t ; }
correct
helpers
payloads in the view model
inject them; similar issues to the renderer, though.
status, http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html describing HTTP status codes is a nice default.
HTTP status, use established status descriptions.
information.
errors happen.
you can generate 404 responses in this format.
social status
chronological order
header
view model
relevant view model based on A c c e p t header; create a special view model type that we can listen for later.
query later
collection or an individual item.
This is meant to be alive demo of the finished API, and maybe some code samples.
persistence related operations and a route, and go. test
What have we learnt today?
but no single, canonical methodology
available for a resource
representations govern how to parse a request as well as how to format a response
and available state changes
RESTful JSON APIs
applications
https://joind.in/7781
Rob Allen : http://akrabat.com : @akrabat Matthew Weier O’Phinney : http://mwop.net : @mwop