kicking off with zend expressive and doctrine orm (zendcon 2016)

78
@asgrim Kicking off with Zend Expressive and Doctrine ORM James Titcumb ZendCon 2016

Upload: james-titcumb

Post on 26-Jan-2017

199 views

Category:

Technology


1 download

TRANSCRIPT

Page 1: Kicking off with Zend Expressive and Doctrine ORM (ZendCon 2016)

@asgrim

Kicking off with Zend Expressiveand

Doctrine ORM

James TitcumbZendCon 2016

Page 2: Kicking off with Zend Expressive and Doctrine ORM (ZendCon 2016)

Who is this guy?

James Titcumbwww.jamestitcumb.com

www.roave.com

www.phphants.co.uk

www.phpsouthcoast.co.uk

@asgrim

Page 3: Kicking off with Zend Expressive and Doctrine ORM (ZendCon 2016)

@asgrim

What is Zend Expressive?

Page 4: Kicking off with Zend Expressive and Doctrine ORM (ZendCon 2016)

@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 (ZendCon 2016)

@asgrim

PSR-7HTTP Message Interfaces

Page 6: Kicking off with Zend Expressive and Doctrine ORM (ZendCon 2016)

@asgrim

HTTP Request

POST /zendcon2016/foo HTTP/1.1

Host: www.zendcon.com

foo=bar&baz=bat

Page 7: Kicking off with Zend Expressive and Doctrine ORM (ZendCon 2016)

@asgrim

HTTP Request

POST /zendcon2016/foo HTTP/1.1

Host: www.zendcon.com

foo=bar&baz=bat

Page 8: Kicking off with Zend Expressive and Doctrine ORM (ZendCon 2016)

@asgrim

HTTP Request

POST /zendcon2016/foo HTTP/1.1

Host: www.zendcon.com

foo=bar&baz=bat

Page 9: Kicking off with Zend Expressive and Doctrine ORM (ZendCon 2016)

@asgrim

HTTP Request

POST /zendcon2016/foo HTTP/1.1

Host: www.zendcon.com

foo=bar&baz=bat

Page 10: Kicking off with Zend Expressive and Doctrine ORM (ZendCon 2016)

@asgrim

HTTP Response

HTTP/1.1 200 OK

Content-Type: text/plain

This is the response body

Page 11: Kicking off with Zend Expressive and Doctrine ORM (ZendCon 2016)

@asgrim

HTTP Response

HTTP/1.1 200 OK

Content-Type: text/plain

This is the response body

Page 12: Kicking off with Zend Expressive and Doctrine ORM (ZendCon 2016)

@asgrim

HTTP Response

HTTP/1.1 200 OK

Content-Type: text/plain

This is the response body

Page 13: Kicking off with Zend Expressive and Doctrine ORM (ZendCon 2016)

@asgrim

HTTP Response

HTTP/1.1 200 OK

Content-Type: text/plain

This is the response body

Page 14: Kicking off with Zend Expressive and Doctrine ORM (ZendCon 2016)

@asgrim

Zend DiactorosPSR-7 implementation

Page 15: Kicking off with Zend Expressive and Doctrine ORM (ZendCon 2016)

@asgrim

Zend StratigilityCreating & dispatching middleware pipelines

Page 16: Kicking off with Zend Expressive and Doctrine ORM (ZendCon 2016)

@asgrim

So what is “middleware”?

Page 17: Kicking off with Zend Expressive and Doctrine ORM (ZendCon 2016)

@asgrim

Middleware example

public function __invoke(

Request $request,

Response $response,

callable $next = null

) {

// ... some code before ...

$response = $next($request, $response);

// ... some code after ...

return $response;

}

Page 18: Kicking off with Zend Expressive and Doctrine ORM (ZendCon 2016)

@asgrim

Middleware example

public function __invoke(

Request $request,

Response $response,

callable $next = null

) {

// ... some code before ...

$response = $next($request, $response);

// ... some code after ...

return $response;

}

Page 19: Kicking off with Zend Expressive and Doctrine ORM (ZendCon 2016)

@asgrim

Middleware example

public function __invoke(

Request $request,

Response $response,

callable $next = null

) {

// ... some code before ...

$response = $next($request, $response);

// ... some code after ...

return $response;

}

Page 20: Kicking off with Zend Expressive and Doctrine ORM (ZendCon 2016)

@asgrim

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

Page 21: Kicking off with Zend Expressive and Doctrine ORM (ZendCon 2016)

@asgrim

Routing

Page 22: Kicking off with Zend Expressive and Doctrine ORM (ZendCon 2016)

@asgrim

container-interop

Page 23: Kicking off with Zend Expressive and Doctrine ORM (ZendCon 2016)

@asgrim

Optionally, templating

Page 24: Kicking off with Zend Expressive and Doctrine ORM (ZendCon 2016)

@asgrim

Piping and Routing

Page 25: Kicking off with Zend Expressive and Doctrine ORM (ZendCon 2016)

@asgrim

Pipe all the things!

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

$pipe->pipe($sessionMiddleware);

$pipe->pipe('/foo', $fooMiddleware);

$pipe->pipe($whateverMiddleware);

$pipe->pipe($dogeMiddleware);

Page 26: Kicking off with Zend Expressive and Doctrine ORM (ZendCon 2016)

@asgrim

Zend Framework 2/3What of them?

Page 27: Kicking off with Zend Expressive and Doctrine ORM (ZendCon 2016)

@asgrim

Middleware vs MVC

Page 28: Kicking off with Zend Expressive and Doctrine ORM (ZendCon 2016)

@asgrim

Getting startedwith Zend Expressive

Page 29: Kicking off with Zend Expressive and Doctrine ORM (ZendCon 2016)

@asgrim

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

Page 30: Kicking off with Zend Expressive and Doctrine ORM (ZendCon 2016)

@asgrim

Expressive Skeleton

Page 31: Kicking off with Zend Expressive and Doctrine ORM (ZendCon 2016)

@asgrim

Expressive installer - start

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

book-library

Installing zendframework/zend-expressive-skeleton (1.0.3)

- Installing zendframework/zend-expressive-skeleton (1.0.3)

Downloading: 100%

Created project in book-library

> ExpressiveInstaller\OptionalPackages::install

Setup data and cache dir

Setting up optional packages

Page 32: Kicking off with Zend Expressive and Doctrine ORM (ZendCon 2016)

@asgrim

Expressive installer - minimal?

Minimal skeleton? (no default middleware, templates, or assets;

configuration only)

[y] Yes (minimal)

[n] No (full; recommended)

Make your selection (No): n

Page 33: Kicking off with Zend Expressive and Doctrine ORM (ZendCon 2016)

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

Page 34: Kicking off with Zend Expressive and Doctrine ORM (ZendCon 2016)

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

Page 35: Kicking off with Zend Expressive and Doctrine ORM (ZendCon 2016)

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

Page 36: Kicking off with Zend Expressive and Doctrine ORM (ZendCon 2016)

@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 37: Kicking off with Zend Expressive and Doctrine ORM (ZendCon 2016)

@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": "zend-expressive.readthedocs.org"}

Page 38: Kicking off with Zend Expressive and Doctrine ORM (ZendCon 2016)

@asgrim

Create the endpoints

Page 39: Kicking off with Zend Expressive and Doctrine ORM (ZendCon 2016)

@asgrim

Book entity

final class Book

{

/**

* @var string

*/

private $id;

/**

* @var bool

*/

private $inStock = true;

public function __construct()

{

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

}

Page 40: Kicking off with Zend Expressive and Doctrine ORM (ZendCon 2016)

@asgrim

Book entity

final class Book

{

/**

* @return void

* @throws \App\Entity\Exception\BookNotAvailable

*/

public function checkOut()

{

if (!$this->inStock) {

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

}

$this->inStock = false;

}

Page 41: Kicking off with Zend Expressive and Doctrine ORM (ZendCon 2016)

@asgrim

Book entity

final class Book

{

/**

* @return void

* @throws \App\Entity\Exception\BookAlreadyStocked

*/

public function checkIn()

{

if ($this->inStock) {

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

}

$this->inStock = true;

}

Page 42: Kicking off with Zend Expressive and Doctrine ORM (ZendCon 2016)

@asgrim

FindBookByUuidInterface

interface FindBookByUuidInterface

{

/**

* @param UuidInterface $slug

* @return Book

* @throws Exception\BookNotFound

*/

public function __invoke(UuidInterface $slug): Book;

}

Page 43: Kicking off with Zend Expressive and Doctrine ORM (ZendCon 2016)

@asgrim

CheckOutAction

public function __invoke( ServerRequestInterface $request, ResponseInterface $response, callable $next = null) : 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 44: Kicking off with Zend Expressive and Doctrine ORM (ZendCon 2016)

@asgrim

Adding some ORM

Page 45: Kicking off with Zend Expressive and Doctrine ORM (ZendCon 2016)

@asgrim

DoctrineORMModule

Page 46: Kicking off with Zend Expressive and Doctrine ORM (ZendCon 2016)

@asgrim

DoctrineORMModule + Expressive?

Page 47: Kicking off with Zend Expressive and Doctrine ORM (ZendCon 2016)

@asgrim

config/autoload/doctrine-modules.global.php

$vendorPath = __DIR__ . '/../../vendor';

$doctrineModuleConfig = require_once

$vendorPath . '/doctrine/doctrine-module/config/module.config.php';

$doctrineModuleConfig['dependencies'] = $doctrineModuleConfig['service_manager'];

unset($doctrineModuleConfig['service_manager']);

$ormModuleConfig = require_once

$vendorPath . '/doctrine/doctrine-orm-module/config/module.config.php';

$ormModuleConfig['dependencies'] = $ormModuleConfig['service_manager'];

unset($ormModuleConfig['service_manager']);

return ArrayUtils::merge($doctrineModuleConfig, $ormModuleConfig);

Page 48: Kicking off with Zend Expressive and Doctrine ORM (ZendCon 2016)

@asgrim

ConfigProvider

namespace Zend\Form;

class ConfigProvider

{

public function __invoke()

{

return [

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

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

];

}

Page 49: Kicking off with Zend Expressive and Doctrine ORM (ZendCon 2016)

@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 50: Kicking off with Zend Expressive and Doctrine ORM (ZendCon 2016)

@asgrim

config/autoload/zend-form.global.php

<?php

use Zend\Form\ConfigProvider;

return (new ConfigProvider())->__invoke();

Page 51: Kicking off with Zend Expressive and Doctrine ORM (ZendCon 2016)

@asgrim

But wait!

Page 52: Kicking off with Zend Expressive and Doctrine ORM (ZendCon 2016)

@asgrim

container-interop-doctrinesaves the day!!!

Page 53: Kicking off with Zend Expressive and Doctrine ORM (ZendCon 2016)

@asgrim

Installation

$ composer require dasprid/container-interop-doctrine

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

./composer.json has been updated

Loading composer repositories with package information

Updating dependencies (including require-dev)

- Installing dasprid/container-interop-doctrine (0.2.2)

Loading from cache

Writing lock file

Generating autoload files

$

Page 54: Kicking off with Zend Expressive and Doctrine ORM (ZendCon 2016)

@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 55: Kicking off with Zend Expressive and Doctrine ORM (ZendCon 2016)

@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 56: Kicking off with Zend Expressive and Doctrine ORM (ZendCon 2016)

@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 57: Kicking off with Zend Expressive and Doctrine ORM (ZendCon 2016)

@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 58: Kicking off with Zend Expressive and Doctrine ORM (ZendCon 2016)

@asgrim

/**

* @ORM\Entity

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

*/

final class Book

{

/**

* @ORM\Id

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

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

* @var string

*/

private $id;

src/App/Entity/Book.php

Page 59: Kicking off with Zend Expressive and Doctrine ORM (ZendCon 2016)

@asgrim

/**

* @ORM\Entity

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

*/

final class Book

{

/**

* @ORM\Id

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

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

* @var string

*/

private $id;

src/App/Entity/Book.php

Page 60: Kicking off with Zend Expressive and Doctrine ORM (ZendCon 2016)

@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 61: Kicking off with Zend Expressive and Doctrine ORM (ZendCon 2016)

@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 62: Kicking off with Zend Expressive and Doctrine ORM (ZendCon 2016)

@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 63: Kicking off with Zend Expressive and Doctrine ORM (ZendCon 2016)

@asgrim

Insert some data

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

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

'The Great Escape',

true

);

Page 64: Kicking off with Zend Expressive and Doctrine ORM (ZendCon 2016)

@asgrim

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

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

Page 65: Kicking off with Zend Expressive and Doctrine ORM (ZendCon 2016)

@asgrim

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

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

Page 66: Kicking off with Zend Expressive and Doctrine ORM (ZendCon 2016)

@asgrim

Doing more with middleware

Page 67: Kicking off with Zend Expressive and Doctrine ORM (ZendCon 2016)

@asgrim

Authentication

Page 68: Kicking off with Zend Expressive and Doctrine ORM (ZendCon 2016)

@asgrim

public function __invoke(

Request $request, Response $response, callable $next = null

) : Response {

$queryParams = $request->getQueryParams();

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

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

return new JsonResponse(

['error' => 'You are not authenticated.'],

403

);

}

return $next($request, $response);

}

Create the middleware

Page 69: Kicking off with Zend Expressive and Doctrine ORM (ZendCon 2016)

@asgrim

public function __invoke(

Request $request, Response $response, callable $next = null

) : Response {

$queryParams = $request->getQueryParams();

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

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

return new JsonResponse(

['error' => 'You are not authenticated.'],

403

);

}

return $next($request, $response);

}

Create the middleware

Page 70: Kicking off with Zend Expressive and Doctrine ORM (ZendCon 2016)

@asgrim

public function __invoke(

Request $request, Response $response, callable $next = null

) : Response {

$queryParams = $request->getQueryParams();

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

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

return new JsonResponse(

['error' => 'You are not authenticated.'],

403

);

}

return $next($request, $response);

}

Create the middleware

Page 71: Kicking off with Zend Expressive and Doctrine ORM (ZendCon 2016)

@asgrim

public function __invoke(

Request $request, Response $response, callable $next = null

) : Response {

$queryParams = $request->getQueryParams();

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

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

return new JsonResponse(

['error' => 'You are not authenticated.'],

403

);

}

return $next($request, $response);

}

Create the middleware

Page 72: Kicking off with Zend Expressive and Doctrine ORM (ZendCon 2016)

@asgrim

public function __invoke(

Request $request, Response $response, callable $next = null

) : Response {

$queryParams = $request->getQueryParams();

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

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

return new JsonResponse(

['error' => 'You are not authenticated.'],

403

);

}

return $next($request, $response);

}

Create the middleware

Page 73: Kicking off with Zend Expressive and Doctrine ORM (ZendCon 2016)

@asgrim

PSR7-Sessioncomposer require psr7-sessions/storageless

Page 74: Kicking off with Zend Expressive and Doctrine ORM (ZendCon 2016)

@asgrim

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

{

$symmetricKey = 'super-secure-key-you-should-not-store-this-key-in-git';

$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 75: Kicking off with Zend Expressive and Doctrine ORM (ZendCon 2016)

@asgrim

'routing' => [

'middleware' => [

ApplicationFactory::ROUTING_MIDDLEWARE,

Helper\UrlHelperMiddleware::class,

PSR7Session\Http\SessionMiddleware::class,

App\Middleware\AuthenticationMiddleware::class,

ApplicationFactory::DISPATCH_MIDDLEWARE,

],

'priority' => 1,

],

Add middleware to pipe

Page 76: Kicking off with Zend Expressive and Doctrine ORM (ZendCon 2016)

@asgrim

$session = $request->getAttribute(

SessionMiddleware::SESSION_ATTRIBUTE

);

$session->set(

'counter',

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

);

Session is stored in Request

Page 77: Kicking off with Zend Expressive and Doctrine ORM (ZendCon 2016)

@asgrim

To summarise...

● PSR-7 is important● Diactoros is just a PSR-7 implementation● Stratigility is a middleware pipeline● Expressive is a glue for container, router (and templating)● DoctrineModule can be used (with some fiddling)● container-interop-doctrine makes Doctrine work easily● Middleware gives you good controls

Page 78: Kicking off with Zend Expressive and Doctrine ORM (ZendCon 2016)

Any questions?

http://joind.in/

James Titcumb @asgrim