Returned values from listeners 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)
Short-circuiting $ 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 }
Priority • Control the order of execution of listeners • $priority is last parameter to attach() $ 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 ) ; • Default is 1 • Larger number increases priority (e.g. 1000) • Smaller number decreases priority (e.g. -500)
Services It (lazily) instantiates and holds objects.
Services • Objects you work with (including Controllers). • Easy to replace alternative implementations. • Clean and simple way to configure dependencies. • Explicit and easy to understand - no magic! • Inversion of Control.
Usage $ 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 ' ) ;
Types of services • Instances ( s e r v i c e s ) • Constructor-less classes ( i n v o k a b l e s ) • Factories for objects with dependencies ( f a c t o r i e s ) • Aliased services ( a l i a s e s ) • Automated initialization ( i n i t i a l i z e r s ) • Factories for multiple related objects ( a b s t r a c t _ f a c t o r i e s )
Instances / / 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 ( ) , ) ) ;
Invokables / / 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 ' , ) ) ;
Factories / / 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 ' , ) ) ;
Aliases / / 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 ' ) ;
Initializers / / 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 ' , ) ) ;
An initializer 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 ' ) ) ; } ,
Abstract factories 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 , ) ;
An abstract factory 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 ; } }
Other features • All plugin managers are services managers. • Services are shared - can disable per service. • Manager “peering” is available.
Configuration in practice • A nested array in: • MyModuleModule::getServiceConfig() • ‘service_manager’ array key in config • sub-array keys : s e r v i c e s , i n v o k a b l e s , f a c t o r i e s , 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
Modules Re-usable pieces of functionality for constructing a more complex application.
Modules Provide your application with: • autoloading • configuration • services (inc controllers, plugins, etc.) • event listeners Reusable between applications - “plug & play”!
What can modules be? Anything! • Plugins: payment module for e-commerce • View helpers: Markdown support • Themes: CSS files, images, view scripts • Libraries: Doctrine2 integration, RESTful support • Applications: blog, e-commerce platform, CMS
A module is… • A PHP namespace • A class called M o d u l e within that namespace • which provides features to the application
A ZF2 Module < ? 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.
A complete ZF2 module 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 ) ; } }
ModuleManager • Loads all modules • Triggers an event for each module • allowing listeners to act on Module classes • Results in calls specific methods within your M o d u l e class
Module methods called • g e t A u t o l o a d e r C o n f i g ( ) • i n i t ( ) • o n B o o t s t r a p ( ) • Service Manager methods: • g e t S e r v i c e C o n f i g ( ) • g e t C o n t r o l l e r C o n f i g ( ) • g e t C o n t r o l l e r P l u g i n C o n f i g ( ) • g e t V i e w H e l p e r C o n f i g ( )
Other actions • If L o c a t o r R e g i s t e r e d I n t e r f a c e is implemented, then register with the service manager. • All configs are merged together: 1. g e t C o n f i g ( ) results merged in the order modules are loaded. 2. Config glob/static paths are merged. 3. The g e t S e r v i c e C o n f i g ( ) (and friends) results are merged together then merged with the result of steps 1 and 2.
A typical Module class 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 } }
Module best practices • Keep i n i t ( ) and o n B o o t s t r a p ( ) very lightweight. • Read-only ( do not perform writes within modules ). • Utilize a vendor prefix (e.g., E d p M a r k d o w n , not M a r k d o w n ). • Do one thing, and do it well.
RESTful ZF2 Putting REST & ZF2 together
Foundations • Routing • 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 • Reacting to request headers • Creating hypermedia payloads • Creating error payloads
Routing • Route to an 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 implementation • Allows a single route to manage all HTTP methods for a given resource • Use a combination of Literal and/or Segment routes
Sample Route ' 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 } ' , ) , ) , ) ,
AbstractRestfulController • Maps HTTP methods to individual class methods • Performs basic content-negotiation ( 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 )
Mapping methods • G E T :: g e t L i s t ( ) or g e t ( $ i d ) • P O S T :: c r e a t e ( $ d a t a ) • P U T :: r e p l a c e L i s t ( ) , u p d a t e ( $ i d , $ d a t a ) • P A T C H :: p a t c h ( $ i d , $ d a t a ) • D E L E T E :: d e l e t e L i s t ( ) , d e l e t e ( $ i d ) • H E A D :: h e a d ( $ i d = n u l l ) • O P T I O N S :: o p t i o n s ( )
Selecting an acceptable view model • Select a view model based on A c c e p t • Attach a view strategy based on view model
AcceptableViewModelSelector • Controller plugin $ 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 ) ;
Changing view strategy based on model • Listen on the controller’s dispatch event $ 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 - 1 0 ) ;
Sample listener 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 ) ; } ,
Directly examining the Accept header $ 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 ; }
Hypermedia payloads • Links should be fully qualified: include, scheme, server, and port if necessary • A s e l f relation is recommended • With paginated sets, include f i r s t , l a s t , n e x t , and p r e v relations
Tools for creating links • The u r l controller plugin and/or view helper can generate the path if a route is known. • The s e r v e r U r l view helper can generate the scheme/server/port combination • Paginators can be inspected and used to generate pagination relations
Generating individual links $ 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 ) ;
Generating paginated links / / $ 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 ; }
cont… 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 ; }
Where to generate links • Controller is easiest, but may not be semantically correct • View model makes sense, but is hard to inject with helpers • Renderer makes sense, but likely requires specialized payloads in the view model • A event listener could process the view model and inject them; similar issues to the renderer, though. • Choose your poison.
Error payloads • Be consistent • Provide detail • a p p l i c a t i o n / a p i - p r o b l e m + j s o n is a nice standard
API-Problem payloads • d e s c r i b e d b y is required. If corresponding to HTTP status, http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html describing HTTP status codes is a nice default. • t i t l e is also required; again, if corresponding to HTTP status, use established status descriptions. • h t t p S t a t u s is not required, but recommended. • d e t a i l is your place to provide any additional information.
Where to generate API-Problem payloads • Typically, within the controller; this is where the errors happen. • You may also want listeners on d i s p a t c h . e r r o r so you can generate 404 responses in this format.
Practical application • YOU will build a simple “status” API for posting social status • “text” representing the status • “user” representing the user posting the status • “timestamp” when the status was created • Collection of statuses by user, in reverse chronological order • User is present in the URI
Steps • Create the domain logic (this is the hard part) • Create a route • Create a controller that: • calls on the domain logic • varies the view model based on the A c c e p t header • creates API-Problem payloads for errors • Create a listener for injecting hypermedia links in the view model
Route • / s t a t u s / : u s e r [ / : i d ]
Controller • Extend 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 • use A c c e p t a b l e V i e w M o d e l S e l e c t o r to pull a relevant view model based on A c c e p t header; create a special view model type that we can listen for later. • set specific variables in the view that we can query later • use a special object for indicating errors • set appropriate HTTP status codes
Listener • Listen for our special view model type • If an error is detected: • Create an API-Problem payload • Set the response status code • Generate hyperlinks based on whether we have a collection or an individual item.
Demonstration This is meant to be alive demo of the finished API, and maybe some code samples.
PhlyRestfully • Module that does these bits for you • Add it to composer • “phly/phly-restfully”: “dev-master@dev” • Provide a resource listener that does the various persistence related operations and a route, and go. test
Review What have we learnt today?
Review • REST is an architecture, with lots of recommendations but no single, canonical methodology • Don’t skimp or skip the documentation!
Review • REST has lots of little details to pay attention to: • URIs per resource • HTTP methods indicating the operations available for a resource • Media types indicating resource representations govern how to parse a request as well as how to format a response • Hypermedia links to promote discoverability and available state changes
Review • Several emerging standards surrounding specifically RESTful JSON APIs • Collection+JSON • Hypertext Application Language (HAL) • API-Problem
Review • ZF2 has a lot of built-in features to help build RESTful applications • 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 • A c c e p t header implementation • Rich HTTP tooling in general • Flexible view layer
Recommend
More recommend