SlideShare a Scribd company logo
@asgrim
Kicking off with Zend Expressive
and Doctrine ORM
James Titcumb
php[MiNDS] March 2018
Follow along (optional): https://github.com/asgrim/book-library
$ whoami
James Titcumb
www.jamestitcumb.com
www.roave.com
@asgrim
@asgrim
What is Zend Expressive?
@asgrim
Layers of an Expressive application
Expressive
Stratigility
Diactoros
PSR-7 Interface
DIRouter Template
Your Application
@asgrim
Layers of an Expressive application
Expressive
Stratigility
Diactoros
PSR-7 Interface
DIRouter Template
Your Application
@asgrim
PSR-7
HTTP Message Interfaces
@asgrim
HTTP Request
POST /talk HTTP/1.1
Host: phpminds.org
foo=bar&baz=bat
@asgrim
HTTP Response
HTTP/1.1 200 OK
Content-Type: text/plain
This is the response body
@asgrim
Layers of an Expressive application
Expressive
Stratigility
Diactoros
PSR-7 Interface
DIRouter Template
Your Application
@asgrim
Zend Diactoros
PSR-7 implementation
@asgrim
Node http.Server using Diactoros
$server = ZendDiactorosServer::createServer(
function ($request, $response, $done) {
return $response->getBody()->write('hello world');
},
$_SERVER,
$_GET,
$_POST,
$_COOKIE,
$_FILES
);
$server->listen();
@asgrim
Layers of an Expressive application
Expressive
Stratigility
Diactoros
PSR-7 Interface
DIRouter Template
Your Application
@asgrim
Zend Stratigility
Creating & dispatching middleware pipelines
@asgrim
So what is “middleware”?
@asgrim
Middleware in the middle
function(Request $request, RequestHandler $handler) : Response
{
// ... some code before ...
$response = $handler->handle($request);
// ... some code after ...
return $response;
}
@asgrim
Some people call it an onion
SessionInitialisingMiddleware
AuthenticationMiddleware
ThingyMiddleware
DashboardHandler
@asgrim
Some people call it an onion
SessionInitialisingMiddleware
AuthenticationMiddleware
ThingyMiddleware
DashboardHandler
@asgrim
Some people call it an onion
SessionInitialisingMiddleware
AuthenticationMiddleware
ThingyMiddleware
DashboardHandler
@asgrim
psr/http-server-middleware
@asgrim
Middleware using MiddlewareInterface
<?php
declare(strict_types=1);
namespace AppMiddleware;
use PsrHttpMessageResponseInterface;
use PsrHttpMessageServerRequestInterface;
use PsrHttpServerMiddlewareInterface;
use PsrHttpServerRequestHandlerInterface;
final class MyMiddleware implements MiddlewareInterface
{
public function process(
ServerRequestInterface $request,
RequestHandlerInterface $requestHandler
) : ResponseInterface {
return $requestHandler>handle($request);
}
}
@asgrim
Double-pass middleware
public function __invoke(
Request $request,
Response $response,
callable $next
): Response {
return $next($request, $response);
}
!!! DEPRECATED !!!
@asgrim
Modifying the response
function(Request $request, RequestHandler $handler) : Response
{
$response = $handler->handle($request);
return $response->withHeader(
'X-Clacks-Overhead',
'GNU Terry Pratchett'
);
}
@asgrim
Pipe all the things!
$pipe = new ZendStratigilityMiddlewarePipe();
$pipe->pipe($logUncaughtErrorsMiddleware);
$pipe->pipe($sessionInitialisingMiddleware);
$pipe->pipe($authenticationMiddleware);
$pipe->pipe('/', $indexHandlerMiddleware);
$pipe->pipe(new NotFoundHandler(...));
@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
);
}
}
@asgrim
SessionInitialisingMiddleware
function(Request $request, RequestHandler $handler) : Response
{
session_start();
return $handler->handle($request);
}
@asgrim
function(Request $request, RequestHandler $handler) : Response
{
if (!$this->doSomeOAuthCheck($request)) {
return new JsonResponse(
[
'message' => 'Invalid OAuth token',
],
403
);
}
return $handler->handle($request);
}
AuthenticationMiddleware
@asgrim
IndexHandlerMiddleware
function(Request $request, RequestHandler $handler) : Response
{
// ... some code ...
return new JsonResponse($someData, 200);
}
@asgrim
Layers of an Expressive application
Expressive
Stratigility
Diactoros
PSR-7 Interface
DIRouter Template
Your Application
@asgrim
Zend Expressive
One Ring to bring them all and in the darkness bind them.
@asgrim
Routing
@asgrim
container-interop
@asgrim
Optionally, templating
@asgrim
Piping and Routing
@asgrim
Zend Framework 2/3
What of them?
@asgrim
Middleware vs MVC
@asgrim
Using ZF components
in Expressive
@asgrim
ZF’s Module.php
class Module
{
public function getConfig()
{
return include __DIR__ . '/../config/module.config.php';
}
}
@asgrim
ConfigProvider
@asgrim
ConfigProvider
namespace ZendForm;
class ConfigProvider
{
public function __invoke()
{
return [
'dependencies' => $this->getDependencyConfig(),
'view_helpers' => $this->getViewHelperConfig(),
];
}
}
@asgrim
ConfigProvider#getDependencyConfig()
public function getDependencyConfig()
{
return [
'abstract_factories' => [
FormAbstractServiceFactory::class,
],
'aliases' => [
'ZendFormAnnotationFormAnnotationBuilder' => 'FormAnnotationBuilder',
AnnotationAnnotationBuilder::class => 'FormAnnotationBuilder',
FormElementManager::class => 'FormElementManager',
],
'factories' => [
'FormAnnotationBuilder' => AnnotationAnnotationBuilderFactory::class,
'FormElementManager' => FormElementManagerFactory::class,
],
@asgrim
ZendForm’s Module.php (for Zend Framework)
class Module
{
public function getConfig()
{
$provider = new ConfigProvider();
return [
'service_manager' => $provider->getDependencyConfig(),
'view_helpers' => $provider->getViewHelperConfig(),
];
}
}
@asgrim
config/config.php
<?php
$aggregator = new ConfigAggregator([
ZendFormConfigProvider::class,
// .. other config ...
], $cacheConfig['config_cache_path']);
return $aggregator->getMergedConfig();
@asgrim
ConfigAggregator
@asgrim
config/config.php
$aggregator = new ConfigAggregator([
ZendExpressiveRouterFastRouteRouterConfigProvider::class,
ZendHttpHandlerRunnerConfigProvider::class,
new ArrayProvider($cacheConfig),
ZendExpressiveHelperConfigProvider::class,
ZendExpressiveConfigProvider::class,
ZendExpressiveRouterConfigProvider::class,
AppConfigProvider::class,
new PhpFileProvider(realpath(__DIR__) . '/autoload/{{,*.}global,{,*.}local}.php'),
new PhpFileProvider(realpath(__DIR__) . '/development.config.php'),
], $cacheConfig['config_cache_path']);
@asgrim
Layers of an Expressive application
Expressive
Stratigility
Diactoros
PSR-7 Interface
DIRouter Template
Your Application
@asgrim
Getting started
with Zend Expressive
@asgrim
https://github.com/asgrim/book-library
@asgrim
Expressive Skeleton
@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
> ExpressiveInstallerOptionalPackages::install
Setting up optional packages
Setup data and cache dir
Removing installer development dependencie
@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
@asgrim
Modular structure
zend-config-aggregator
@asgrim
Modular structure
zend-config-aggregator
@asgrim
Flat or Modular?
@asgrim
Default modular structure
src/
MyModule/
src/
ConfigProvider.php
Handler/
Entity/
Middleware/
templates/
test/
@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
@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
@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):
@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
@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/"
}
@asgrim
Create the endpoints
@asgrim
Book entity
class Book
{
/**
* @var string
*/
private $id;
/**
* @var bool
*/
private $inStock = true;
public function __construct()
{
$this->id = (string)Uuid::uuid4();
}
@asgrim
Book entity
class Book
{
/**
* @return void
* @throws AppEntityExceptionBookNotAvailable
*/
public function checkOut()
{
if (!$this->inStock) {
throw ExceptionBookNotAvailable::fromBook($this);
}
$this->inStock = false;
}
@asgrim
Book entity
class Book
{
/**
* @return void
* @throws AppEntityExceptionBookAlreadyStocked
*/
public function checkIn()
{
if ($this->inStock) {
throw ExceptionBookAlreadyStocked::fromBook($this);
}
$this->inStock = true;
}
@asgrim
FindBookByUuidInterface
interface FindBookByUuidInterface
{
/**
* @param UuidInterface $slug
* @return Book
* @throws ExceptionBookNotFound
*/
public function __invoke(UuidInterface $slug): Book;
}
@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()),
]);
}
@asgrim
return function (
Application $app,
MiddlewareFactory $factory,
ContainerInterface $container
): void {
$app->pipe(ZendStratigilityMiddlewareErrorHandler::class);
$app->pipe(ZendExpressiveRouterMiddlewarePathBasedRoutingMiddleware::class); // Routing
$app->pipe(ZendExpressiveRouterMiddlewareDispatchMiddleware::class); // Dispatch
$app->pipe(ZendExpressiveHandlerNotFoundHandler::class);
};
Define application pipeline - pipeline.php
@asgrim
return function (
Application $app,
MiddlewareFactory $factory,
ContainerInterface $container
): void {
$app->get(
'/book/{id}/check-out',
AppHandlerCheckOutHandler::class,
'check-out'
);
$app->get(
'/book/{id}/check-in',
AppHandlerCheckInHandler::class,
'check-in'
);
};
Define routes - routes.php
@asgrim
Adding some ORM
@asgrim
Your application
Doctrine quick overview
DB
DBAL
ORM
(EntityManager)
Entities
Finders,
Services,
...
@asgrim
container-interop-doctrine
@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
@asgrim
src/App/ConfigProvider.php
use DoctrineORMEntityManagerInterface;
use ContainerInteropDoctrineEntityManagerFactory;
final class ConfigProvider
{
// ...
public function getDependencies() : array
{
return [
'factories' => [
EntityManagerInterface::class => EntityManagerFactory::class,
// ... other services
],
];
@asgrim
'doctrine' => [
'connection' => [
'orm_default' => [
'driver_class' => PDOPgSqlDriver::class,
'params' => [
'url' => 'postgres://user:pass@localhost/book_library',
],
],
],
'driver' => [
'orm_default' => [
'class' => MappingDriverChain::class,
'drivers' => [
// ... and so on ...
config/autoload/doctrine.global.php
@asgrim
'doctrine' => [
'connection' => [
'orm_default' => [
'driver_class' => PDOPgSqlDriver::class,
'params' => [
'url' => 'postgres://user:pass@localhost/book_library',
],
],
],
'driver' => [
'orm_default' => [
'class' => MappingDriverChain::class,
'drivers' => [
// ... and so on ...
config/autoload/doctrine.global.php
@asgrim
<?php
declare(strict_types = 1);
use DoctrineORMEntityManagerInterface;
use DoctrineORMToolsConsoleHelperEntityManagerHelper;
use SymfonyComponentConsoleHelperHelperSet;
$container = require __DIR__ . '/container.php';
return new HelperSet([
'em' => new EntityManagerHelper(
$container->get(EntityManagerInterface::class)
),
]);
config/cli-config.php
@asgrim
Annotating the Entities
@asgrim
/**
* @ORMEntity
* @ORMTable(name="book")
*/
class Book
{
/**
* @ORMId
* @ORMColumn(name="id", type="guid")
* @ORMGeneratedValue(strategy="NONE")
* @var string
*/
private $id;
src/App/Entity/Book.php
@asgrim
/**
* @ORMEntity
* @ORMTable(name="book")
*/
class Book
{
/**
* @ORMId
* @ORMColumn(name="id", type="guid")
* @ORMGeneratedValue(strategy="NONE")
* @var string
*/
private $id;
src/App/Entity/Book.php
@asgrim
public function __invoke(UuidInterface $id): Book
{
/** @var Book|null $book */
$book = $this->repository->find((string)$id);
if (null === $book) {
throw ExceptionBookNotFound::fromUuid($id);
}
return $book;
}
src/App/Service/Book/DoctrineFindBookByUuid.php
@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
@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!
$
@asgrim
Insert some data
INSERT INTO book (id, name, in_stock) VALUES (
'1c06bec9-adae-47c2-b411-73b1db850e6f',
'The Great Escape',
true
);
@asgrim
/book/1c06bec9-adae-47c2-b411-.../check-out
{"info":"You have checked out The Great Escape"}
@asgrim
/book/1c06bec9-adae-47c2-b411-.../check-in
{"info":"You have checked in The Great Escape"}
@asgrim
Automatic Flushing Middleware
@asgrim
FlushingMiddleware
public function process(Request $request, RequestHandler $handler)
{
$response = $handler->handle($request);
if ($this->entityManager->isOpen()) {
$this->entityManager->flush();
}
return $response;
}
@asgrim
FlushingMiddleware
public function process(Request $request, RequestHandler $handler)
{
$response = $handler->handle($request);
if ($this->entityManager->isOpen()) {
$this->entityManager->flush();
}
return $response;
}
@asgrim
FlushingMiddleware
public function process(Request $request, RequestHandler $handler)
{
$response = $handler->handle($request);
if ($this->entityManager->isOpen()) {
$this->entityManager->flush();
}
return $response;
}
@asgrim
Add to pipeline
return function (
Application $app,
MiddlewareFactory $factory,
ContainerInterface $container
): void {
$app->pipe(ZendStratigilityMiddlewareErrorHandler::class);
$app->pipe(ZendExpressiveRouterMiddlewarePathBasedRoutingMiddleware::class); // Routing
$app->pipe(AppMiddlewareFlushingMiddleware::class);
$app->pipe(ZendExpressiveRouterMiddlewareDispatchMiddleware::class); // Dispatch
$app->pipe(ZendExpressiveHandlerNotFoundHandler::class);
};
@asgrim
Doing more with
middleware
@asgrim
Authentication
@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
@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
@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
@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
@asgrim
return function (
Application $app,
MiddlewareFactory $factory,
ContainerInterface $container
): void {
$app->pipe(ZendStratigilityMiddlewareErrorHandler::class);
$app->pipe(ZendExpressiveRouterMiddlewarePathBasedRoutingMiddleware::class); // Routing
$app->pipe(AppMiddlewareFlushingMiddleware::class);
$app->pipe(AppMiddlewareAuthenticationMiddleware::class);
$app->pipe(ZendExpressiveRouterMiddlewareDispatchMiddleware::class); // Dispatch
$app->pipe(ZendExpressiveHandlerNotFoundHandler::class);
};
Add middleware to pipe
@asgrim
Helios
composer require dasprid/helios
@asgrim
PSR7-Session
composer require psr7-sessions/storageless
@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 SignerHmacSha256(),
$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
@asgrim
return function (
Application $app,
MiddlewareFactory $factory,
ContainerInterface $container
): void {
$app->pipe(ZendStratigilityMiddlewareErrorHandler::class);
$app->pipe(ZendExpressiveRouterMiddlewarePathBasedRoutingMiddleware::class); // Routing
$app->pipe(AppMiddlewareFlushingMiddleware::class);
$app->pipe(PSR7SessionsStoragelessHttpSessionMiddleware::class);
$app->pipe(AppMiddlewareAuthenticationMiddleware::class);
$app->pipe(ZendExpressiveRouterMiddlewareDispatchMiddleware::class); // Dispatch
$app->pipe(ZendExpressiveHandlerNotFoundHandler::class);
};
Add middleware to pipe
@asgrim
$session = $request->getAttribute(SessionMiddleware::SESSION_ATTRIBUTE);
$session->set('counter', $session->get('counter', 0) + 1);
Session is stored in Request
@asgrim
$skills++;
@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)
@asgrim
Useful references
● https://github.com/asgrim/book-library
● https://framework.zend.com/blog/2017-12-14-expressive-3-dev.html
● 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/
Any questions?
James Titcumb
@asgrim

More Related Content

PDF
Kicking off with Zend Expressive and Doctrine ORM (PHP South Africa 2018)
PDF
Kicking off with Zend Expressive and Doctrine ORM (PHP Srbija 2017)
KEY
The use of Symfony2 @ Overblog
PDF
Tame cloud complexity with F# powered DSLs (build stuff)
PDF
Conscious Decoupling - Lone Star PHP
PDF
Documenting from the Trenches
ODP
Aspect-oriented programming in Perl
PDF
SPIFFE Meetup Tokyo #2 - Attestation Internals in SPIRE - Shingo Omura
Kicking off with Zend Expressive and Doctrine ORM (PHP South Africa 2018)
Kicking off with Zend Expressive and Doctrine ORM (PHP Srbija 2017)
The use of Symfony2 @ Overblog
Tame cloud complexity with F# powered DSLs (build stuff)
Conscious Decoupling - Lone Star PHP
Documenting from the Trenches
Aspect-oriented programming in Perl
SPIFFE Meetup Tokyo #2 - Attestation Internals in SPIRE - Shingo Omura

What's hot (20)

PPTX
Function pointer
PDF
Resting with OroCRM Webinar
PDF
OroCRM Partner Technical Training: September 2015
PPTX
Catch and Release: A New Look at Detecting and Mitigating highly obfuscated E...
PDF
Customizing oro crm webinar
PDF
Working with oro crm entities
PPTX
POLITEKNIK MALAYSIA
PPTX
Python Programming Essentials - M19 - Namespaces, Global Variables and Docstr...
PDF
Java EE 6 CDI Integrates with Spring & JSF
PDF
Sun certifiedwebcomponentdeveloperstudyguide
PPTX
Terraform Abstractions for Safety and Power
PPTX
Comprehensive Terraform Training
PDF
Intro to JavaScript
PDF
PHP 8: What's New and Changed
KEY
A Conversation About REST
PDF
Proxy Deep Dive Voxxed Belgrad 2015
PDF
Functional Javascript
PDF
Terraform - Taming Modern Clouds
PDF
Infrastructure as Code with Terraform
PDF
Adding custom ui controls to your application (1)
Function pointer
Resting with OroCRM Webinar
OroCRM Partner Technical Training: September 2015
Catch and Release: A New Look at Detecting and Mitigating highly obfuscated E...
Customizing oro crm webinar
Working with oro crm entities
POLITEKNIK MALAYSIA
Python Programming Essentials - M19 - Namespaces, Global Variables and Docstr...
Java EE 6 CDI Integrates with Spring & JSF
Sun certifiedwebcomponentdeveloperstudyguide
Terraform Abstractions for Safety and Power
Comprehensive Terraform Training
Intro to JavaScript
PHP 8: What's New and Changed
A Conversation About REST
Proxy Deep Dive Voxxed Belgrad 2015
Functional Javascript
Terraform - Taming Modern Clouds
Infrastructure as Code with Terraform
Adding custom ui controls to your application (1)
Ad

Similar to Kicking off with Zend Expressive and Doctrine ORM (PHP MiNDS March 2018) (20)

PDF
Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)
PDF
Kicking off with Zend Expressive and Doctrine ORM (ZendCon 2016)
PDF
Kicking off with Zend Expressive and Doctrine ORM (Sunshine PHP 2017)
PDF
Kicking off with Zend Expressive and Doctrine ORM (PHPNW2016)
PDF
Kicking off with Zend Expressive and Doctrine ORM (PHP UK 2017)
ODP
Building Web Services with Zend Framework (PHP Benelux meeting 20100713 Vliss...
PDF
Ростислав Михайлив "Zend Framework 3 - evolution or revolution"
PDF
Creating a modern web application using Symfony API Platform, ReactJS and Red...
PDF
Manage cloud infrastructures using Zend Framework 2 (and ZF1)
PDF
Foundations of Zend Framework
PDF
A quick start on Zend Framework 2
PPTX
Z ray plugins for dummies
PDF
Divide and Conquer – Microservices with Node.js
PPTX
express.js.pptxgghhhhhhnnbvcdssazxvuyiknvc
PDF
Express node js
PDF
Whoops! Where did my architecture go?
PPT
Red5 - PHUG Workshops
PDF
Workshop 17: EmberJS parte II
PDF
ZFConf 2012: Zend Framework 2, a quick start (Enrico Zimuel)
PDF
Zend Framework 2 quick start
Kicking off with Zend Expressive and Doctrine ORM (ConFoo YVR 2017)
Kicking off with Zend Expressive and Doctrine ORM (ZendCon 2016)
Kicking off with Zend Expressive and Doctrine ORM (Sunshine PHP 2017)
Kicking off with Zend Expressive and Doctrine ORM (PHPNW2016)
Kicking off with Zend Expressive and Doctrine ORM (PHP UK 2017)
Building Web Services with Zend Framework (PHP Benelux meeting 20100713 Vliss...
Ростислав Михайлив "Zend Framework 3 - evolution or revolution"
Creating a modern web application using Symfony API Platform, ReactJS and Red...
Manage cloud infrastructures using Zend Framework 2 (and ZF1)
Foundations of Zend Framework
A quick start on Zend Framework 2
Z ray plugins for dummies
Divide and Conquer – Microservices with Node.js
express.js.pptxgghhhhhhnnbvcdssazxvuyiknvc
Express node js
Whoops! Where did my architecture go?
Red5 - PHUG Workshops
Workshop 17: EmberJS parte II
ZFConf 2012: Zend Framework 2, a quick start (Enrico Zimuel)
Zend Framework 2 quick start
Ad

More from James Titcumb (20)

PDF
Living the Best Life on a Legacy Project (phpday 2022).pdf
PDF
Tips for Tackling a Legacy Codebase (ScotlandPHP 2021)
PDF
Climbing the Abstract Syntax Tree (Midwest PHP 2020)
PDF
Best practices for crafting high quality PHP apps (Bulgaria 2019)
PDF
Climbing the Abstract Syntax Tree (php[world] 2019)
PDF
Best practices for crafting high quality PHP apps (php[world] 2019)
PDF
Crafting Quality PHP Applications (PHP Joburg Oct 2019)
PDF
Climbing the Abstract Syntax Tree (PHP Russia 2019)
PDF
Best practices for crafting high quality PHP apps - PHP UK 2019
PDF
Climbing the Abstract Syntax Tree (ScotlandPHP 2018)
PDF
Best practices for crafting high quality PHP apps (ScotlandPHP 2018)
PDF
Best practices for crafting high quality PHP apps (PHP South Africa 2018)
PDF
Climbing the Abstract Syntax Tree (PHP Developer Days Dresden 2018)
PDF
Climbing the Abstract Syntax Tree (Southeast PHP 2018)
PDF
Crafting Quality PHP Applications (PHPkonf 2018)
PDF
Best practices for crafting high quality PHP apps (PHP Yorkshire 2018)
PDF
Crafting Quality PHP Applications: an overview (PHPSW March 2018)
PDF
Climbing the Abstract Syntax Tree (PHP UK 2018)
PDF
Crafting Quality PHP Applications (PHP Benelux 2018)
PDF
Crafting Quality PHP Applications (ConFoo YVR 2017)
Living the Best Life on a Legacy Project (phpday 2022).pdf
Tips for Tackling a Legacy Codebase (ScotlandPHP 2021)
Climbing the Abstract Syntax Tree (Midwest PHP 2020)
Best practices for crafting high quality PHP apps (Bulgaria 2019)
Climbing the Abstract Syntax Tree (php[world] 2019)
Best practices for crafting high quality PHP apps (php[world] 2019)
Crafting Quality PHP Applications (PHP Joburg Oct 2019)
Climbing the Abstract Syntax Tree (PHP Russia 2019)
Best practices for crafting high quality PHP apps - PHP UK 2019
Climbing the Abstract Syntax Tree (ScotlandPHP 2018)
Best practices for crafting high quality PHP apps (ScotlandPHP 2018)
Best practices for crafting high quality PHP apps (PHP South Africa 2018)
Climbing the Abstract Syntax Tree (PHP Developer Days Dresden 2018)
Climbing the Abstract Syntax Tree (Southeast PHP 2018)
Crafting Quality PHP Applications (PHPkonf 2018)
Best practices for crafting high quality PHP apps (PHP Yorkshire 2018)
Crafting Quality PHP Applications: an overview (PHPSW March 2018)
Climbing the Abstract Syntax Tree (PHP UK 2018)
Crafting Quality PHP Applications (PHP Benelux 2018)
Crafting Quality PHP Applications (ConFoo YVR 2017)

Recently uploaded (20)

PDF
Peak of Data & AI Encore- AI for Metadata and Smarter Workflows
PDF
Encapsulation_ Review paper, used for researhc scholars
PDF
Approach and Philosophy of On baking technology
PDF
Unlocking AI with Model Context Protocol (MCP)
PPTX
KOM of Painting work and Equipment Insulation REV00 update 25-dec.pptx
PDF
Review of recent advances in non-invasive hemoglobin estimation
PDF
How UI/UX Design Impacts User Retention in Mobile Apps.pdf
PDF
cuic standard and advanced reporting.pdf
PDF
Architecting across the Boundaries of two Complex Domains - Healthcare & Tech...
PDF
Machine learning based COVID-19 study performance prediction
PDF
Network Security Unit 5.pdf for BCA BBA.
PDF
NewMind AI Weekly Chronicles - August'25 Week I
PDF
The Rise and Fall of 3GPP – Time for a Sabbatical?
PPTX
Big Data Technologies - Introduction.pptx
PPTX
Digital-Transformation-Roadmap-for-Companies.pptx
PDF
Agricultural_Statistics_at_a_Glance_2022_0.pdf
PPTX
Effective Security Operations Center (SOC) A Modern, Strategic, and Threat-In...
PDF
CIFDAQ's Market Insight: SEC Turns Pro Crypto
PDF
Chapter 3 Spatial Domain Image Processing.pdf
PDF
Build a system with the filesystem maintained by OSTree @ COSCUP 2025
Peak of Data & AI Encore- AI for Metadata and Smarter Workflows
Encapsulation_ Review paper, used for researhc scholars
Approach and Philosophy of On baking technology
Unlocking AI with Model Context Protocol (MCP)
KOM of Painting work and Equipment Insulation REV00 update 25-dec.pptx
Review of recent advances in non-invasive hemoglobin estimation
How UI/UX Design Impacts User Retention in Mobile Apps.pdf
cuic standard and advanced reporting.pdf
Architecting across the Boundaries of two Complex Domains - Healthcare & Tech...
Machine learning based COVID-19 study performance prediction
Network Security Unit 5.pdf for BCA BBA.
NewMind AI Weekly Chronicles - August'25 Week I
The Rise and Fall of 3GPP – Time for a Sabbatical?
Big Data Technologies - Introduction.pptx
Digital-Transformation-Roadmap-for-Companies.pptx
Agricultural_Statistics_at_a_Glance_2022_0.pdf
Effective Security Operations Center (SOC) A Modern, Strategic, and Threat-In...
CIFDAQ's Market Insight: SEC Turns Pro Crypto
Chapter 3 Spatial Domain Image Processing.pdf
Build a system with the filesystem maintained by OSTree @ COSCUP 2025

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