Upgrade to Pro — share decks privately, control downloads, hide ads and more …

The quickest way to stable API in Laravel

The quickest way to stable API in Laravel

Builiding API using great combination of tools and workflows how we can vastly speedup and improve our API development, without compromising stability and testability.

Milan Popović

March 23, 2016
Tweet

More Decks by Milan Popović

Other Decks in Programming

Transcript

  1. ME PHP developer Work for Navus Consulting Gmbh I like

    to learn and share knowledge Active member of PHP Srbija
  2. Why Laravel? Enables rapid development Stable and well tested Community

    support Documentation Learning resources Easy to recruit developers LTS
  3. Why Laravel for quick API development? DB Migration DB seeding

    Model factory Form request Testing 3rd Party Packages
  4. DB MIGRATION Version control for you DB schema Laravel Schema

    facade provides database agnostic support for creating and manipulating tables / / C r e a t e n e w m i g r a t i o n p h p a r t i s a n m a k e : m i g r a t i o n c r e a t e _ u s e r s _ t a b l e / / R u n m i g r a t i o n p h p a r t i s a n m i g r a t e / / R o l l b a c k m i g r a t i o n p h p a r t i s a n m i g r a t e
  5. DB SEED Sample/Test data for your DB / / C

    r e a t e n e w s e e d e r p h p a r t i s a n m a k e : s e e d e r U s e r s T a b l e S e e d e r / / R u n s e e d e r s p h p a r t i s a n d b : s e e d / / R u n s e e d e r s v 2 p h p a r t i s a n m i g r a t e - - s e e d / / R u n s i n g l e s e e d e r p h p a r t i s a n d b : s e e d - - c l a s s = U s e r s T a b l e S e e d e r
  6. MODEL FACTORY Default model attributes Uses Faker for generating values

    $ f a c t o r y - > d e f i n e ( A p p \ U s e r : : c l a s s , f u n c t i o n ( F a k e r \ G e n e r a t o r $ f a k e r ) { r e t u r n [ ' n a m e ' = > $ f a k e r - > n a m e , ' e m a i l ' = > $ f a k e r - > e m a i l , ' p a s s w o r d ' = > b c r y p t ( s t r _ r a n d o m ( 1 0 ) ) , ' r e m e m b e r _ t o k e n ' = > s t r _ r a n d o m ( 1 0 ) , ] ; } ) ;
  7. Multiple Factory Types - multiple factories for the same Eloquent

    model class $ f a c t o r y - > d e f i n e A s ( A p p \ U s e r : : c l a s s , ' a d m i n ' , f u n c t i o n ( $ f a k e r ) u s e ( $ f a c t o r y $ u s e r = $ f a c t o r y - > r a w ( A p p \ U s e r : : c l a s s ) ; r e t u r n a r r a y _ m e r g e ( $ u s e r , [ ' a d m i n ' = > t r u e ] ) ; } ) ;
  8. Attach relationships $ f a c t o r y

    - > d e f i n e ( A p p \ P o s t : : c l a s s , f u n c t i o n ( $ f a k e r ) { r e t u r n [ ' t i t l e ' = > $ f a k e r - > t i t l e , ' c o n t e n t ' = > $ f a k e r - > p a r a g r a p h , ' u s e r _ i d ' = > f u n c t i o n ( ) { r e t u r n f a c t o r y ( A p p \ U s e r : : c l a s s ) - > c r e a t e ( ) - > i d ; } , ' u s e r _ t y p e ' = > f u n c t i o n ( a r r a y $ p o s t ) { r e t u r n A p p \ U s e r : : f i n d ( $ p o s t [ ' u s e r _ i d ' ] ) - > t y p e ; } ] ; } ) ;
  9. Using factories / / C r e a t e

    t h r e e A p p \ U s e r i n s t a n c e s . . . $ u s e r s = f a c t o r y ( A p p \ U s e r : : c l a s s , 3 ) - > m a k e ( ) ; / / C r e a t e t h r e e A p p \ U s e r " a d m i n " i n s t a n c e s . . . $ u s e r s = f a c t o r y ( A p p \ U s e r : : c l a s s , ' a d m i n ' , 3 ) - > m a k e ( ) ; / / C r e a t e A p p \ U s e r u s e r w i t h g i v e n n a m e . . . $ u s e r = f a c t o r y ( A p p \ U s e r : : c l a s s ) - > m a k e ( [ ' n a m e ' = > ' A b i g a i l ' , ] ) ; / / P e r s i s t i n g F a c t o r y M o d e l s $ u s e r = f a c t o r y ( A p p \ U s e r : : c l a s s ) - > c r e a t e ( ) ;
  10. FORM REQUESTS Special class for validating request params and authorizing

    Each class has rules() (returns array) and authorize() (returns boolean) methods Type Hinting in Controller methods
  11. p u b l i c f u n c

    t i o n r u l e s ( ) { r e t u r n [ ' t i t l e ' = > ' r e q u i r e d | u n i q u e : p o s t s | m a x : 2 5 5 ' , ' b o d y ' = > ' r e q u i r e d ' , ] ; } p u b l i c f u n c t i o n a u t h o r i z e ( ) { $ c o m m e n t I d = $ t h i s - > r o u t e ( ' c o m m e n t ' ) ; r e t u r n C o m m e n t : : w h e r e ( ' i d ' , $ c o m m e n t I d ) - > w h e r e ( ' u s e r _ i d ' , A u t h : : i d ( ) ) - > e x i s t s ( ) ; }
  12. TESTING PHPUnit is included out of the box Laravel is

    built with testing in mind It's also ships with convenient helper methods Uses Mockery as mock object framework
  13. WORKING WITH DATABASE SeeInDatabase helper seeInDatabase($table, array $data, $connection =

    null) NotSeeInDatabase helper notSeeInDatabase($table, array $data, $connection = null) Seed (seed($class = 'DatabaseSeeder')
  14. CREATING HTTP REQUEST / / M a i n m

    e t h o d p u b l i c f u n c t i o n c a l l ( $ m e t h o d , $ u r i , $ p a r a m e t e r s = [ ] , $ c o o k i e s = [ ] , $ f i l e s = / / T h e r e a r e a l s o m e t h o d s f o r o t h e r h t t p v e r b s p u b l i c f u n c t i o n p o s t ( $ u r i , a r r a y $ d a t a = [ ] , a r r a y $ h e a d e r s = [ ] ) / / V i s i t t h e g i v e n U R I w i t h a J S O N r e q u e s t . p u b l i c f u n c t i o n j s o n ( $ m e t h o d , $ u r i , a r r a y $ d a t a = [ ] , a r r a y $ h e a d e r s = [ ] ) { / / r e m o v e d c o d e f r o m o r i g i n a l m e t h o d $ c o n t e n t = j s o n _ e n c o d e ( $ d a t a ) ; $ h e a d e r s = a r r a y _ m e r g e ( [ ' C O N T E N T _ L E N G T H ' = > m b _ s t r l e n ( $ c o n t e n t , ' 8 b i t ' ) , ' C O N T E N T _ T Y P E ' = > ' a p p l i c a t i o n / j s o n ' , ' A c c e p t ' = > ' a p p l i c a t i o n / j s o n ' , ] , $ h e a d e r s ) ;
  15. DINGO API " r e q u i r e

    " : { " d i n g o / a p i " : " 1 . 0 . * @ d e v " } Provides set of tools to easily and quickly build your own API Requires Laravel 5.1+ or Lumen 5.1+ and PHP 5.5.9+
  16. CONFIGURATION Much of the package comes preconfigured Use your .env

    file to configure most of the package Finer tuning of the package will require publishing the configuration file
  17. Configure: Name Default Version Authentication Provider Throttling / Rate Limiting

    (Disabled by default) Response Transformer (Fractal is the default) Response Format (JSON and a JSON response format is registered by default) Error Format
  18. CREATING API ENDPOINTS / / T o a v o

    i d c o m p l i c a t i o n s w i t h y o u r m a i n a p p l i c a t i o n r o u t e s t h i s p a c k a g e u t i l $ a p i = a p p ( ' D i n g o \ A p i \ R o u t i n g \ R o u t e r ' ) ; $ a p i - > v e r s i o n ( ' v 1 ' , f u n c t i o n ( $ a p i ) { $ a p i - > p o s t ( ' r e g i s t e r ' , ' S a m p l e A p i \ H t t p \ C o n t r o l l e r s \ R e g i s t e r C o n t r o l l e r $ a p i - > p o s t ( ' l o g i n ' , ' S a m p l e A p i \ H t t p \ C o n t r o l l e r s \ L o g i n C o n t r o l l e r @ l o g i n $ a p i - > g e t ( ' b l o c k s ' , ' S a m p l e A p i \ H t t p \ C o n t r o l l e r s \ B l o c k s C o n t r o l l e r @ g e t A $ a p i - > g e t ( ' b l o c k s / { i d } ' , ' S a m p l e A p i \ H t t p \ C o n t r o l l e r s \ B l o c k s C o n t r o l l e r $ a p i - > p o s t ( ' b l o c k s ' , ' S a m p l e A p i \ H t t p \ C o n t r o l l e r s \ B l o c k s C o n t r o l l e r @ c r e $ a p i - > p u t ( ' b l o c k s / { i d } ' , ' S a m p l e A p i \ H t t p \ C o n t r o l l e r s \ B l o c k s C o n t r o l l e r $ a p i - > d e l e t e ( ' b l o c k s / { i d } ' , ' S a m p l e A p i \ H t t p \ C o n t r o l l e r s \ B l o c k s C o n t r o l } ) ;
  19. RESPONSES There's a number of different ways to return responses

    Use Response Builder Provides a fluent interface to easily build a more customizable response Generally used in conjunction with transformers. Use the Dingo\Api\Routing\Helpers trait
  20. u s e D i n g o \ A

    p i \ R o u t i n g \ H e l p e r s ; u s e I l l u m i n a t e \ R o u t i n g \ C o n t r o l l e r ; c l a s s A p i C o n t r o l l e r e x t e n d s C o n t r o l l e r { u s e H e l p e r s ; }
  21. RESPONDING WITH AN ARRAY p u b l i c

    f u n c t i o n s h o w ( $ i d ) { $ b l o c k = $ t h i s - > b l o c k - > f i n d ( $ i d ) ; r e t u r n $ t h i s - > r e s p o n s e - > a r r a y ( $ b l o c k - > t o A r r a y ( ) ) ; }
  22. RESPONDING WITH A SINGLE ITEM p u b l i

    c f u n c t i o n s h o w ( $ i d ) { $ b l o c k = $ t h i s - > b l o c k - > f i n d ( $ i d ) ; r e t u r n $ t h i s - > r e s p o n s e - > i t e m ( $ b l o c k , n e w B l o c k T r a n s f o r m e r ) ; }
  23. RESPONDING WITH A COLLECTION OF ITEMS p u b l

    i c f u n c t i o n s h o w ( $ i d ) { $ b l o c k s = $ t h i s - > b l o c k - > f i n d ( $ i d ) ; r e t u r n $ t h i s - > r e s p o n s e - > c o l l e c t i o n ( $ b l o c k s , n e w B l o c k T r a n s f o r m e r ) ; }
  24. RESPONDING WITH PAGINATED ITEMS p u b l i c

    f u n c t i o n s h o w ( $ i d ) { $ b l o c k s = $ t h i s - > b l o c k - > f i n d ( $ i d ) ; r e t u r n $ t h i s - > r e s p o n s e - > p a g i n a t i o n ( $ b l o c k s , n e w B l o c k T r a n s f o r m e r ) ; }
  25. OTHER RESPONSES / / R e s p o n

    d i n g W i t h N o C o n t e n t r e t u r n $ t h i s - > r e s p o n s e - > n o C o n t e n t ( ) ; / / R e s p o n d i n g W i t h C r e a t e d R e s p o n s e r e t u r n $ t h i s - > r e s p o n s e - > c r e a t e d ( ) ; / / A g e n e r i c e r r o r w i t h c u s t o m m e s s a g e a n d s t a t u s c o d e . r e t u r n $ t h i s - > r e s p o n s e - > e r r o r ( ' T h i s i s a n e r r o r . ' , 4 0 4 ) ; / / A n o t f o u n d e r r o r w i t h a n o p t i o n a l m e s s a g e a s t h e f i r s t p a r a m e t e r . r e t u r n $ t h i s - > r e s p o n s e - > e r r o r N o t F o u n d ( ) ; / / T h e r e a r e a l s o e r r o r B a d R e q u e s t , e r r o r F o r b i d d e n , e r r o r I n t e r n a l , e r r o r U n a u t h
  26. Adding Additional Headers r e t u r n $

    t h i s - > r e s p o n s e - > i t e m ( $ u s e r , n e w U s e r T r a n s f o r m e r ) - > w i t h H e a d e r ( ' X - F o o ' Adding Meta Data r e t u r n $ t h i s - > r e s p o n s e - > i t e m ( $ u s e r , n e w U s e r T r a n s f o r m e r ) - > a d d M e t a ( ' f o o ' / / s e t a n a r r a y o f m e t a d a t a i n s t e a d o f c h a i n i n g m u l t i p l e m e t h o d c a l l s r e t u r n $ t h i s - > r e s p o n s e - > i t e m ( $ u s e r , n e w U s e r T r a n s f o r m e r ) - > s e t M e t a ( $ m e t a Setting Response Status Code r e t u r n $ t h i s - > r e s p o n s e - > i t e m ( $ u s e r , n e w U s e r T r a n s f o r m e r ) - > s e t S t a t u s C o d e
  27. TRANSFORMERS Easily and consistently transform objects into an array Type-cast

    integers and booleans, include pagination results, and nest relationships Fractal is the default transformation layer used by Dingo
  28. 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 - > a v a i l a b l e I n c l u d e s = [ ' p r e s e n t a t i o n s ' , ' b l o c k _ s e t t i n g s ' , ] ; $ t h i s - > d e f a u l t I n c l u d e s = [ ' b l o c k _ s e t t i n g s ' , ] ; } p u b l i c f u n c t i o n t r a n s f o r m ( B l o c k $ b l o c k ) { r e t u r n [ ' i d ' = > ( i n t ) $ b l o c k - > i d , ' n a m e ' = > $ b l o c k - > n a m e ,
  29. FORM REQUEST END ERROR RESPONSES Extend the base API form

    request class Implement your own class Dingo\Api\Exception\ValidationHttpException should be thrown
  30. “ Dredd is a language-agnostic command-line tool for validating API

    documentation written in API Blueprint format against its backend implementation. ” Dredd reads your API description Validates whether API replies with expected responses Continuous Integration Support Hooks — a glue code for each test setup and teardown Use of Gavel.js library Do not install dredd manually on Homestead - use a er.sh
  31. HEADERS EXPECTATIONS All headers given in example must be present

    in the response Only values of headers significant for content negotiation are validated All other headers values can differ
  32. BODY EXPECTATIONS All JSON keys on any level given in

    the example must be present in the response JSON Response JSON values must be of the same JSON primitive type All JSON values can differ Arrays can have additional items, type or structure is not validated. Plain text must match perfectly
  33. PHP Dredd hook handler Provides a bridge between the Dredd

    API Testing Framework and PHP environment " r e q u i r e - d e v " : { " d d e l n a n o / d r e d d - h o o k s - p h p " : " ~ 1 . 0 . 0 " , }
  34. HOOKS USAGE Loading db fixtures Cleanup a er test step

    or steps Handling authentication and sessions Passing data between transactions (saving state from responses to stash) Modifying request generated from blueprint Changing generated expectations Setting custom expectations Debugging via logging stuff
  35. HOOKS TYPE beforeEach a erEach beforeAll a erAll before a

    er beforeEachValidationHooks a erEachValidationHooks
  36. r e q u i r e _ _ D

    I R _ _ . ' / . . / . . / . . / v e n d o r / a u t o l o a d . p h p ' ; $ a p p = r e q u i r e _ _ D I R _ _ . ' / . . / . . / . . / b o o t s t r a p / a p p . p h p ' ; $ a p p - > m a k e ( \ I l l u m i n a t e \ C o n t r a c t s \ C o n s o l e \ K e r n e l : : c l a s s ) - > b o o t s t r a p ( ) ; A r t i s a n : : c a l l ( ' m i g r a t e ' ) ; $ u s e r = f a c t o r y ( \ E m a t e r i a l s \ A p i \ M o d e l s \ U s e r : : c l a s s ) - > c r e a t e ( [ ' e m a i l ' = > ' k n o w n u s e r @ e x a m p l e . d e v ' ] ) ; H o o k s : : b e f o r e E a c h ( f u n c t i o n ( & $ t r a n s a c t i o n ) u s e ( $ a p p ) { $ a p p - > m a k e ( ' d b ' ) - > b e g i n T r a n s a c t i o n ( ) ; } ) ; H o o k s : : a f t e r E a c h ( f u n c t i o n ( & $ t r a n s a c t i o n ) u s e ( $ a p p ) {
  37. RUN DREDD d r e d d d o c

    u m e n t a t i o n . a p i b h t t p : / / l o c a l h o s t : 8 0 0 1 - - s e r v e r " p h p - S 0 . 0 . 0 . 0 : 8 0 0 1 - t p u b l i c / " - - l a n g u a g e v e n d o r / b i n / d r e d d - h o o k s - p h p - - h o o k f i l e s t e s t s / d r e d d / h o o k s / h o o k f i l e . p h p
  38. REPOSITORIES " r e q u i r e "

    : { " b o s n a d e v / r e p o s i t o r i e s " : " 0 . * " } Container where data access logic is stored Hides the details of data access logic from business logic
  39. i n t e r f a c e R

    e p o s i 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 a l l ( $ c o l u m n s = a r r a y ( ' * ' ) ) ; p u b l i c f u n c t i o n p a g i n a t e ( $ p e r P a g e = 1 5 , $ c o l u m n s = a r r a y ( ' * ' ) ) ; p u b l i c f u n c t i o n c r e a t e ( a r r a y $ d a t a ) ; p u b l i c f u n c t i o n u p d a t e ( a r r a y $ d a t a , $ i d ) ; p u b l i c f u n c t i o n d e l e t e ( $ i d ) ; p u b l i c f u n c t i o n f i n d ( $ i d , $ c o l u m n s = a r r a y ( ' * ' ) ) ; p u b l i c f u n c t i o n f i n d B y ( $ f i e l d , $ v a l u e , $ c o l u m n s = a r r a y ( ' * ' ) ) ; }
  40. u s e A p p \ R e p

    o s i t o r i e s \ F i l m R e p o s i t o r y a s F i l m ; c l a s s F i l m s C o n t r o l l e r e x t e n d s C o n t r o l l e r { / * * * @ v a r F i l m * / p r i v a t e $ f i l m ; p u b l i c f u n c t i o n _ _ c o n s t r u c t ( F i l m $ f i l m ) { $ t h i s - > f i l m = $ f i l m ; } p u b l i c f u n c t i o n i n d e x ( ) { r e t u r n \ R e s p o n s e : : j s o n ( $ t h i s - > f i l m - > a l l ( ) ) ;
  41. Basic actions are just enough for simple querying Added CriteriaInterface

    i n t e r f a c e C r i t e r i a I n t e r f a c e { p u b l i c f u n c t i o n s k i p C r i t e r i a ( $ s t a t u s = t r u e ) ; p u b l i c f u n c t i o n g e t C r i t e r i a ( ) ; p u b l i c f u n c t i o n g e t B y C r i t e r i a ( C r i t e r i a $ c r i t e r i a ) ; p u b l i c f u n c t i o n p u s h C r i t e r i a ( C r i t e r i a $ c r i t e r i a ) ; p u b l i c f u n c t i o n a p p l y C r i t e r i a ( ) ; }
  42. c l a s s L e n g t

    h O v e r T w o H o u r s i m p l e m e n t s C r i t e r i a I n t e r f a c e { p u b l i c f u n c t i o n a p p l y ( $ m o d e l , R e p o s i t o r y $ r e p o s i t o r y ) { $ q u e r y = $ m o d e l - > w h e r e ( ' l e n g t h ' , ' > ' , 1 2 0 ) ; r e t u r n $ q u e r y ; } }
  43. u s e A p p \ R e p

    o s i t o r i e s \ C r i t e r i a \ F i l m s \ L e n g t h O v e r T w o H o u r s ; u s e A p p \ R e p o s i t o r i e s \ F i l m R e p o s i t o r y a s F i l m ; c l a s s F i l m s C o n t r o l l e r e x t e n d s C o n t r o l l e r { / * * * @ v a r F i l m * / p r i v a t e $ f i l m ; p u b l i c f u n c t i o n _ _ c o n s t r u c t ( F i l m $ f i l m ) { $ t h i s - > f i l m = $ f i l m ; } p u b l i c f u n c t i o n i n d e x ( ) {
  44. CORS (CROSS-ORIGIN RESOURCE SHARING) " r e q u i

    r e " : { " b a r r y v d h / l a r a v e l - c o r s " : " ^ 0 . 7 . 2 " } Allows you to send CORS headers with ACL-style per-url configuration. Supports Laravel & Lumen