kicking off with zend expressive and doctrine orm (php minds march 2018)

105
@asgrim Kicking off with Zend Expressive and Doctrine ORM James Titcumb php[MiNDS] March 2018 Follow along (optional): https://github.com/asgrim/book-library

Upload: james-titcumb

Post on 18-Mar-2018

62 views

Category:

Technology


10 download

TRANSCRIPT

Page 1: Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)

@asgrim

Kicking off with Zend Expressiveand Doctrine ORM

James Titcumbphp[MiNDS] March 2018

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

Page 2: Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)

$ whoami

James Titcumb

www.jamestitcumb.com

www.roave.com

@asgrim

Page 3: Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)

@asgrim

What is Zend Expressive?

Page 4: Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)

@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 (PHP MiNDS March 2018)

@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 (PHP MiNDS March 2018)

@asgrim

PSR-7HTTP Message Interfaces

Page 7: Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)

@asgrim

HTTP Request

POST /talk HTTP/1.1

Host: phpminds.org

foo=bar&baz=bat

Page 8: Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)

@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 (PHP MiNDS March 2018)

@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 (PHP MiNDS March 2018)

@asgrim

Zend DiactorosPSR-7 implementation

Page 11: Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)

@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 (PHP MiNDS March 2018)

@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 (PHP MiNDS March 2018)

@asgrim

Zend StratigilityCreating & dispatching middleware pipelines

Page 14: Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)

@asgrim

So what is “middleware”?

Page 15: Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)

@asgrim

Middleware in the middle

function(Request $request, RequestHandler $handler) : Response

{

// ... some code before ...

$response = $handler->handle($request);

// ... some code after ...

return $response;

}

Page 16: Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)

@asgrim

Some people call it an onion

SessionInitialisingMiddleware

AuthenticationMiddleware

ThingyMiddleware

DashboardHandler

Page 17: Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)

@asgrim

Some people call it an onion

SessionInitialisingMiddleware

AuthenticationMiddleware

ThingyMiddleware

DashboardHandler

Page 18: Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)

@asgrim

Some people call it an onion

SessionInitialisingMiddleware

AuthenticationMiddleware

ThingyMiddleware

DashboardHandler

Page 19: Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)

@asgrim

psr/http-server-middleware

Page 20: Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)

@asgrim

Middleware using MiddlewareInterface

<?php

declare(strict_types=1);

namespace App\Middleware;

use Psr\Http\Message\ResponseInterface;

use Psr\Http\Message\ServerRequestInterface;

use Psr\Http\Server\MiddlewareInterface;

use Psr\Http\Server\RequestHandlerInterface;

final class MyMiddleware implements MiddlewareInterface

{

public function process(

ServerRequestInterface $request,

RequestHandlerInterface $requestHandler

) : ResponseInterface {

return $requestHandler>handle($request);

}

}

Page 21: Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)

@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 (PHP MiNDS March 2018)

@asgrim

Modifying the response

function(Request $request, RequestHandler $handler) : Response

{

$response = $handler->handle($request);

return $response->withHeader(

'X-Clacks-Overhead',

'GNU Terry Pratchett'

);

}

Page 23: Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)

@asgrim

Pipe all the things!

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

$pipe->pipe($logUncaughtErrorsMiddleware);

$pipe->pipe($sessionInitialisingMiddleware);

$pipe->pipe($authenticationMiddleware);

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

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

Page 24: Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)

@asgrim

LogErrorsCaughtMiddleware

function(Request $request, RequestHandler $handler) : Response

{

try {

return $handler->handle($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 (PHP MiNDS March 2018)

@asgrim

SessionInitialisingMiddleware

function(Request $request, RequestHandler $handler) : Response

{

session_start();

return $handler->handle($request);

}

Page 26: Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)

@asgrim

function(Request $request, RequestHandler $handler) : Response

{

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

return new JsonResponse(

[

'message' => 'Invalid OAuth token',

],

403

);

}

return $handler->handle($request);

}

AuthenticationMiddleware

Page 27: Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)

@asgrim

IndexHandlerMiddleware

function(Request $request, RequestHandler $handler) : Response

{

// ... some code ...

return new JsonResponse($someData, 200);

}

Page 28: Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)

@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 (PHP MiNDS March 2018)

@asgrim

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

Page 30: Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)

@asgrim

Routing

Page 31: Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)

@asgrim

container-interop

Page 32: Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)

@asgrim

Optionally, templating

Page 33: Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)

@asgrim

Piping and Routing

Page 34: Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)

@asgrim

Zend Framework 2/3What of them?

Page 35: Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)

@asgrim

Middleware vs MVC

Page 36: Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)

@asgrim

Using ZF componentsin Expressive

Page 37: Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)

@asgrim

ZF’s Module.php

class Module

{

public function getConfig()

{

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

}

}

Page 38: Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)

@asgrim

ConfigProvider

Page 39: Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)

@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 (PHP MiNDS March 2018)

@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 (PHP MiNDS March 2018)

@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 (PHP MiNDS March 2018)

@asgrim

config/config.php

<?php

$aggregator = new ConfigAggregator([

\Zend\Form\ConfigProvider::class,

// .. other config ...

], $cacheConfig['config_cache_path']);

return $aggregator->getMergedConfig();

Page 43: Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)

@asgrim

ConfigAggregator

Page 44: Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)

@asgrim

config/config.php

$aggregator = new ConfigAggregator([

\Zend\Expressive\Router\FastRouteRouter\ConfigProvider::class,

\Zend\HttpHandlerRunner\ConfigProvider::class,

new ArrayProvider($cacheConfig),

\Zend\Expressive\Helper\ConfigProvider::class,

\Zend\Expressive\ConfigProvider::class,

\Zend\Expressive\Router\ConfigProvider::class,

\App\ConfigProvider::class,

new PhpFileProvider(realpath(__DIR__) . '/autoload/{{,*.}global,{,*.}local}.php'),

new PhpFileProvider(realpath(__DIR__) . '/development.config.php'),

], $cacheConfig['config_cache_path']);

Page 45: Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)

@asgrim

Layers of an Expressive application

Expressive

Stratigility

Diactoros

PSR-7 Interface

DIRouter Template

Your Application

Page 46: Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)

@asgrim

Getting startedwith Zend Expressive

Page 47: Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)

@asgrim

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

Page 48: Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)

@asgrim

Expressive Skeleton

Page 49: Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)

@asgrim

Expressive installer - start

composer create-project zendframework/zend-expressive-skeleton:3.0.0-rc1 book-library

Installing zendframework/zend-expressive-skeleton (3.0.0rc1)

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

Created project in test

> ExpressiveInstaller\OptionalPackages::install

Setting up optional packages

Setup data and cache dir

Removing installer development dependencie

Page 50: Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)

@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): 2

- Copying src/App/ConfigProvider.php

Page 51: Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)

@asgrim

Modular structurezend-config-aggregator

Page 52: Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)

@asgrim

Modular structurezend-config-aggregator

Page 53: Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)

@asgrim

Flat or Modular?

Page 54: Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)

@asgrim

Default modular structure

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

Page 55: Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)

@asgrim

Expressive installer - container?

Which container do you want to use for dependency injection?

[1] Aura.Di

[2] Pimple

[3] zend-servicemanager

[4] Auryn

[5] Symfony DI Container

Make your selection or type a composer package name and version (zend-servicemanager):

- Adding package zendframework/zend-servicemanager (^3.3)

- Copying config/container.php

Page 56: Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)

@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):

- Adding package zendframework/zend-expressive-fastroute (^3.0.0alpha1)

- Whitelist package zendframework/zend-expressive-fastroute

- Copying config/routes.php

Page 57: Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)

@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 58: Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)

@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): n

Page 59: Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)

@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 60: Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)

@asgrim

Create the endpoints

Page 61: Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)

@asgrim

Book entity

class Book

{

/**

* @var string

*/

private $id;

/**

* @var bool

*/

private $inStock = true;

public function __construct()

{

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

}

Page 62: Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)

@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 63: Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)

@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 64: Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)

@asgrim

FindBookByUuidInterface

interface FindBookByUuidInterface

{

/**

* @param UuidInterface $slug

* @return Book

* @throws Exception\BookNotFound

*/

public function __invoke(UuidInterface $slug): Book;

}

Page 65: Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)

@asgrim

CheckOutHandler

public function process(ServerRequestInterface $request, RequestHandler $handler): 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 66: Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)

@asgrim

return function (

Application $app,

MiddlewareFactory $factory,

ContainerInterface $container

): void {

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

$app->pipe(\Zend\Expressive\Router\Middleware\PathBasedRoutingMiddleware::class); // Routing

$app->pipe(\Zend\Expressive\Router\Middleware\DispatchMiddleware::class); // Dispatch

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

};

Define application pipeline - pipeline.php

Page 67: Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)

@asgrim

return function (

Application $app,

MiddlewareFactory $factory,

ContainerInterface $container

): void {

$app->get(

'/book/{id}/check-out',

\App\Handler\CheckOutHandler::class,

'check-out'

);

$app->get(

'/book/{id}/check-in',

\App\Handler\CheckInHandler::class,

'check-in'

);

};

Define routes - routes.php

Page 68: Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)

@asgrim

Adding some ORM

Page 69: Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)

@asgrim

Your application

Doctrine quick overview

DBDBAL ORM

(EntityManager)Entities

Finders,Services,

...

Page 70: Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)

@asgrim

container-interop-doctrine

Page 71: Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)

@asgrim

Installation

$ composer require dasprid/container-interop-doctrine

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

./composer.json has been updated

Loading composer repositories with package information

Updating dependencies (including require-dev)

Package operations: 9 installs, 0 updates, 0 removals

- Installing doctrine/lexer (v1.0.1): Loading from cache

- Installing doctrine/inflector (v1.3.0): Loading from cache

- Installing doctrine/collections (v1.5.0): Loading from cache

- Installing doctrine/cache (v1.7.1): Loading from cache

- Installing doctrine/annotations (v1.6.0): Loading from cache

- Installing doctrine/common (v2.8.1): Loading from cache

- Installing doctrine/dbal (v2.6.3): Loading from cache

- Installing doctrine/orm (v2.6.1): Loading from cache

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

Writing lock file

Generating autoload files

Page 72: Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)

@asgrim

src/App/ConfigProvider.php

use Doctrine\ORM\EntityManagerInterface;

use ContainerInteropDoctrine\EntityManagerFactory;

final class ConfigProvider

{

// ...

public function getDependencies() : array

{

return [

'factories' => [

EntityManagerInterface::class => EntityManagerFactory::class,

// ... other services

],

];

Page 73: Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)

@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 (PHP MiNDS March 2018)

@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 (PHP MiNDS March 2018)

@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 (PHP MiNDS March 2018)

@asgrim

Annotating the Entities

Page 77: Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)

@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 (PHP MiNDS March 2018)

@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 (PHP MiNDS March 2018)

@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 (PHP MiNDS March 2018)

@asgrim

try {

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

$book->checkOut();

});

} catch (BookNotAvailable $bookNotAvailable) {

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

}

src/App/Handler/CheckOutHandler.php

Page 81: Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)

@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 (PHP MiNDS March 2018)

@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 (PHP MiNDS March 2018)

@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 (PHP MiNDS March 2018)

@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 (PHP MiNDS March 2018)

@asgrim

Automatic Flushing Middleware

Page 86: Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)

@asgrim

FlushingMiddleware

public function process(Request $request, RequestHandler $handler)

{

$response = $handler->handle($request);

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

$this->entityManager->flush();

}

return $response;

}

Page 87: Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)

@asgrim

FlushingMiddleware

public function process(Request $request, RequestHandler $handler)

{

$response = $handler->handle($request);

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

$this->entityManager->flush();

}

return $response;

}

Page 88: Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)

@asgrim

FlushingMiddleware

public function process(Request $request, RequestHandler $handler)

{

$response = $handler->handle($request);

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

$this->entityManager->flush();

}

return $response;

}

Page 89: Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)

@asgrim

Add to pipeline

return function (

Application $app,

MiddlewareFactory $factory,

ContainerInterface $container

): void {

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

$app->pipe(\Zend\Expressive\Router\Middleware\PathBasedRoutingMiddleware::class); // Routing

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

$app->pipe(\Zend\Expressive\Router\Middleware\DispatchMiddleware::class); // Dispatch

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

};

Page 90: Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)

@asgrim

Doing more with middleware

Page 91: Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)

@asgrim

Authentication

Page 92: Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)

@asgrim

public function process(

ServerRequestInterface $request,

RequestHandler $handler

) : 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 $handler->handle($request);

}

Create the middleware

Page 93: Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)

@asgrim

public function process(

ServerRequestInterface $request,

RequestHandler $handler

) : 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 $handler->handle($request);

}

Create the middleware

Page 94: Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)

@asgrim

public function process(

ServerRequestInterface $request,

RequestHandler $handler

) : 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 $handler->handle($request);

}

Create the middleware

Page 95: Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)

@asgrim

public function process(

ServerRequestInterface $request,

RequestHandler $handler

) : 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 $handler->handle($request);

}

Create the middleware

Page 96: Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)

@asgrim

return function (

Application $app,

MiddlewareFactory $factory,

ContainerInterface $container

): void {

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

$app->pipe(\Zend\Expressive\Router\Middleware\PathBasedRoutingMiddleware::class); // Routing

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

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

$app->pipe(\Zend\Expressive\Router\Middleware\DispatchMiddleware::class); // Dispatch

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

};

Add middleware to pipe

Page 97: Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)

@asgrim

Helioscomposer require dasprid/helios

Page 98: Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)

@asgrim

PSR7-Sessioncomposer require psr7-sessions/storageless

Page 99: Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)

@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 SystemClock()

);

}

Factory the middleware

Page 100: Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)

@asgrim

return function (

Application $app,

MiddlewareFactory $factory,

ContainerInterface $container

): void {

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

$app->pipe(\Zend\Expressive\Router\Middleware\PathBasedRoutingMiddleware::class); // Routing

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

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

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

$app->pipe(\Zend\Expressive\Router\Middleware\DispatchMiddleware::class); // Dispatch

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

};

Add middleware to pipe

Page 101: Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)

@asgrim

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

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

Session is stored in Request

Page 102: Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)

@asgrim

$skills++;

Page 103: Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)

@asgrim

To summarise...

● PSR-7 & PSR-15 lay the foundations!● Diactoros is just a PSR-7 implementation● Stratigility is a middleware pipeline: the main bit● Expressive is a glue for everything● ConfigProvider is great for aggregating (merging) config● container-interop-doctrine makes Doctrine work easier● Middleware all the things!● Similar concepts in other frameworks (e.g. Slim)

Page 105: Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018)

Any questions?

James Titcumb@asgrim