Building Websites with Zend Expressive 3

A presentation at PHP UK Conference 2018 in February 2018 in London, UK by Rob Allen

Slide 1

Slide 1

Building Websites with Zend Expressive 3 Rob Allen, Nineteen Feet     February 2018 ~ @akrabat

Slide 2

Slide 2

A microframework with full stack components Rob Allen ~ @akrabat

Slide 3

Slide 3

µ Framework core • Router • Container • Template renderer • Error handler • Configuration Rob Allen ~ @akrabat

Slide 4

Slide 4

Ecosystem • Filtering and validation • API rendering: HAL & Problem-API • Database abstraction • Session handling • Logging • Mail • Pagination • Caching Rob Allen ~ @akrabat

Slide 5

Slide 5

Agnostic Router: FastRoute, Aura.Router or Zend Router DI Container: Aura.Di, Auryn, Pimple, Symfony DI Container or Zend-ServiceManager Template: Plates, Twig or Zend View Rob Allen ~ @akrabat

Slide 6

Slide 6

Middleware pipeline   Rob Allen ~ @akrabat

Slide 7

Slide 7

PSR-15 MiddlewareInterface namespace   Psr\Http\Server ; use   Psr\Http\Message\ResponseInterface ; use   Psr\Http\Message\ServerRequestInterface ; interface   MiddlewareInterface {      public   function   process (          ServerRequestInterface   $request ,          RequestHandlerInterface   $handler      )   :   ResponseInterface ; } Rob Allen ~ @akrabat

Slide 8

Slide 8

PSR-15 MiddlewareInterface namespace   Psr\Http\Server ; use   Psr\Http\Message\ResponseInterface ; use   Psr\Http\Message\ServerRequestInterface ; interface   MiddlewareInterface {      public   function   process (          ServerRequestInterface   $request ,          RequestHandlerInterface   $handler      )   :   ResponseInterface ; } Rob Allen ~ @akrabat

Slide 9

Slide 9

PSR-15 MiddlewareInterface namespace   Psr\Http\Server ; use   Psr\Http\Message\ResponseInterface ; use   Psr\Http\Message\ServerRequestInterface ; interface   MiddlewareInterface {      public   function   process (          ServerRequestInterface   $request ,          RequestHandlerInterface   $handler      )   :   ResponseInterface ; } Rob Allen ~ @akrabat

Slide 10

Slide 10

PSR-15 MiddlewareInterface namespace   Psr\Http\Server ; use   Psr\Http\Message\ResponseInterface ; use   Psr\Http\Message\ServerRequestInterface ; interface   MiddlewareInterface {      public   function   process (          ServerRequestInterface   $request ,          RequestHandlerInterface   $handler      )   :   ResponseInterface ; } Rob Allen ~ @akrabat

Slide 11

Slide 11

Structure of middleware class   TimerMiddleware   implements   MiddlewareInterface {      public   function   process ( $request ,   $handler )      {          $start  

  microtime ( true );          $response  

  $handler

handle ( $request );          $taken  

  microtime ( true )  

  $start ;          $response

getBody ()

write ( " < !-- Time:  $taken  --

" );          return   $response ;      } } Rob Allen ~ @akrabat

Slide 12

Slide 12

Structure of middleware class   TimerMiddleware   implements   MiddlewareInterface {      public   function   process ( $request ,   $handler )      {          $start  

  microtime ( true );          $response  

  $handler

handle ( $request );          $taken  

  microtime ( true )  

  $start ;          $response

getBody ()

write ( " < !-- Time:  $taken  --

" );          return   $response ;      } } Rob Allen ~ @akrabat

Slide 13

Slide 13

Structure of middleware class   TimerMiddleware   implements   MiddlewareInterface {      public   function   process ( $request ,   $handler )      {          $start  

  microtime ( true );          $response  

  $handler

handle ( $request );          $taken  

  microtime ( true )  

  $start ;          $response

getBody ()

write ( " < !-- Time:  $taken  --

" );          return   $response ;      } } Rob Allen ~ @akrabat

Slide 14

Slide 14

Structure of middleware class   TimerMiddleware   implements   MiddlewareInterface {      public   function   process ( $request ,   $handler )      {          $start  

  microtime ( true );          $response  

  $handler

handle ( $request );          $taken  

  microtime ( true )  

  $start ;          $response

getBody ()

write ( " < !-- Time:  $taken  --

" );          return   $response ;      } } Rob Allen ~ @akrabat

Slide 15

Slide 15

Structure of middleware class   TimerMiddleware   implements   MiddlewareInterface {      public   function   process ( $request ,   $handler )      {          $start  

  microtime ( true );          $response  

  $handler

handle ( $request );          $taken  

  microtime ( true )  

  $start ;          $response

getBody ()

write ( " < !-- Time:  $taken  --

" );          return   $response ;      } } Rob Allen ~ @akrabat

Slide 16

Slide 16

Getting started Rob Allen ~ @akrabat

Slide 17

Slide 17

Skeleton $ composer create-project zendframework/zend-expressive-skeleton new-app Rob Allen ~ @akrabat

Slide 18

Slide 18

Skeleton $ composer create-project zendframework/zend-expressive-skeleton new-app Rob Allen ~ @akrabat

Slide 19

Slide 19

Skeleton Rob Allen ~ @akrabat

Slide 20

Slide 20

Directory structure . ├── bin/                                     ├── data/ ├── config/                                  ├── public/ │   ├── autoload/                            │   ├── css/ │   │   ├── dependencies.global.php          │   ├── js/ │   │   ├── development.local.php            │   └── index.php │   │   ├── development.local.php.dist       ├── src/ │   │   ├── local.php.dist                   │   └── App/ │   │   ├── router.global.php                ├── test/ │   │   ├── templates.global.php             │   └── AppTest/ │   │   └── zend-expressive.global.php       ├── vendor/ │   ├── config.php                           ├── composer.json │   ├── container.php                        ├── composer.lock │   ├── development.config.php               ├── phpcs.xml.dist │   ├── development.config.php.dist          └── phpunit.xml.dist │   ├── pipeline.php │   └── routes.php Rob Allen ~ @akrabat

Slide 21

Slide 21

src/App directory • Each module lives in its own namespace • Contains all code for application • ConfigProvider class for initialisation • Configuration • DI registration Rob Allen ~ @akrabat

Slide 22

Slide 22

src/App directory structure └── src/     └── App/         ├── src/         │   ├── Handler/         │   │   ├── HomePageFactory.php         │   │   ├── HomePageHandler.php         │   │   └── PingHandler.php         │   └── ConfigProvider.php         ├── templates/         │   ├── app/         │   │   └── home-page.html.twig         │   ├── error/         │   │   ├── 404.html.twig         │   │   └── error.html.twig         │   └── layout/         │       └── default.html.twig         └── test/             └── AppTest/                 └── Handler/ Rob Allen ~ @akrabat

Slide 23

Slide 23

A handler (AKA: an action) namespace   App\Handler ; use   ... ; class   HomePageHandler   implements   RequestHandlerInterface {      public   function   handle (          ServerRequestInterface   $request      )   :   ResponseInterface   {          return   new   HtmlResponse ( ' < p

Hello World < /p

' );      } } Rob Allen ~ @akrabat

Slide 24

Slide 24

Let's write a web page! Rob Allen ~ @akrabat

Slide 25

Slide 25

Bitcoin conversion   Create a page that displays the current value of 1 Bitcoin in £ , $ & € Rob Allen ~ @akrabat

Slide 26

Slide 26

Create a route config/routes.php: $app

get (      '/bitcoin' ,      App\Handler\BitcoinPageHandler :: class ,      'bitcoin' ); Rob Allen ~ @akrabat

Slide 27

Slide 27

Routes have a method config/routes.php: $app

get (      '/bitcoin' ,      App\Handler\BitcoinPageHandler :: class ,      'bitcoin' ); Rob Allen ~ @akrabat

Slide 28

Slide 28

HTTP Method $app-

get() $app-

post() $app-

put() $app-

patch() $app-

delete() Multiple methods: $app-

any() $app-

route(…, …, ['GET', 'POST'], …); Rob Allen ~ @akrabat

Slide 29

Slide 29

Routes have a pattern config/routes.php: $app

get (      '/bitcoin' ,      App\Handler\BitcoinPageHandler :: class ,      'bitcoin' ); Rob Allen ~ @akrabat

Slide 30

Slide 30

FastRoute URI pattern • Literal string match $app

get ( '/hello' ,   … ); Rob Allen ~ @akrabat

Slide 31

Slide 31

FastRoute URI pattern • Literal string match $app

get ( '/hello' ,   … ); • Placeholders are wrapped in {

} $app

get ( '/hello/{name}' ,   … ); Rob Allen ~ @akrabat

Slide 32

Slide 32

FastRoute URI pattern • Literal string match $app

get ( '/hello' ,   … ); • Placeholders are wrapped in {

} $app

get ( '/hello/{name}' ,   … ); • Optional segments are wrapped with [

] $app

get ( '/news[/{year}[/{month}]]' ,   … ); Rob Allen ~ @akrabat

Slide 33

Slide 33

FastRoute URI pattern • Literal string match $app

get ( '/hello' ,   … ); • Placeholders are wrapped in {

} $app

get ( '/hello/{name}' ,   … ); • Optional segments are wrapped with [

] $app

get ( '/news[/{year}[/{month}]]' ,   … ); • Constrain placeholders via Regex $app

get ( '/news/{year:\d{4}}}' ,   … );   // exactly 4 digits Rob Allen ~ @akrabat

Slide 34

Slide 34

Routes have a name config/routes.php: $app

get (      '/bitcoin' ,      App\Handler\BitcoinPageHandler :: class ,      'bitcoin' ); Rob Allen ~ @akrabat

Slide 35

Slide 35

Url helper • Builds URLs from route names • Can be helpful to use . to group related route names Use the router :    $router

generateUri ( 'user.profile' ,   [ 'name'  

  'Rob' ]); or in the template:    {{   path ( 'user.profile' ,   { 'name' :   'Rob' })   }} Rob Allen ~ @akrabat

Slide 36

Slide 36

Routes have a handler config/routes.php: $app

get (      '/bitcoin' ,      App\Handler\BitcoinPageHandler :: class ,      'bitcoin' ); Rob Allen ~ @akrabat

Slide 37

Slide 37

Handlers • Receive a PSR-7 Request • Manage business logic operations • Must return a PSR-7 Response • Implemented as PSR-15 RequestHandler • Create using CLI tool: $ composer expressive handler:create 
  App\Handler\BitcoinPageHandler Rob Allen ~ @akrabat

Slide 38

Slide 38

Bitcoin handler class   BitcoinPageHandler   implements   RequestHandlerInterface {    public   function   handle (      ServerRequestInterface   $request    )   :   ResponseInterface   {      $data [ 'prices' ]  

  $this

btcService

getCurrentPrices ();      return   new   HtmlResponse (        $this

template

render ( 'app::bitcoin-page' ,   $data )      );    } Rob Allen ~ @akrabat

Slide 39

Slide 39

Bitcoin handler class   BitcoinPageHandler   implements   RequestHandlerInterface {    public   function   handle (      ServerRequestInterface   $request    )   :   ResponseInterface   {      $data [ 'prices' ]  

  $this

btcService

getCurrentPrices ();      return   new   HtmlResponse (        $this

template

render ( 'app::bitcoin-page' ,   $data )      );    } Rob Allen ~ @akrabat

Slide 40

Slide 40

Bitcoin handler class   BitcoinPageHandler   implements   RequestHandlerInterface {    public   function   handle (      ServerRequestInterface   $request    )   :   ResponseInterface   {      $data [ 'prices' ]  

  $this

btcService

getCurrentPrices ();      return   new   HtmlResponse (        $this

template

render ( 'app::bitcoin-page' ,   $data )      );    } Rob Allen ~ @akrabat

Slide 41

Slide 41

Bitcoin handler class   BitcoinPageHandler   implements   RequestHandlerInterface {    public   function   handle (      ServerRequestInterface   $request    )   :   ResponseInterface   {      $data [ 'prices' ]  

  $this

btcService

getCurrentPrices ();      return   new   HtmlResponse (        $this

template

render ( 'app::bitcoin-page' ,   $data )      );    } Rob Allen ~ @akrabat

Slide 42

Slide 42

Injecting dependencies class   BitcoinPageHandler   implements   RequestHandlerInterface {    protected   $template ;    protected   $btcService ;    public   function   __construct (      TemplateRendererInterface   $template ,      BitcoinService   $btcService    )   {        $this

template  

  $template ;        $this

btcService  

  $btcService ;    }    // … Rob Allen ~ @akrabat

Slide 43

Slide 43

Expressive configuration A mushed-up associative array created from: • Invoked ConfigProvider classes from libs and modules • Config files in config/autoload/ Common top level keys: • dependencies • templates • twig • filters • validators • db • cache Rob Allen ~ @akrabat

Slide 44

Slide 44

ConfigProvider namespace   App ; class   ConfigProvider {      public   function   __invoke ()   :   array      {          return   [              'dependencies'  

  $this

getDependencies (),              'templates'     

  $this

getTemplates (),          ];      } Rob Allen ~ @akrabat

Slide 45

Slide 45

ConfigProvider namespace   App ; class   ConfigProvider {      public   function   __invoke ()   :   array      {          return   [              'dependencies'  

  $this

getDependencies (),              'templates'     

  $this

getTemplates (),          ];      } Rob Allen ~ @akrabat

Slide 46

Slide 46

Dependency configuration      public   function   getDependencies ()   :   array      {          return   [              'factories'   

  [                  Handler\BitcoinPageHandler :: class  

                     Handler\BitcoinPageFactory :: class ,              ],          ];      } Rob Allen ~ @akrabat

Slide 47

Slide 47

Dependency configuration      public   function   getDependencies ()   :   array      {          return   [              'factories'   

  [                  Handler\BitcoinPageHandler :: class  

                     Handler\BitcoinPageFactory :: class ,              ],          ];      } Rob Allen ~ @akrabat

Slide 48

Slide 48

Dependency configuration      public   function   getDependencies ()   :   array      {          return   [              'factories'   

  [                  Handler\BitcoinPageHandler :: class  

                     Handler\BitcoinPageFactory :: class ,              ],          ];      } Rob Allen ~ @akrabat

Slide 49

Slide 49

Handler factory Return an instance of the handler with its injected dependencies class   BitcoinPageFactory {      public   function   __invoke ( $container )      {          return   new   BitcoinPageHandler (              $container

get ( TemplateRendererInterface :: class ),              $container

get ( BitcoinService :: class )          );      } } Rob Allen ~ @akrabat

Slide 50

Slide 50

Handler factory Return an instance of the handler with its injected dependencies class   BitcoinPageFactory {      public   function   __invoke ( $container )      {          return   new   BitcoinPageHandler (              $container

get ( TemplateRendererInterface :: class ),              $container

get ( BitcoinService :: class )          );      } } Rob Allen ~ @akrabat

Slide 51

Slide 51

Handler factory Return an instance of the handler with its injected dependencies class   BitcoinPageFactory {      public   function   __invoke ( $container )      {          return   new   BitcoinPageHandler (              $container

get ( TemplateRendererInterface :: class ),              $container

get ( BitcoinService :: class )          );      } } Rob Allen ~ @akrabat

Slide 52

Slide 52

Templates Expressive's view layer Rob Allen ~ @akrabat

Slide 53

Slide 53

Templating • Render method to convert page: $html  

  $this

template

render ( 'app::bitcoin-page' ,   $data ); • Templates are namespaced: namespace::template • Extension is resolved by the adapter: app::bitcoin-page =

 app/bitcoin-page.html.twig Rob Allen ~ @akrabat

Slide 54

Slide 54

Twig templates "Twig is a modern template engine for PHP" • Manual: https://twig.symfony.com • Script extension: .twig • Variables: {{   }} • Control statements: {%   %} • Comments: {#   #} Rob Allen ~ @akrabat

Slide 55

Slide 55

Action template {%   extends   '@layout/default.html.twig'   %} {%   block   title   %} Bitcoin converter {%   endblock   %} {%   block   content   %}      < h1

Bitcoin converter < / h1

     < p

One BTC: < / p

     {%   for   price   in   prices   %}          {{   price.symbol   }}          {{   price.rate_float | number_format ( 2 ,   '.' ,   ',' )   }} < br

     {%   endfor   %} {%   endblock   %} Rob Allen ~ @akrabat

Slide 56

Slide 56

Print variables {%   extends   '@layout/default.html.twig'   %} {%   block   title   %} Bitcoin converter {%   endblock   %} {%   block   content   %}      < h1

Bitcoin converter < / h1

     < p

One BTC: < / p

     {%   for   price   in   prices   %}          {{   price.symbol   }}          {{   price.rate_float | number_format ( 2 ,   '.' ,   ',' )   }} < br

     {%   endfor   %} {%   endblock   %} Rob Allen ~ @akrabat

Slide 57

Slide 57

Control statements {%   extends   '@layout/default.html.twig'   %} {%   block   title   %} Bitcoin converter {%   endblock   %} {%   block   content   %}      < h1

Bitcoin converter < / h1

     < p

One BTC: < / p

     {%   for   price   in   prices   %}          {{   price.symbol   }}          {{   price.rate_float | number_format ( 2 ,   '.' ,   ',' )   }} < br

     {%   endfor   %} {%   endblock   %} Rob Allen ~ @akrabat

Slide 58

Slide 58

Template inheritance {%   extends   '@layout/default.html.twig'   %} {%   block   title   %} Bitcoin converter {%   endblock   %} {%   block   content   %}      < h1

Bitcoin converter < / h1

     < p

One BTC: < / p

     {%   for   price   in   prices   %}          {{   price.symbol   }}          {{   price.rate_float | number_format ( 2 ,   '.' ,   ',' )   }} < br

     {%   endfor   %} {%   endblock   %} Rob Allen ~ @akrabat

Slide 59

Slide 59

Template inheritance • For cohesive look and feel • includes default CSS, JS & structural HTML • Build a base skeleton • Define blocks for children to override • Each child chooses which template to inherit from Rob Allen ~ @akrabat

Slide 60

Slide 60

Base skeleton src/App/templates/layout/default.html.twig: < !DOCTYPE html

< html

   < head

     {%   block   head   %}          < link   rel

"stylesheet"   href

"style.css"   /

         < title

{%   block   title   %}{%   endblock   %}  - Akrabat < / title

     {%   endblock   %}    < / head

   < body

     {%   block   content   %}{%   endblock   %}      < footer

{%   block   footer   %} & copy;  2017 {%   endblock   %} < / footer

   < / body

< / html

Rob Allen ~ @akrabat

Slide 61

Slide 61

Base skeleton src/App/templates/layout/default.html.twig: < !DOCTYPE html

< html

   < head

     {%   block   head   %}          < link   rel

"stylesheet"   href

"style.css"   /

         < title

{%   block   title   %}{%   endblock   %}  - Akrabat < / title

     {%   endblock   %}    < / head

   < body

     {%   block   content   %}{%   endblock   %}      < footer

{%   block   footer   %} & copy;  2017 {%   endblock   %} < / footer

   < / body

< / html

Rob Allen ~ @akrabat

Slide 62

Slide 62

Base skeleton src/App/templates/layout/default.html.twig: < !DOCTYPE html

< html

   < head

     {%   block   head   %}          < link   rel

"stylesheet"   href

"style.css"   /

         < title

{%   block   title   %}{%   endblock   %}  - Akrabat < / title

     {%   endblock   %}    < / head

   < body

     {%   block   content   %}{%   endblock   %}      < footer

{%   block   footer   %} & copy;  2017 {%   endblock   %} < / footer

   < / body

< / html

Rob Allen ~ @akrabat

Slide 63

Slide 63

Base skeleton src/App/templates/layout/default.html.twig: < !DOCTYPE html

< html

   < head

     {%   block   head   %}          < link   rel

"stylesheet"   href

"style.css"   /

         < title

{%   block   title   %}{%   endblock   %}  - Akrabat < / title

     {%   endblock   %}    < / head

   < body

     {%   block   content   %}{%   endblock   %}      < footer

{%   block   footer   %} & copy;  2017 {%   endblock   %} < / footer

   < / body

< / html

Rob Allen ~ @akrabat

Slide 64

Slide 64

. Rob Allen ~ @akrabat

Slide 65

Slide 65

Adding components Rob Allen ~ @akrabat

Slide 66

Slide 66

Add a form Rob Allen ~ @akrabat

Slide 67

Slide 67

Add a form: HTML < form   method

"GET"   action

"/bitcoin"

     < label

& pound; < / label

     < input   name

"amount"   value

" {{   amount   }} "

     < button

Convert < / button

< / form

< p

& pound; {{ amount }}  is  {{   bitcoins | number_format ( 6 )   }}  BTC Rob Allen ~ @akrabat

Slide 68

Slide 68

Add a form: HTML < form   method

"GET"   action

"/bitcoin"

     < label

& pound; < / label

     < input   name

"amount"   value

" {{   amount   }} "

     < button

Convert < / button

< / form

< p

& pound; {{ amount }}  is  {{   bitcoins | number_format ( 6 )   }}  BTC Rob Allen ~ @akrabat

Slide 69

Slide 69

Add a form: HTML < form   method

"GET"   action

"/bitcoin"

     < label

& pound; < / label

     < input   name

"amount"   value

" {{   amount   }} "

     < button

Convert < / button

< / form

< p

& pound; {{ amount }}  is  {{   bitcoins | number_format ( 6 )   }}  BTC Rob Allen ~ @akrabat

Slide 70

Slide 70

Add a form: HTML < form   method

"GET"   action

"/bitcoin"

     < label

& pound; < / label

     < input   name

"amount"   value

" {{   amount   }} "

     < button

Convert < / button

< / form

< p

& pound; {{ amount }}  is  {{   bitcoins | number_format ( 6 )   }}  BTC Rob Allen ~ @akrabat

Slide 71

Slide 71

Add a form: HTML < form   method

"GET"   action

"/bitcoin"

     < label

& pound; < / label

     < input   name

"amount"   value

" {{   amount   }} "

     < button

Convert < / button

< / form

< p

& pound; {{ amount }}  is  {{   bitcoins | number_format ( 6 )   }}  BTC < / p

Rob Allen ~ @akrabat

Slide 72

Slide 72

Zend-InputFilter   Rob Allen ~ @akrabat

Slide 73

Slide 73

Integration via ConfigProvider Rob Allen ~ @akrabat

Slide 74

Slide 74

Create an input filter $factory  

  new   Zend\InputFilter\Factory (); $inputFilter  

  $factory

createInputFilter ([      'amount'  

  [          'filters'  

  [              [ 'name'  

  'NumberParse' ],          ],          'validators'  

  [              [                  'name'  

  'GreaterThan' ,                  'options'  

  [ 'min'  

  0 ],              ],          ]      ], ]); Rob Allen ~ @akrabat

Slide 75

Slide 75

Create an input filter $factory  

  new   Zend\InputFilter\Factory (); $inputFilter  

  $factory

createInputFilter ([      'amount'  

  [          'filters'  

  [              [ 'name'  

  'ToInt' ],          ],          'validators'  

  [              [                  'name'  

  'GreaterThan' ,                  'options'  

  [ 'min'  

  0 ],              ],          ]      ], ]); Rob Allen ~ @akrabat

Slide 76

Slide 76

Create an input filter $factory  

  new   Zend\InputFilter\Factory (); $inputFilter  

  $factory

createInputFilter ([      'amount'  

  [          'filters'  

  [              [ 'name'  

  'ToInt' ],          ],          'validators'  

  [              [                  'name'  

  'GreaterThan' ,                  'options'  

  [ 'min'  

  0 ],              ],          ]      ], ]); Rob Allen ~ @akrabat

Slide 77

Slide 77

Create an input filter $factory  

  new   Zend\InputFilter\Factory (); $inputFilter  

  $factory

createInputFilter ([      'amount'  

  [          'filters'  

  [              [ 'name'  

  'ToInt' ],          ],          'validators'  

  [              [                  'name'  

  'GreaterThan' ,                  'options'  

  [ 'min'  

  0 ],              ],          ]      ], ]); Rob Allen ~ @akrabat

Slide 78

Slide 78

Create an input filter $factory  

  new   Zend\InputFilter\Factory (); $inputFilter  

  $factory

createInputFilter ([      'amount'  

  [          'filters'  

  [              [ 'name'  

  'ToInt' ],          ],          'validators'  

  [              [                  'name'  

  'GreaterThan' ,                  'options'  

  [ 'min'  

  0 ],              ],          ]      ], ]); Rob Allen ~ @akrabat

Slide 79

Slide 79

Validating request data 1. Retrieve data from Request object 2. Pass to InputFilter 3. Call isValid() 4. Retrieve sanitized, valid data using getValues() 5. Use getMessages() to find out what failed Rob Allen ~ @akrabat

Slide 80

Slide 80

Validating request data public   function   handle ( ServerRequestInterface   $request ) {      $requestData  

  $request

getQueryParams ();      $this

inputFilter

setData ( $requestData );      if   ( $this

inputFilter

isValid ())   {          /* request data is valid */          $values  

  $this

inputFilter

getValues ();          $btc  

  $this

btcService

convert ( $values [ 'amount' ]);      }   else   {          /* request data is invalid */          $errors  

  $this

inputFilter

getMessages ();      } Rob Allen ~ @akrabat

Slide 81

Slide 81

Validating request data public   function   handle ( ServerRequestInterface   $request ) {      $requestData  

  $request

getQueryParams ();      $this

inputFilter

setData ( $requestData );      if   ( $this

inputFilter

isValid ())   {          /* request data is valid */          $values  

  $this

inputFilter

getValues ();          $btc  

  $this

btcService

convert ( $values [ 'amount' ]);      }   else   {          /* request data is invalid */          $errors  

  $this

inputFilter

getMessages ();      } Rob Allen ~ @akrabat

Slide 82

Slide 82

Validating request data public   function   handle ( ServerRequestInterface   $request ) {      $requestData  

  $request

getQueryParams ();      $this

inputFilter

setData ( $requestData );      if   ( $this

inputFilter

isValid ())   {          /* request data is valid */          $values  

  $this

inputFilter

getValues ();          $btc  

  $this

btcService

convert ( $values [ 'amount' ]);      }   else   {          /* request data is invalid */          $errors  

  $this

inputFilter

getMessages ();      } Rob Allen ~ @akrabat

Slide 83

Slide 83

Validating request data public   function   handle ( ServerRequestInterface   $request ) {      $requestData  

  $request

getQueryParams ();      $this

inputFilter

setData ( $requestData );      if   ( $this

inputFilter

isValid ())   {          /* request data is valid */          $values  

  $this

inputFilter

getValues ();          $btc  

  $this

btcService

convert ( $values [ 'amount' ]);      }   else   {          /* request data is invalid */          $errors  

  $this

inputFilter

getMessages ();      } Rob Allen ~ @akrabat

Slide 84

Slide 84

Validating request data public   function   handle ( ServerRequestInterface   $request ) {      $requestData  

  $request

getQueryParams ();      $this

inputFilter

setData ( $requestData );      if   ( $this

inputFilter

isValid ())   {          /* request data is valid */          $values  

  $this

inputFilter

getValues ();          $btc  

  $this

btcService

convert ( $values [ 'amount' ]);      }   else   {          /* request data is invalid */          $errors  

  $this

inputFilter

getMessages ();      } Rob Allen ~ @akrabat

Slide 85

Slide 85

Summary Rob Allen ~ @akrabat

Slide 86

Slide 86

Resources • https://docs.zendframework.com/zend-expressive/ • https://github.com/zendframework/ • https://akrabat.com/category/zend-expressive/ • https://framework.zend.com/blog • Zend Expressive Essentials by Matt Setter Rob Allen ~ @akrabat

Slide 87

Slide 87

Thank you! https://joind.in/talk/52ee1 Rob Allen ~ @akrabat