Top Banner
The Real World Beyond the Blog Example Robert Lemke_
57

TYPO3 Flow: Beyond the Blog Example (Inspiring Flow 2013)

May 19, 2015

Download

Technology

Robert Lemke

This talks was a mixture of hints for your next Flow application and insights into features of TYPO3 Flow 2.0
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: TYPO3 Flow: Beyond the Blog Example (Inspiring Flow 2013)

The Real WorldBeyond the Blog Example

Robert Lemke_

Page 2: TYPO3 Flow: Beyond the Blog Example (Inspiring Flow 2013)

TEXT HERE

project founder of TYPO3 Flow and TYPO3 Neos

co-founder of the TYPO3 Association

coach, coder, consultant

36 years old

lives in Lübeck, Germany

1 wife, 2 daughters, 1 espresso machine

likes drumming

Page 3: TYPO3 Flow: Beyond the Blog Example (Inspiring Flow 2013)

"the leftovers talks"

Page 4: TYPO3 Flow: Beyond the Blog Example (Inspiring Flow 2013)

TEXT HERE

Page 5: TYPO3 Flow: Beyond the Blog Example (Inspiring Flow 2013)

Application Structure

Page 6: TYPO3 Flow: Beyond the Blog Example (Inspiring Flow 2013)
Page 7: TYPO3 Flow: Beyond the Blog Example (Inspiring Flow 2013)

Request / Responseand MVC

Page 8: TYPO3 Flow: Beyond the Blog Example (Inspiring Flow 2013)

$context = getenv('FLOW_CONTEXT') ?: (getenv('REDIRECT_FLOW_CONTEXT') ?: 'Development');$bootstrap = new \TYPO3\Flow\Core\Bootstrap($context);$bootstrap->run();

Page 9: TYPO3 Flow: Beyond the Blog Example (Inspiring Flow 2013)

/** * Bootstraps the minimal infrastructure, resolves a fitting request handler and * then passes control over to that request handler. * * @return void * @api */public function run() { Scripts::initializeClassLoader($this); Scripts::initializeSignalSlot($this); Scripts::initializePackageManagement($this);

$this->activeRequestHandler = $this->resolveRequestHandler(); $this->activeRequestHandler->handleRequest();}

Page 10: TYPO3 Flow: Beyond the Blog Example (Inspiring Flow 2013)

/** * Handles a HTTP request * * @return void */public function handleRequest() { $this->request = Request::createFromEnvironment(); $this->response = new Response();

$this->boot(); $this->resolveDependencies(); $this->request->injectSettings($this->settings);

$this->router->setRoutesConfiguration($this->routesConfiguration); $actionRequest = $this->router->route($this->request); $this->securityContext->setRequest($actionRequest);

$this->dispatcher->dispatch($actionRequest, $this->response);

$this->response->makeStandardsCompliant($this->request); $this->response->send();

$this->bootstrap->shutdown('Runtime'); $this->exit->__invoke();}

Page 11: TYPO3 Flow: Beyond the Blog Example (Inspiring Flow 2013)

/** * Dispatches a request to a controller * * @param \TYPO3\Flow\Mvc\RequestInterface $request The request to dispatch * @param \TYPO3\Flow\Mvc\ResponseInterface $response The response, to be modified by the controller * @return void * @throws \TYPO3\Flow\Mvc\Exception\InfiniteLoopException * @api */public function dispatch(RequestInterface $request, ResponseInterface $response) { $dispatchLoopCount = 0; while (!$request->isDispatched()) { $controller = $this->resolveController($request); try { $this->emitBeforeControllerInvocation($request, $response, $controller); $controller->processRequest($request, $response); $this->emitAfterControllerInvocation($request, $response, $controller); } catch (StopActionException $exception) { $this->emitAfterControllerInvocation($request, $response, $controller); if ($exception instanceof ForwardException) { $request = $exception->getNextRequest(); } elseif ($request->isMainRequest() === FALSE) { $request = $request->getParentRequest(); } } }}

Page 12: TYPO3 Flow: Beyond the Blog Example (Inspiring Flow 2013)

/** * Book controller for the RobertLemke.Example.Bookshop package */class BookController extends ActionController {

/** * @Flow\Inject * @var \RobertLemke\Example\Bookshop\Domain\Repository\BookRepository */ protected $bookRepository;

/** * Shows a single book object * * @param \RobertLemke\Example\Bookshop\Domain\Model\Book $book The book to show * @return void */ public function showAction(Book $book) { $this->view->assign('book', $book); }

}

Page 13: TYPO3 Flow: Beyond the Blog Example (Inspiring Flow 2013)

/** * Dispatches a request to a controller * * @param \TYPO3\Flow\Mvc\RequestInterface $request The request to dispatch * @param \TYPO3\Flow\Mvc\ResponseInterface $response The response, to be modified by the controller * @return void * @throws \TYPO3\Flow\Mvc\Exception\InfiniteLoopException * @api */public function dispatch(RequestInterface $request, ResponseInterface $response) { $dispatchLoopCount = 0; while (!$request->isDispatched()) { $controller = $this->resolveController($request); try { $this->emitBeforeControllerInvocation($request, $response, $controller); $controller->processRequest($request, $response); $this->emitAfterControllerInvocation($request, $response, $controller); } catch (StopActionException $exception) { $this->emitAfterControllerInvocation($request, $response, $controller); if ($exception instanceof ForwardException) { $request = $exception->getNextRequest(); } elseif ($request->isMainRequest() === FALSE) { $request = $request->getParentRequest(); } } }}

Page 14: TYPO3 Flow: Beyond the Blog Example (Inspiring Flow 2013)

/** * Handles a HTTP request * * @return void */public function handleRequest() { $this->request = Request::createFromEnvironment(); $this->response = new Response();

$this->boot(); $this->resolveDependencies(); $this->request->injectSettings($this->settings);

$this->router->setRoutesConfiguration($this->routesConfiguration); $actionRequest = $this->router->route($this->request); $this->securityContext->setRequest($actionRequest);

$this->dispatcher->dispatch($actionRequest, $this->response);

$this->response->makeStandardsCompliant($this->request); $this->response->send();

$this->bootstrap->shutdown('Runtime'); $this->exit->__invoke();}

Page 15: TYPO3 Flow: Beyond the Blog Example (Inspiring Flow 2013)

namespace Acme\Demo;

class InvoiceController extends \Acme\Demo\Controller\AbstractBaseController {

/** * GeneratePdf action * * @param \Acme\Demo\Domain\Model\Invoice $invoice * @param boolean $forward */ public function generatePdfAction(\Acme\Demo\Domain\Model\Invoice $invoice, $forward = TRUE) { $fopCommand = $this->settings['pdf']['fopCommand'];

$storageInvoiceFilename = $this->renderInvoiceFilename($invoice);

$outputPath = $this->environment->getPathToTemporaryDirectory() . 'Acme.Demo/';

$packageResourcesPath = $this->packageManager->getPackage('Acme.Demo')->getResourcesPath(); $xmlPathAndFilename = $outputPath . 'Xml/'. $storageInvoiceFilename . '.xml'; $configurationPathAndFilename = $outputPath . 'Configuration.xml'; $pdfPathAndFilename = $outputPath.'Pdf/'. $storageInvoiceFilename; $fontsPath = $packageResourcesPath . 'Private/Fop/Fonts/'; $xslPathAndFilename = $packageResourcesPath . 'Private/Fop/Xsl/Document.xsl';

if (!file_exists($outputPath . 'Pdf')){ Files::createDirectoryRecursively($outputPath . 'Pdf'); } if (!file_exists($outputPath . 'Xml')){ Files::createDirectoryRecursively($outputPath . 'Xml'); }

$standaloneView = new StandaloneView(); $standaloneView->setTemplatePathAndFilename('resource://Acme.Demo/Private/Fop/Xml/Document.xml'); $standaloneView->assign('invoice', $invoice);

file_put_contents($xmlPathAndFilename, $standaloneView->render());

$standaloneView = new StandaloneView(); $standaloneView->setTemplatePathAndFilename('resource://Acme.Demo/Private/Fop/Configuration.xml'); $standaloneView->assign('fontPath', $fontsPath);

file_put_contents($configurationPathAndFilename, $standaloneView->render());

$command = $fopCommand . ' -q -c ' . $configurationPathAndFilename . ' -xml ' . $xmlPathAndFilename . ' -xsl ' . $xslPathAndFilename . ' -pdf ' . $pdfPathAndFilename; exec($command);

$pdf = $this->resourceManager->importResource($pdfPathAndFilename, 'DemoInvoices'); $invoice->setPdf($pdf);

if($forward) { $this->forward('download', NULL, NULL, array('invoice' => $invoice)); } }

Page 16: TYPO3 Flow: Beyond the Blog Example (Inspiring Flow 2013)

namespace Acme\Demo;

class InvoiceController extends \Acme\Demo\Controller\AbstractBaseController {

/** * @var \Acme\Demo\Application\Service\InvoiceGenerator * @Flow\Inject */ protected $invoiceGenerator;

/** * GeneratePdf action * * @param \Acme\Demo\Domain\Model\Invoice $invoice * @param boolean $forward */ public function generatePdfAction(\Acme\Demo\Domain\Model\Invoice $invoice, $forward = TRUE) { $this->invoiceGeneratorService->generate($invoice, $pdf); $this->forward('download', NULL, NULL, array('invoice' => $invoice)); }

Page 17: TYPO3 Flow: Beyond the Blog Example (Inspiring Flow 2013)

Forms

Page 18: TYPO3 Flow: Beyond the Blog Example (Inspiring Flow 2013)

TEXT HERE

Page 19: TYPO3 Flow: Beyond the Blog Example (Inspiring Flow 2013)

HTTP Caches

Page 20: TYPO3 Flow: Beyond the Blog Example (Inspiring Flow 2013)

$this->response->getHeaders()->setCacheControlDirective('s-max-age', 100);

Page 21: TYPO3 Flow: Beyond the Blog Example (Inspiring Flow 2013)

Safe Request / method tunneling

Page 22: TYPO3 Flow: Beyond the Blog Example (Inspiring Flow 2013)

Network Working Group R. FieldingRequest for Comments: 2616 UC IrvineObsoletes: 2068 J. GettysCategory: Standards Track Compaq/W3C J. Mogul Compaq H. Frystyk W3C/MIT L. Masinter Xerox P. Leach Microsoft T. Berners-Lee W3C/MIT June 1999

Hypertext Transfer Protocol -- HTTP/1.1

Status of this Memo

This document specifies an Internet standards track protocol for the Internet community, and requests discussion and suggestions for improvements. Please refer to the current edition of the "Internet Official Protocol Standards" (STD 1) for the standardization state and status of this protocol. Distribution of this memo is unlimited.

Copyright Notice

Copyright (C) The Internet Society (1999). All Rights Reserved.

Abstract

The Hypertext Transfer Protocol (HTTP) is an application-level protocol for distributed, collaborative, hypermedia information systems. It is a generic, stateless, protocol which can be used for many tasks beyond its use for hypertext, such as name servers and distributed object management systems, through extension of its request methods, error codes and headers [47]. A feature of HTTP is the typing and negotiation of data representation, allowing systems to be built independently of the data being transferred.

HTTP has been in use by the World-Wide Web global information initiative since 1990. This specification defines the protocol referred to as "HTTP/1.1", and is an update to RFC 2068 [33].

Page 23: TYPO3 Flow: Beyond the Blog Example (Inspiring Flow 2013)

9.1 Safe and Idempotent Methods

9.1.1 Safe Methods

Implementors should be aware that the software represents the user in their interactions over the Internet, and should be careful to allow the user to be aware of any actions they might take which may have an unexpected significance to themselves or others.

In particular, the convention has been established that the GET and HEAD methods SHOULD NOT have the significance of taking an action other than retrieval. These methods ought to be considered "safe". This allows user agents to represent other methods, such as POST, PUT and DELETE, in a special way, so that the user is made aware of the fact that a possibly unsafe action is being requested.

Naturally, it is not possible to ensure that the server does not generate side-effects as a result of performing a GET request; in fact, some dynamic resources consider that a feature. The important distinction here is that the user did not request the side-effects, so therefore cannot be held accountable for them.

9.1.2 Idempotent Methods

Methods can also have the property of "idempotence" in that (aside from error or expiration issues) the side-effects of N > 0 identical requests is the same as for a single request. The methods GET, HEAD, PUT and DELETE share this property. Also, the methods OPTIONS and TRACE SHOULD NOT have side effects, and so are inherently idempotent.However, it is possible that a sequence of several requests is non- idempotent, even if all of the methods executed in that sequence are idempotent. (A sequence is idempotent if a single execution of the entire sequence always yields a result that is not changed by a reexecution of all, or part, of that sequence.) For example, a sequence is non-idempotent if its result depends on a value that is later modified in the same sequence.A sequence that never has side effects is idempotent, by definition (provided that no concurrent operations are being executed on the same set of resources).

Page 24: TYPO3 Flow: Beyond the Blog Example (Inspiring Flow 2013)

no automatic persistence

Page 25: TYPO3 Flow: Beyond the Blog Example (Inspiring Flow 2013)

CSRF

Page 26: TYPO3 Flow: Beyond the Blog Example (Inspiring Flow 2013)

<a href="http://myserver.com/book/amazing-apps-with-flow/delete">Delete Book</a>

Page 27: TYPO3 Flow: Beyond the Blog Example (Inspiring Flow 2013)

<a href="http://myserver.com/book/amazing-apps-with-flow/delete?__csrfToken=abcdef1234567890">Delete Book</a>

Page 28: TYPO3 Flow: Beyond the Blog Example (Inspiring Flow 2013)

<form enctype="multipart/form-data" name="newBook" action="book/create" method="post">

<input type="hidden" name="__trustedProperties" value="a:1:{s:7:&quot;newBook&quot;;a:6: <input type="hidden" name="__csrfToken" value="10fa21087d49e5bb37d9c91248ea693a"/>

...

Page 29: TYPO3 Flow: Beyond the Blog Example (Inspiring Flow 2013)

speed!

Page 30: TYPO3 Flow: Beyond the Blog Example (Inspiring Flow 2013)

Lazy Dependency Injection

Page 31: TYPO3 Flow: Beyond the Blog Example (Inspiring Flow 2013)

class BookController extends ActionController {

/** * @Flow\Inject * @var BookRepository */ protected $bookRepository;

}

Page 32: TYPO3 Flow: Beyond the Blog Example (Inspiring Flow 2013)

class BookController extends ActionController {

… public function myAction() { // $this->bookRepository is instance of Dependency Proxy $this->bookRepository->findAll();

// $this->bookRepository is the real BookRepository

}

}

Page 33: TYPO3 Flow: Beyond the Blog Example (Inspiring Flow 2013)

$greet = function($name) { printf("Hello %s", $name);};

$greet('World');

Page 34: TYPO3 Flow: Beyond the Blog Example (Inspiring Flow 2013)

class BookController extends BookController_Original implements ProxyInterface {

/** * Autogenerated Proxy Method */ public function __construct() { $this->Flow_Proxy_injectProperties(); }

Page 35: TYPO3 Flow: Beyond the Blog Example (Inspiring Flow 2013)

$bookRepository_reference = &$this->bookRepository;

$this->bookRepository = Bootstrap::$staticObjectManager->getLazyDependencyByHash('d0e87f8f658d7866eec63db44a6918b4', $bookRepository_reference);

if ($this->bookRepository === NULL) { $this->bookRepository = Bootstrap::$staticObjectManager

->createLazyDependency('d0e87f8f658d7866eec63db44a6918b4', $bookRepository_reference, 'RobertLemke\Example\Bookshop\Domain\Repository\BookRepository', function() {

return Bootstrap::$staticObjectManager->get('RobertLemke\Example\Bookshop\Domain\Repository\BookRepository'

); });

}

Page 36: TYPO3 Flow: Beyond the Blog Example (Inspiring Flow 2013)

class BookController extends ActionController {

… public function myAction() { $this->bookRepository->findAll(); }

}

Page 37: TYPO3 Flow: Beyond the Blog Example (Inspiring Flow 2013)

class DependencyProxy {

… /** * Proxy magic call method which triggers the injection of the real dependency * and returns the result of a call to the original method in the dependency * * @param string $methodName Name of the method to be called * @param array $arguments An array of arguments to be passed to the method * @return mixed */ public function __call($methodName, array $arguments) { return call_user_func_array(array($this->_activateDependency(), $methodName), $arguments); }

/** * Activate the dependency and set it in the object. * * @return object The real dependency object * @api */ public function _activateDependency() { $realDependency = $this->builder->__invoke(); foreach($this->propertyVariables as &$propertyVariable) { $propertyVariable = $realDependency; } return $realDependency; }

}

Page 38: TYPO3 Flow: Beyond the Blog Example (Inspiring Flow 2013)

Accounts, Users, Authentication

Flow distinguishes between accounts and persons:

_ account: \TYPO3\Flow\Security\Account

_ person: \TYPO3\Party\Domain\Model\Person

A person (or machine) can have any number of accounts.

Page 39: TYPO3 Flow: Beyond the Blog Example (Inspiring Flow 2013)

Creating Accounts

_ always use the AccountFactory

_ create a party (eg. a Person) separately

_ assign the account to the party

_ add account and party to their respective repositories

Page 40: TYPO3 Flow: Beyond the Blog Example (Inspiring Flow 2013)

$account = $this->accountFactory->createAccountWithPassword( $accountIdentifier, $password, array($role));

$this->accountRepository->add($account);

$person = new Person();$person->addAccount($account);

$name = new PersonName('', 'Robert', '', 'Lemke');$person->setName($name);

$this->partyRepository->add($person);

Page 41: TYPO3 Flow: Beyond the Blog Example (Inspiring Flow 2013)

Roles

Page 42: TYPO3 Flow: Beyond the Blog Example (Inspiring Flow 2013)

roles: User: [] Manager: ['User'] Editor: ['User', 'TYPO3.Neos:Editor']

Page 43: TYPO3 Flow: Beyond the Blog Example (Inspiring Flow 2013)
Page 44: TYPO3 Flow: Beyond the Blog Example (Inspiring Flow 2013)
Page 45: TYPO3 Flow: Beyond the Blog Example (Inspiring Flow 2013)

/** * Create a role and return a role instance for it. * * @param string $roleIdentifier * @return \TYPO3\Flow\Security\Policy\Role * @throws \TYPO3\Flow\Security\Exception\RoleExistsException */public function createRole($roleIdentifier) { $this->initializeRolesFromPolicy();

if (isset($this->systemRoles[$roleIdentifier])) { throw new RoleExistsException(sprintf('Could not create role %s because a system role with }

if (preg_match('/^[\w]+((\.[\w]+)*\:[\w]+)+$/', $roleIdentifier) !== 1) { throw new \InvalidArgumentException(sprintf('Could not create role %s because it does not }

if ($this->roleRepository->findByIdentifier($roleIdentifier) !== NULL) { throw new RoleExistsException(sprintf('Could not create role %s because a role with that }

$role = new Role($roleIdentifier); $this->roleRepository->add($roleIdentifier);

return $role;}

Page 46: TYPO3 Flow: Beyond the Blog Example (Inspiring Flow 2013)

Virtual Browser

Page 47: TYPO3 Flow: Beyond the Blog Example (Inspiring Flow 2013)

/** * @Flow\Inject * @var \TYPO3\Flow\Http\Client\Browser */protected $browser;

/** * @return array */public function getBookInfo($isbn) { $this->browser->setRequestEngine(new CurlEngine()); $response = $this->browser->request(

'http://isbndb.com/api/books.xml?index1=isbn&value1=' . $isbn); $xml = simplexml_load_string($response->getContent());

return $bookInfo;}

Page 48: TYPO3 Flow: Beyond the Blog Example (Inspiring Flow 2013)

Page Cache

Page 49: TYPO3 Flow: Beyond the Blog Example (Inspiring Flow 2013)

/** * Shows a list of books * * @return void */public function indexAction() { $this->view->assign('books', $books);}

Page 50: TYPO3 Flow: Beyond the Blog Example (Inspiring Flow 2013)

RobertLemke_Example_Bookshop_Html: frontend: TYPO3\Flow\Cache\Frontend\StringFrontend backend: TYPO3\Flow\Cache\Backend\FileBackend

Page 51: TYPO3 Flow: Beyond the Blog Example (Inspiring Flow 2013)

/** * Shows a list of books * * @return string */public function indexAction() { $output = $this->htmlCache->get('BookController_index'); if ($output === FALSE) { $books = $this->bookRepository->findAll(); $this->view->assign('books', $books); $output = $this->view->render(); $this->htmlCache->set('BookController_index', $output); } return $output;}

Page 52: TYPO3 Flow: Beyond the Blog Example (Inspiring Flow 2013)

RobertLemke\Example\Bookshop\Controller\BookController: properties: htmlCache: object: factoryObjectName: TYPO3\Flow\Cache\CacheManager factoryMethodName: getCache arguments: 1: value: 'RobertLemke_Example_Bookshop_Html'

Page 53: TYPO3 Flow: Beyond the Blog Example (Inspiring Flow 2013)

Ask me anything *

* technical

Page 55: TYPO3 Flow: Beyond the Blog Example (Inspiring Flow 2013)

TYPO3 Flow Trainings and Inhouse Workshops

Page 56: TYPO3 Flow: Beyond the Blog Example (Inspiring Flow 2013)

Robert Lemke Blog

Page 57: TYPO3 Flow: Beyond the Blog Example (Inspiring Flow 2013)

Robert Lemke_robertlemke.com@robertlemke