kicking off with zend expressive and doctrine orm (confoo yvr 2017)

104
@asgrim Kicking off with Zend Expressive and Doctrine ORM James Titcumb ConFoo Vancouver 2017 Follow along (optional): https://github.com/asgrim/book-library

Upload: james-titcumb

Post on 29-Jan-2018

146 views

Category:

Technology


4 download

TRANSCRIPT

Page 1: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

Kicking off with Zend Expressiveand Doctrine ORM

James TitcumbConFoo Vancouver 2017

Follow along (optional): https://github.com/asgrim/book-library

Page 2: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

$ whoami

James Titcumb

www.jamestitcumb.com

www.roave.com

@asgrim

Page 3: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

What is Zend Expressive?

Page 4: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

Layers of an Expressive application

Expressive

Stratigility

Diactoros

PSR-7 Interface

DIRouter Template

Your Application

Page 5: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

Layers of an Expressive application

Expressive

Stratigility

Diactoros

PSR-7 Interface

DIRouter Template

Your Application

Page 6: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

PSR-7HTTP Message Interfaces

Page 7: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

HTTP Request

POST /yvr2017/foo HTTP/1.1

Host: confoo.ca

foo=bar&baz=bat

Page 8: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

HTTP Response

HTTP/1.1 200 OK

Content-Type: text/plain

This is the response body

Page 9: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

Layers of an Expressive application

Expressive

Stratigility

Diactoros

PSR-7 Interface

DIRouter Template

Your Application

Page 10: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

Zend DiactorosPSR-7 implementation

Page 11: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

Node http.Server using Diactoros

$server = \Zend\Diactoros\Server::createServer(

function ($request, $response, $done) {

return $response->getBody()->write('hello world');

},

$_SERVER,

$_GET,

$_POST,

$_COOKIE,

$_FILES

);

$server->listen();

Page 12: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

Layers of an Expressive application

Expressive

Stratigility

Diactoros

PSR-7 Interface

DIRouter Template

Your Application

Page 13: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

Zend StratigilityCreating & dispatching middleware pipelines

Page 14: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

So what is “middleware”?

Page 15: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

Middleware in the middle

function(Request $request, DelegateInterface $delegate) : Response

{

// ... some code before ...

$response = $delegate->process($request);

// ... some code after ...

return $response;

}

Page 16: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

Some people call it an onion

SessionInitialisingMiddleware

AuthenticationMiddleware

ThingyMiddleware

DashboardAction

Page 17: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

Some people call it an onion

SessionInitialisingMiddleware

AuthenticationMiddleware

ThingyMiddleware

DashboardAction

Page 18: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

Some people call it an onion

SessionInitialisingMiddleware

AuthenticationMiddleware

ThingyMiddleware

DashboardAction

Page 19: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

http-interop/http-middleware(work in progress)

Page 20: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

Middleware using MiddlewareInterface

<?php

declare(strict_types=1);

namespace App\Middleware;

use Interop\Http\ServerMiddleware\DelegateInterface;

use Interop\Http\ServerMiddleware\MiddlewareInterface;

use Psr\Http\Message\ResponseInterface;

use Psr\Http\Message\ServerRequestInterface;

final class MyMiddleware implements MiddlewareInterface

{

public function process(

ServerRequestInterface $request,

DelegateInterface $delegate

) : ResponseInterface {

return $delegate->process($request);

}

}

Page 21: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

Double-pass middleware

public function __invoke(

Request $request,

Response $response,

callable $next

): Response {

return $next($request, $response);

}

!!! DEPRECATED !!!

Page 22: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

Modifying the response

function(Request $request, DelegateInterface $delegate) : Response

{

$response = $delegate->process($request);

return $response->withHeader(

'X-Clacks-Overhead',

'GNU Terry Pratchett'

);

}

Page 23: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

Pipe all the things!

$pipe = new \Zend\Stratigility\MiddlewarePipe();

$pipe->pipe($logUncaughtErrorsMiddleware);

$pipe->pipe($sessionInitialisingMiddleware);

$pipe->pipe($authenticationMiddleware);

$pipe->pipe('/', $indexActionMiddleware);

$pipe->pipe(new NotFoundHandler(...));

Page 24: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

LogErrorsCaughtMiddleware

function(Request $request, DelegateInterface $delegate) : Response

{

try {

return $delegate->process($request);

} catch (\Throwable $throwable) {

// Log the error, handle it with error page etc.

return new JsonResponse(

[

'message' => $throwable->getMessage(),

],

500

);

}

}

Page 25: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

SessionInitialisingMiddleware

function(Request $request, DelegateInterface $delegate) : Response

{

session_start();

return $delegate->process($request);

}

Page 26: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

function(Request $request, DelegateInterface $delegate) : Response

{

if (!$this->doSomeOAuthCheck($request)) {

return new JsonResponse(

[

'message' => 'Invalid OAuth token',

],

403

);

}

return $delegate->process($request);

}

AuthenticationMiddleware

Page 27: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

IndexActionMiddleware

function(Request $request, DelegateInterface $delegate) : Response

{

// ... some code ...

return new JsonResponse($someData, 200);

}

Page 28: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

Layers of an Expressive application

Expressive

Stratigility

Diactoros

PSR-7 Interface

DIRouter Template

Your Application

Page 29: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

Zend ExpressiveOne Ring to bring them all and in the darkness bind them.

Page 30: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

Routing

Page 31: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

container-interop

Page 32: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

Optionally, templating

Page 33: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

Piping and Routing

Page 34: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

Zend Framework 2/3What of them?

Page 35: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

Middleware vs MVC

Page 36: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

Using ZF componentsin Expressive

Page 37: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

Module.php

class Module

{

public function getConfig()

{

return include __DIR__ . '/../config/module.config.php';

}

}

Page 38: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

ConfigProvider

Page 39: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

ConfigProvider

namespace Zend\Form;

class ConfigProvider

{

public function __invoke()

{

return [

'dependencies' => $this->getDependencyConfig(),

'view_helpers' => $this->getViewHelperConfig(),

];

}

}

Page 40: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

ConfigProvider#getDependencyConfig()

public function getDependencyConfig()

{

return [

'abstract_factories' => [

FormAbstractServiceFactory::class,

],

'aliases' => [

'Zend\Form\Annotation\FormAnnotationBuilder' => 'FormAnnotationBuilder',

Annotation\AnnotationBuilder::class => 'FormAnnotationBuilder',

FormElementManager::class => 'FormElementManager',

],

'factories' => [

'FormAnnotationBuilder' => Annotation\AnnotationBuilderFactory::class,

'FormElementManager' => FormElementManagerFactory::class,

],

Page 41: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

Zend\Form’s Module.php (for Zend Framework)

class Module

{

public function getConfig()

{

$provider = new ConfigProvider();

return [

'service_manager' => $provider->getDependencyConfig(),

'view_helpers' => $provider->getViewHelperConfig(),

];

}

}

Page 42: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

config/autoload/zend-form.global.php

<?php

return (new \Zend\Form\ConfigProvider())->__invoke();

Page 43: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

Layers of an Expressive application

Expressive

Stratigility

Diactoros

PSR-7 Interface

DIRouter Template

Your Application

Page 44: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

Getting startedwith Zend Expressive

Page 45: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

https://github.com/asgrim/book-library

Page 46: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

Expressive Skeleton

Page 47: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

Expressive installer - start

$ composer create-project zendframework/zend-expressive-skeleton book-library

Installing zendframework/zend-expressive-skeleton (2.0.4)

- Installing zendframework/zend-expressive-skeleton (2.0.4): Downloading (100%)

Created project in book-library-2

> ExpressiveInstaller\OptionalPackages::install

Setting up optional packages

Setup data and cache dir

Removing installer development dependencies

Page 48: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

Expressive installer - structure

What type of installation would you like?

[1] Minimal (no default middleware, templates, or assets; configuration only)

[2] Flat (flat source code structure; default selection)

[3] Modular (modular source code structure; recommended)

Make your selection (2):

Page 49: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

Modular structurezend-config-aggregator

Page 50: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

Modular structurezend-config-aggregator

Page 51: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

Flat or Modular?

Page 52: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

Default modular structure

src/ MyModule/ src/ ConfigProvider.php Action/ Entity/ Middleware/ templates/ test/

Page 53: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

Expressive installer - container?

Which container do you want to use for dependency injection?

[1] Aura.Di

[2] Pimple

[3] Zend ServiceManager

Make your selection or type a composer package name and version (Zend ServiceManager):

Page 54: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

Expressive installer - router?

Which router do you want to use?

[1] Aura.Router

[2] FastRoute

[3] Zend Router

Make your selection or type a composer package name and version (FastRoute):

Page 55: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

Expressive installer - template?

Which template engine do you want to use?

[1] Plates

[2] Twig

[3] Zend View installs Zend ServiceManager

[n] None of the above

Make your selection or type a composer package name and version (n):

Page 56: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

Expressive installer - whoops?

Which error handler do you want to use during development?

[1] Whoops

[n] None of the above

Make your selection or type a composer package name and version (Whoops):

Page 57: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

Expressive installer - run it!

$ composer serve

> php -S 0.0.0.0:8080 -t public/ public/index.php

[Thu Sep 1 20:29:33 2016] 127.0.0.1:48670 [200]: /favicon.ico

{ "welcome": "Congratulations! You have installed the zend-expressive skeleton application.", "docsUrl": "https://docs.zendframework.com/zend-expressive/"}

Page 58: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

Create the endpoints

Page 59: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

Book entity

class Book

{

/**

* @var string

*/

private $id;

/**

* @var bool

*/

private $inStock = true;

public function __construct()

{

$this->id = (string)Uuid::uuid4();

}

Page 60: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

Book entity

class Book

{

/**

* @return void

* @throws \App\Entity\Exception\BookNotAvailable

*/

public function checkOut()

{

if (!$this->inStock) {

throw Exception\BookNotAvailable::fromBook($this);

}

$this->inStock = false;

}

Page 61: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

Book entity

class Book

{

/**

* @return void

* @throws \App\Entity\Exception\BookAlreadyStocked

*/

public function checkIn()

{

if ($this->inStock) {

throw Exception\BookAlreadyStocked::fromBook($this);

}

$this->inStock = true;

}

Page 62: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

FindBookByUuidInterface

interface FindBookByUuidInterface

{

/**

* @param UuidInterface $slug

* @return Book

* @throws Exception\BookNotFound

*/

public function __invoke(UuidInterface $slug): Book;

}

Page 63: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

CheckOutAction

public function process(ServerRequestInterface $request, DelegateInterface $delegate): JsonResponse{ try { $book = $this->findBookByUuid->__invoke(Uuid::fromString($request->getAttribute('id'))); } catch (BookNotFound $bookNotFound) { return new JsonResponse(['info' => $bookNotFound->getMessage()], 404); }

try { $book->checkOut(); } catch (BookNotAvailable $bookNotAvailable) { return new JsonResponse(['info' => $bookNotAvailable->getMessage()], 423); }

return new JsonResponse([ 'info' => sprintf('You have checked out %s', $book->getId()), ]);}

Page 64: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

Config vs Programmatic Pipe

'middleware_pipeline' => [

'routing' => [

'middleware' => [

App\Middleware\ErrorCatchingMiddleware::class,

ApplicationFactory::ROUTING_MIDDLEWARE,

Helper\UrlHelperMiddleware::class,

PSR7Sessions\Storageless\Http\SessionMiddleware::class,

App\Middleware\AuthenticationMiddleware::class,

ApplicationFactory::DISPATCH_MIDDLEWARE,

],

'priority' => 1,

],

Page 65: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

Config vs Programmatic Pipe

$app->pipe(\App\Middleware\ErrorCatchingMiddleware::class);

$app->pipeRoutingMiddleware();

$app->pipe(\Zend\Expressive\Helper\UrlHelperMiddleware::class);

$app->pipe(\PSR7Sessions\Storageless\Http\SessionMiddleware::class);

$app->pipe(\App\Middleware\AuthenticationMiddleware::class);

$app->pipeDispatchMiddleware();

Page 66: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

Injecting config into programmatic pipeline

final class BookRoutesConfigProvider{ public function __invoke() { return [ 'routes' => [ [ 'name' => 'check-out', 'path' => '/book/{id}/check-out', 'middleware' => App\Action\CheckOutAction::class, 'allowed_methods' => ['GET'], ], [ 'name' => 'check-in', 'path' => '/book/{id}/check-in', 'middleware' => App\Action\CheckInAction::class, 'allowed_methods' => ['GET'], ], ], ]; }

Page 67: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

Injecting config into programmatic pipeline

$app->injectRoutesFromConfig((new BookRoutesConfigProvider())());

$app->injectPipelineFromConfig((new BookPipelineConfigProvider())());

Page 68: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

Adding some ORM

Page 69: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

Your application

Doctrine quick overview

DBDBAL ORM

(EntityManager)Entities

Finders,Services,

...

Page 70: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

container-interop-doctrine

Page 71: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

Installation

$ composer require dasprid/container-interop-doctrine

Using version ^1.0 for dasprid/container-interop-doctrine

./composer.json has been updated

Loading composer repositories with package information

Updating dependencies (including require-dev)

Package operations: 1 install, 0 updates, 0 removals

- Installing dasprid/container-interop-doctrine (1.0.0): Loading from cache

Package http-interop/http-middleware is abandoned, you should avoid using it.

Use http-interop/http-server-middleware instead.

Writing lock file

Generating autoload files

$

Page 72: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

config/autoload/doctrine.global.php

<?php

declare(strict_types = 1);

use Doctrine\ORM\EntityManagerInterface;

use ContainerInteropDoctrine\EntityManagerFactory;

return [

'dependencies' => [

'factories' => [

EntityManagerInterface::class => EntityManagerFactory::class,

],

],

];

Page 73: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

'doctrine' => [

'connection' => [

'orm_default' => [

'driver_class' => PDOPgSql\Driver::class,

'params' => [

'url' => 'postgres://user:pass@localhost/book_library',

],

],

],

'driver' => [

'orm_default' => [

'class' => MappingDriverChain::class,

'drivers' => [

// ... and so on ...

config/autoload/doctrine.global.php

Page 74: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

'doctrine' => [

'connection' => [

'orm_default' => [

'driver_class' => PDOPgSql\Driver::class,

'params' => [

'url' => 'postgres://user:pass@localhost/book_library',

],

],

],

'driver' => [

'orm_default' => [

'class' => MappingDriverChain::class,

'drivers' => [

// ... and so on ...

config/autoload/doctrine.global.php

Page 75: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

<?php

declare(strict_types = 1);

use Doctrine\ORM\EntityManagerInterface;

use Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper;

use Symfony\Component\Console\Helper\HelperSet;

$container = require __DIR__ . '/container.php';

return new HelperSet([

'em' => new EntityManagerHelper(

$container->get(EntityManagerInterface::class)

),

]);

config/cli-config.php

Page 76: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

Annotating the Entities

Page 77: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

/**

* @ORM\Entity

* @ORM\Table(name="book")

*/

class Book

{

/**

* @ORM\Id

* @ORM\Column(name="id", type="guid")

* @ORM\GeneratedValue(strategy="NONE")

* @var string

*/

private $id;

src/App/Entity/Book.php

Page 78: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

/**

* @ORM\Entity

* @ORM\Table(name="book")

*/

class Book

{

/**

* @ORM\Id

* @ORM\Column(name="id", type="guid")

* @ORM\GeneratedValue(strategy="NONE")

* @var string

*/

private $id;

src/App/Entity/Book.php

Page 79: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

public function __invoke(UuidInterface $id): Book

{

/** @var Book|null $book */

$book = $this->repository->find((string)$id);

if (null === $book) {

throw Exception\BookNotFound::fromUuid($id);

}

return $book;

}

src/App/Service/Book/DoctrineFindBookByUuid.php

Page 80: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

try {

$this->entityManager->transactional(function () use ($book) {

$book->checkOut();

});

} catch (BookNotAvailable $bookNotAvailable) {

return new JsonResponse(['info' => $bookNotAvailable->getMessage()], 423);

}

src/App/Action/CheckOutAction.php

Page 81: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

Generate the schema

$ vendor/bin/doctrine orm:schema-tool:create

ATTENTION: This operation should not be executed in a production

environment.

Creating database schema...

Database schema created successfully!

$

Page 82: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

Insert some data

INSERT INTO book (id, name, in_stock) VALUES (

'1c06bec9-adae-47c2-b411-73b1db850e6f',

'The Great Escape',

true

);

Page 83: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

/book/1c06bec9-adae-47c2-b411-.../check-out

{"info":"You have checked out The Great Escape"}

Page 84: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

/book/1c06bec9-adae-47c2-b411-.../check-in

{"info":"You have checked in The Great Escape"}

Page 85: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

Automatic Flushing Middleware

Page 86: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

FlushingMiddleware

public function process(Request $request, DelegateInterface $delegate)

{

$response = $delegate->process($request);

if ($this->entityManager->isOpen()) {

$this->entityManager->flush();

}

return $response;

}

Page 87: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

FlushingMiddleware

public function process(Request $request, DelegateInterface $delegate)

{

$response = $delegate->process($request);

if ($this->entityManager->isOpen()) {

$this->entityManager->flush();

}

return $response;

}

Page 88: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

FlushingMiddleware

public function process(Request $request, DelegateInterface $delegate)

{

$response = $delegate->process($request);

if ($this->entityManager->isOpen()) {

$this->entityManager->flush();

}

return $response;

}

Page 89: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

Add to pipeline

/** @var \Zend\Expressive\Application $app */

$app->pipe(\Zend\Stratigility\Middleware\OriginalMessages::class);

$app->pipe(\Zend\Stratigility\Middleware\ErrorHandler::class);

$app->pipe(\Zend\Expressive\Helper\ServerUrlMiddleware::class);

$app->pipe(\App\Middleware\ErrorCatchingMiddleware::class);

$app->pipeRoutingMiddleware();

$app->pipe(\App\Middleware\FlushingMiddleware::class);

$app->pipe(\Zend\Expressive\Middleware\ImplicitHeadMiddleware::class);

$app->pipe(\Zend\Expressive\Middleware\ImplicitOptionsMiddleware::class);

$app->pipe(\Zend\Expressive\Helper\UrlHelperMiddleware::class);

$app->pipe(\PSR7Sessions\Storageless\Http\SessionMiddleware::class);

$app->pipe(\App\Middleware\AuthenticationMiddleware::class);

$app->pipeDispatchMiddleware();

$app->pipe(\Zend\Expressive\Middleware\NotFoundHandler::class);

Page 90: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

Doing more with middleware

Page 91: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

Authentication

Page 92: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

public function process(

ServerRequestInterface $request,

DelegateInterface $delegate

) : Response {

$queryParams = $request->getQueryParams();

// DO NOT DO THIS IN REAL LIFE

// It's really not secure ;)

if (!array_key_exists('authenticated', $queryParams)

|| $queryParams['authenticated'] !== '1') {

return new JsonResponse(['error' => 'You are not authenticated.'], 403);

}

return $delegate->process($request);

}

Create the middleware

Page 93: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

public function process(

ServerRequestInterface $request,

DelegateInterface $delegate

) : Response {

$queryParams = $request->getQueryParams();

// DO NOT DO THIS IN REAL LIFE

// It's really not secure ;)

if (!array_key_exists('authenticated', $queryParams)

|| $queryParams['authenticated'] !== '1') {

return new JsonResponse(['error' => 'You are not authenticated.'], 403);

}

return $delegate->process($request);

}

Create the middleware

Page 94: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

public function process(

ServerRequestInterface $request,

DelegateInterface $delegate

) : Response {

$queryParams = $request->getQueryParams();

// DO NOT DO THIS IN REAL LIFE

// It's really not secure ;)

if (!array_key_exists('authenticated', $queryParams)

|| $queryParams['authenticated'] !== '1') {

return new JsonResponse(['error' => 'You are not authenticated.'], 403);

}

return $delegate->process($request);

}

Create the middleware

Page 95: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

public function process(

ServerRequestInterface $request,

DelegateInterface $delegate

) : Response {

$queryParams = $request->getQueryParams();

// DO NOT DO THIS IN REAL LIFE

// It's really not secure ;)

if (!array_key_exists('authenticated', $queryParams)

|| $queryParams['authenticated'] !== '1') {

return new JsonResponse(['error' => 'You are not authenticated.'], 403);

}

return $delegate->process($request);

}

Create the middleware

Page 96: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

Helioscomposer require dasprid/helios

Page 97: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

PSR7-Sessioncomposer require psr7-sessions/storageless

Page 98: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

public function __invoke(ContainerInterface $container, $_, array $_ = null)

{

$symmetricKey = 'do-not-store-this-key-in-git-store-it-in-configuration-instead-please';

$expirationTime = 1200; // 20 minutes

return new SessionMiddleware(

new Signer\Hmac\Sha256(),

$symmetricKey,

$symmetricKey,

SetCookie::create(SessionMiddleware::DEFAULT_COOKIE)

->withSecure(false) // false on purpose, unless you have https locally

->withHttpOnly(true)

->withPath('/'),

new Parser(),

$expirationTime,

new SystemCurrentTime()

);

}

Factory the middleware

Page 99: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

/** @var \Zend\Expressive\Application $app */

$app->pipe(\Zend\Stratigility\Middleware\OriginalMessages::class);

$app->pipe(\Zend\Stratigility\Middleware\ErrorHandler::class);

$app->pipe(\Zend\Expressive\Helper\ServerUrlMiddleware::class);

$app->pipe(\App\Middleware\ErrorCatchingMiddleware::class);

$app->pipeRoutingMiddleware();

$app->pipe(\App\Middleware\FlushingMiddleware::class);

$app->pipe(\Zend\Expressive\Middleware\ImplicitHeadMiddleware::class);

$app->pipe(\Zend\Expressive\Middleware\ImplicitOptionsMiddleware::class);

$app->pipe(\Zend\Expressive\Helper\UrlHelperMiddleware::class);

$app->pipe(\PSR7Sessions\Storageless\Http\SessionMiddleware::class);

$app->pipe(\App\Middleware\AuthenticationMiddleware::class);

$app->pipeDispatchMiddleware();

$app->pipe(\Zend\Expressive\Middleware\NotFoundHandler::class);

Add middleware to pipe

Page 100: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

$session = $request->getAttribute(SessionMiddleware::SESSION_ATTRIBUTE);

$session->set('counter', $session->get('counter', 0) + 1);

Session is stored in Request

Page 101: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

$skills++;

Page 102: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

To summarise...

● PSR-7 is the foundation for this!● Diactoros is just a PSR-7 implementation● Stratigility is a middleware pipeline: the main bit● Expressive is a glue for everything● DoctrineModule can be used (with some fiddling)● container-interop-doctrine makes Doctrine work easier● Middleware all the things!● Similar concepts in other frameworks (e.g. Slim)

Page 103: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

@asgrim

Useful references

● https://github.com/asgrim/book-library● https://framework.zend.com/blog/2017-03-13-expressive-2-migration.html● https://framework.zend.com/blog/2017-03-30-expressive-config-routes.html● https://mwop.net/blog/2016-05-16-programmatic-expressive.html● https://docs.zendframework.com/zend-expressive/● https://docs.zendframework.com/zend-expressive/cookbook/autowiring-routes-and-pipelines/● https://docs.zendframework.com/zend-expressive/cookbook/passing-data-between-middleware/● http://www.masterzendframework.com/zend-expressive-introduction/

Page 104: Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)

Any questions?

James Titcumb@asgrim