TYPO3 Flow 2.0 Workshop T3BOARD13

69
TYPO3 Flow 2.0 Robert Lemke_

description

Slides of the TYPO3 Flow 2.0 fundamentals workshop which took place at Schmittenhöhe, Austria during T3BOARD13

Transcript of TYPO3 Flow 2.0 Workshop T3BOARD13

Page 1: TYPO3 Flow 2.0 Workshop T3BOARD13

TYPO3 Flow 2.0

Robert Lemke_

Page 2: TYPO3 Flow 2.0 Workshop T3BOARD13

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 2.0 Workshop T3BOARD13

TYPO3 Flow Website and Download

Page 4: TYPO3 Flow 2.0 Workshop T3BOARD13

Installation via Composer

$ curl -s https://getcomposer.org/installer | php$ sudo mv composer.phar /usr/local/bin/composer

$ composer create-project --stability="beta" --dev typo3/flow-base-distribution MyProject

Page 5: TYPO3 Flow 2.0 Workshop T3BOARD13

Set File Permissions

$ sudo ./flow core:setfilepermissions robert _www _wwwTYPO3 Flow File Permission Script

Checking permissions from here upwards.Making sure Data and Web/_Resources exist.Setting file permissions, trying to set ACLs via chmod ...Done.

$ sudo usermod -a -G www-data robert

$ sudo dscl . -append /Groups/_www GroupMembership robert

Linux:

Mac OS X:

Page 6: TYPO3 Flow 2.0 Workshop T3BOARD13

Set Up Database Connection

Configuration/Settings.yaml

TYPO3: Flow: persistence: backendOptions: host: '127.0.0.1' # adjust to your database host dbname: 'training' # adjust to your database name user: 'root' # adjust to your database user password: 'password' # adjust to your database password

# if you want to log executed SQL queries, enable the next 2 lines # doctrine: # sqlLogger: 'TYPO3\Flow\Persistence\Doctrine\Logging\SqlLogger'

# You might need to uncomment the following lines and specify # the location of the PHP binary manually. # core: # phpBinaryPathAndFilename: 'C:/path/to/php.exe'

Page 7: TYPO3 Flow 2.0 Workshop T3BOARD13

Set Up Virtual Host

Apache Virtual Host

<VirtualHost *:80> DocumentRoot ~/Sites/Flow/Web/ ServerName flow.dev SetEnv FLOW_CONTEXT Development</VirtualHost>

<VirtualHost *:80> DocumentRoot ~/Sites/Flow/Web/ ServerName flow.prod SetEnv FLOW_CONTEXT Production</VirtualHost>

Page 8: TYPO3 Flow 2.0 Workshop T3BOARD13

Final Check

Page 9: TYPO3 Flow 2.0 Workshop T3BOARD13

Command Line Use

$ ./flow help kickstart:package

Kickstart a new package

COMMAND: typo3.kickstart:kickstart:package

USAGE: ./flow kickstart:package <package key>

ARGUMENTS: --package-key The package key, for example "MyCompany.MyPackageName"

DESCRIPTION: Creates a new package and creates a standard Action Controller and a sample template for its Index Action. For creating a new package without sample code use the package:create command.

SEE ALSO: typo3.flow:package:create (Create a new package)

Page 10: TYPO3 Flow 2.0 Workshop T3BOARD13

Command Line Use

$ ./flow help kickstart:actioncontroller

Kickstart a new action controller

COMMAND: typo3.kickstart:kickstart:actioncontroller

USAGE: ./flow kickstart:actioncontroller [<options>] <package key> <controller name>

ARGUMENTS: --package-key The package key of the package for the new controller with an optional subpackage, (e.g. "MyCompany.MyPackage/Admin"). --controller-name The name for the new controller. This may also be a comma separated list of controller names.

OPTIONS: --generate-actions Also generate index, new, create, edit, update and delete actions. --generate-templates Also generate the templates for each action. --generate-related Also create the mentioned package, related model and repository if neccessary. --force Overwrite any existing controller or template code. Regardless of this flag, the package, model and repository will never be overwritten.

DESCRIPTION: Generates an Action Controller with the given name in the specified package. In its default mode it will create just the controller containing a sample indexAction. By specifying the --generate-actions flag, this command will also create a set of actions. If no model or repository exists which matches the controller name (for example "CoffeeRepository" for "CoffeeController"), an error will be shown. Likewise the command exits with an error if the specified package does not exist. By using the --generate-related flag, a missing package, model or repository can be created alongside, avoiding such an error. By specifying the --generate-templates flag, this command will also create matching Fluid templates for the actions created. This option can only be used in combination with --generate-actions. The default behavior is to not overwrite any existing code. This can be overridden by specifying the --force flag.

SEE ALSO: typo3.kickstart:kickstart:commandcontroller (Kickstart a new command controller)

Page 11: TYPO3 Flow 2.0 Workshop T3BOARD13

Biggest Book Store: Amazon

Page 12: TYPO3 Flow 2.0 Workshop T3BOARD13

Biggest River: Amazon River

© Google

Page 13: TYPO3 Flow 2.0 Workshop T3BOARD13

Smallest River: Roe River

© Google

Page 14: TYPO3 Flow 2.0 Workshop T3BOARD13

Smallest River: Roe River

© Google

Page 15: TYPO3 Flow 2.0 Workshop T3BOARD13

Smallest River: Roe River

© Google

Page 16: TYPO3 Flow 2.0 Workshop T3BOARD13

Smallest River: Roe River

Page 17: TYPO3 Flow 2.0 Workshop T3BOARD13

Smallest Book Store: Roebooks

Page 18: TYPO3 Flow 2.0 Workshop T3BOARD13

Sketchy Model

Page 19: TYPO3 Flow 2.0 Workshop T3BOARD13

Robert LemkeD.P. Fluxtr

time();

5 1 12

Hello World …

Page 20: TYPO3 Flow 2.0 Workshop T3BOARD13

Object Management

Dependency Injection

_ a class doesn't create or retrieve the instance of another class but get's it injected

_ fosters loosely-coupling and high cohesion

_ more stable, reusable code

Page 21: TYPO3 Flow 2.0 Workshop T3BOARD13

<?php

class SomeService { protected static $instance; public function getInstance() { if (self::$instance === NULL) { self::$instance = new self; } return self::$instance; }}

class SomeOtherController { public function action() { $service = SomeService::getInstance(); … } }

?>

Page 22: TYPO3 Flow 2.0 Workshop T3BOARD13

class ServiceLocator { protected static $services = array(); public function getInstance($name) { return self::$service[$name]; }

}

class SomeOtherController { public function action() { $service = ServiceLocator::getInstance("SomeService"); … } }

Page 23: TYPO3 Flow 2.0 Workshop T3BOARD13

class BookController extends ActionController {

/** * @var BookRepository */ protected $bookRepository; /** * @param BookRepository $bookRepository */ public function __construct(BookRepository $bookRepository) { $this->bookRepository = $bookRepository; } }

Page 24: TYPO3 Flow 2.0 Workshop T3BOARD13

class BookController extends ActionController {

/** * @var BookRepository */ protected $bookRepository; /** * @param BookRepository $bookRepository */ public function injectBookRepository(BookRepository $bookRepository) { $this->bookRepository = $bookRepository; } }

Page 25: TYPO3 Flow 2.0 Workshop T3BOARD13

class BookController extends ActionController {

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

}

Page 26: TYPO3 Flow 2.0 Workshop T3BOARD13

TYPO3\Flow\Security\Cryptography\RsaWalletServiceInterface: className: TYPO3\Flow\Security\Cryptography\RsaWalletServicePhp scope: singleton properties: keystoreCache: object: factoryObjectName: TYPO3\FLOW3\Cache\CacheManager factoryMethodName: getCache arguments: 1: value: FLOW3_Security_Cryptography_RSAWallet

Page 27: TYPO3 Flow 2.0 Workshop T3BOARD13

Object Management

Flow's take on Dependency Injection

_ one of the first PHP implementations(started in 2006, improved ever since)

_ object management for the whole lifecycle of all objects

_ no unnecessary configuration if information can be gatered automatically (autowiring)

_ intuitive use and no bad magical surprises

_ fast! (like hardcoded or faster)

Page 28: TYPO3 Flow 2.0 Workshop T3BOARD13

class Customer {

/** * @Flow\Inject * @var \Acme\CustomerNumberGenerator */ protected $customerNumberGenerator; ...}

$customer = new Customer();$customer->getCustomerNumber();

Page 29: TYPO3 Flow 2.0 Workshop T3BOARD13

Object Management

<?phpdeclare(ENCODING = 'utf-8');namespace TYPO3\Conference\Domain\Model\Conference;use TYPO3\Flow\Annotations as Flow;/** * Autogenerated Proxy Class * @Flow\Scope(“prototype”) * @Flow\Entity */class Paper extends Paper_Original implements \TYPO3\Flow\Object\Proxy\ProxyInterface, \TYPO3\Flow\Persistence\Aspect\PersistenceMagicInterface { /** * @var string * @ORM\Id * @ORM\Column(length="40") * introduced by TYPO3\Flow\Persistence\Aspect\PersistenceMagicAspect */ protected $Flow_Persistence_Identifier = NULL; private $Flow_AOP_Proxy_targetMethodsAndGroupedAdvices = array(); private $Flow_AOP_Proxy_groupedAdviceChains = array(); private $Flow_AOP_Proxy_methodIsInAdviceMode = array();

/** * Autogenerated Proxy Method */ public function __construct() { $this->Flow_AOP_Proxy_buildMethodsAndAdvicesArray(); if (isset($this->Flow_AOP_Proxy_methodIsInAdviceMode['__construct'])) { parent::__construct();

} else {

Flow creates proxy classesfor realizing DI and AOP magic

_ new operator is supported

_ proxy classes are created on the fly

_ in production context all code is static

Page 30: TYPO3 Flow 2.0 Workshop T3BOARD13

Object Scope

_ prototype: multiple instances for one request

_ singleton: one unique instance for one request

_ session: one unique instance for one session

_ default scope: prototype.

/** * @Flow\Scope("prototype") */class BookController extends ActionController {

Page 31: TYPO3 Flow 2.0 Workshop T3BOARD13

Lifecycle Methods

/** * Called after the object has been constructed and all * dependencies have been injected * * @param integer $initializationCause * @return void */ public function initializeObject($initializationCause) { switch ($initializationCause) { case ObjectManagerInterface::INITIALIZATIONCAUSE_CREATED : … case ObjectManagerInterface::INITIALIZATIONCAUSE_RECREATED : … } }

Page 32: TYPO3 Flow 2.0 Workshop T3BOARD13

Lifecycle Methods

/** * Called shortly before the framework shuts down */ public function shutdownObject() { }

Page 33: TYPO3 Flow 2.0 Workshop T3BOARD13

Aspect-Oriented Programming

_ programming paradigm

_ separates concerns to improve modularization

_ OOP modularizes concerns into objects

_ AOP modularizes cross-cutting concerns into aspects

_ FLOW3 makes it easy (and possible at all)to use AOP in PHP

Page 34: TYPO3 Flow 2.0 Workshop T3BOARD13

AOP

FLOW3 uses AOP for ...

_ persistence magic

_ logging

_ debugging

_ security

/** * @Aspect * @Introduce TYPO3\Flow\Persistence\Aspect\PersistenceMagicInterface, TYPO3\Flow\Persistence

*/class PersistenceMagicAspect { /** * @Pointcut classTaggedWith(entity) || classTaggedWith(valueobject) */ public function isEntityOrValueObject() {} /** * After returning advice, making sure we have an UUID for each and every entity.

* * @param \TYPO3\Flow\AOP\JoinPointInterface $joinPoint The current join point

* @return void * @Before classTaggedWith(entity) && method(.*->__construct()) */ public function generateUUID(JoinPointInterface $joinPoint) { $proxy = $joinPoint->getProxy(); ObjectAccess::setProperty($proxy, 'Flow_Persistence_Identifier', … }

Page 35: TYPO3 Flow 2.0 Workshop T3BOARD13
Page 36: TYPO3 Flow 2.0 Workshop T3BOARD13

Aspect

_ part of the application where cross-cutting concerns are implemented

_ in Flow aspects are classes annotated with@Flow\Aspect

Page 37: TYPO3 Flow 2.0 Workshop T3BOARD13

Join Point

A single point in the call graph

_ method execution

_ exception

Page 38: TYPO3 Flow 2.0 Workshop T3BOARD13

Join Point

A single point in the call graph

_ method execution

_ exception

Represents an event, not a location

Page 39: TYPO3 Flow 2.0 Workshop T3BOARD13

Pointcut

A set of join points where advices could be executed

_ can be composed

_ can be named

Page 40: TYPO3 Flow 2.0 Workshop T3BOARD13

Advice

Action to take at a join points defined by the point cut

Page 41: TYPO3 Flow 2.0 Workshop T3BOARD13

Kinds of Advice

Advice types supported by Flow:

@Flow\Before@Flow\AfterReturning@Flow\AfterThrowing@Flow\After@Flow\Around

Page 42: TYPO3 Flow 2.0 Workshop T3BOARD13

Pointcut Designators

method(Acme\Demo\MyClass->myMethod())class(Acme\Demo\MyClass)within(Acme\Demo\MyInterface)classAnnotatedWith(someTag)methodAnnotatedWith(anotherTag)setting(Acme.Demo.SomeSetting = "yeah, do it")filter(Acme\Demo\MyCustomFilterImplementation)

evaluate(coffe.kind = "Arabica")

Page 43: TYPO3 Flow 2.0 Workshop T3BOARD13

namespace TYPO3\Flow\Session\Aspect;use TYPO3\Flow\Annotations as Flow;

/** * An aspect which centralizes the logging of important session actions. * * @Flow\Aspect * @Flow\Scope("singleton") */class LoggingAspect {

/** * @var \TYPO3\Flow\Log\SystemLoggerInterface * @Flow\Inject */ protected $systemLogger;

/** * Logs calls of start() * * @Flow\After("within(TYPO3\Flow\Session\SessionInterface) && method(.*->start())") * @param \TYPO3\Flow\Aop\JoinPointInterface $joinPoint The current joinpoint */ public function logStart(\TYPO3\Flow\Aop\JoinPointInterface $joinPoint) { $session = $joinPoint->getProxy(); if ($session->isStarted()) { $this->systemLogger->log(sprintf('Started session with id %s', $session->getId()), LOG_DEBUG);

} }

Page 44: TYPO3 Flow 2.0 Workshop T3BOARD13

Persistence

Object Persistence in the Flow

_ based on Doctrine 2

_ seamless integration into Flow

_ provides all the great Doctrine 2 features

_ uses UUIDs

_ low level persistence API

_ allows for own, custom persistence backends (instead of Doctrine 2)

_ e.g. CouchDB, Solr

Page 45: TYPO3 Flow 2.0 Workshop T3BOARD13

// Create a new customer and persist it:$customer = new Customer("Robert");$this->customerRepository->add($customer);

// Find an existing customer:$otherCustomer = $this->customerRepository->findByFirstName("Karsten");

// and delete it:$this->customerRepository->remove($otherCustomer);

Page 46: TYPO3 Flow 2.0 Workshop T3BOARD13

Annotations

In order to use less code, the following examples assume that annotations have been imported directly:

use TYPO3\Flow\Annotations\Entity;

/** * @Entity */class Foo {}

Page 47: TYPO3 Flow 2.0 Workshop T3BOARD13

Validation and Doctrine Annotations

/** * @Entity */class Blog {

/** * @var string * @Validate Text, StringLength(minimum = 1, maximum = 80) * @Column(length="80") */ protected $title;

/** * @var \Doctrine\Common\Collections\Collection<\TYPO3\Blog\Domain\Model\Post> * @OneToMany(mappedBy="blog") * @OrderBy({"date" = "DESC"}) */ protected $posts;

...

}

Page 48: TYPO3 Flow 2.0 Workshop T3BOARD13

Persistence-related Annotations

@Entity Declares a class as "entity"

@Column Controls the database column related to the class property. Very useful for longer text content (type="text" !)

@ManyToOne @OneToMany @ManyToMany@OneToOne

Defines relations to other entities. Unlike with vanilla Doctrine targetEntity does not have to be given but will be reused from the @var annotation.

cascade can be used to cascade operation to related objects.

Page 49: TYPO3 Flow 2.0 Workshop T3BOARD13

@var Defines the type of a property, collections can be typed using angle brackets

Collection<\TYPO3\Conference\Domain\Model\Comment>

@transient The property will be ignored, it will neither be persisted nor reconstituted

@identity Marks the property as part of an objects identity

Persistence-related Annotations

Page 50: TYPO3 Flow 2.0 Workshop T3BOARD13

Custom Queries using theQuery Object Model

class PostRepository extends Repository {

/** * Finds posts by the specified tag and blog * * @param \TYPO3\Blog\Domain\Model\Tag $tag * @param \TYPO3\Blog\Domain\Model\Blog $blog The blog the post must refer to * @return \TYPO3\Flow\Persistence\QueryResultInterface The posts */ public function findByTagAndBlog(\TYPO3\Blog\Domain\Model\Tag $tag, \TYPO3\Blog\Domain\Model\Blog $blog) { $query = $this->createQuery(); return $query->matching( $query->logicalAnd( $query->equals('blog', $blog), $query->contains('tags', $tag) ) ) ->setOrderings(array( 'date' => \TYPO3\Flow\Persistence\QueryInterface::ORDER_DESCENDING) ) ->execute(); }}

Page 51: TYPO3 Flow 2.0 Workshop T3BOARD13

Schema Management

Doctrine 2 Migrations

_ Migrations allow schema versioning and change deployment

_ Migrations are the recommended way for DB updates

_ Tools to create and deploy migrations are integrated with Flow

Page 52: TYPO3 Flow 2.0 Workshop T3BOARD13

Schema Management

Executing migration scripts

Needed after installation or upgrade:

$ ./flow doctrine:migrate

Page 53: TYPO3 Flow 2.0 Workshop T3BOARD13

Schema Management

Manual database updates

Ad-hoc table and column creation, while you’re developing:

$ ./flow doctrine:create

$ ./flow doctrine:update

Page 54: TYPO3 Flow 2.0 Workshop T3BOARD13

Schema Management

Generating migration scripts

Creates a basis for a migration script which sometimes needs to be adjusted but in any case needs to be checked:

$ ./flow doctrine:migrationgenerate

Page 55: TYPO3 Flow 2.0 Workshop T3BOARD13

Security

_ centrally managed (through AOP)

_ as secure as possible by default

_ modeled after TYPO3 CMS and Spring Security

_ authentication, authorization, validation, filtering ...

_ can intercept arbitrary method calls

_ transparently filters content through query-rewriting

_ extensible for new authentication or authorization mechanisms

Page 56: TYPO3 Flow 2.0 Workshop T3BOARD13

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 57: TYPO3 Flow 2.0 Workshop T3BOARD13

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 58: TYPO3 Flow 2.0 Workshop T3BOARD13

$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 59: TYPO3 Flow 2.0 Workshop T3BOARD13

Authentication Configuration

_ Authentication Provider is responsible for authentication in a specific "area"

_ Entry Point kicks in if a restricted resource is accessed and no account is authenticated yet

Page 60: TYPO3 Flow 2.0 Workshop T3BOARD13

TYPO3: Flow: security: authentication: providers: DefaultProvider: provider: 'PersistedUsernamePasswordProvider' entryPoint: 'WebRedirect' entryPointOptions: routeValues: '@package': 'RobertLemke.Example.Bookshop' '@controller': 'Login' '@action': 'login' '@format': 'html'

Page 61: TYPO3 Flow 2.0 Workshop T3BOARD13

Security Policy (policy.yaml)

_ resources defines what can potentially be protected

_ roles defines who can potentially be granted or denied access

_ aclsdefines who may or may not access which resource

Page 62: TYPO3 Flow 2.0 Workshop T3BOARD13

resources: methods: BookManagementMethods: 'method(.*Controller->(new|edit|create|delete|update)Action())' BookManagementDelete: 'method(.*BookController->deleteAction())'

roles: Administrator: []

acls: methods: Administrator: BookManagementMethods: GRANT

Page 63: TYPO3 Flow 2.0 Workshop T3BOARD13

Login / Logout

_ simply extend AbstractAuthenticationController

_ create a Fluid template with a login form

Page 64: TYPO3 Flow 2.0 Workshop T3BOARD13

/** * @Flow\Scope("singleton") */class LoginController extends AbstractAuthenticationController {

/** * @param \TYPO3\Flow\Mvc\ActionRequest $originalRequest The request that * @return string */ protected function onAuthenticationSuccess(ActionRequest $originalRequest = NULL) { $this->redirect('index', 'Book'); }

/** * @return void */ public function logoutAction() { parent::logoutAction(); $this->redirect('index', 'Book'); }}

Page 65: TYPO3 Flow 2.0 Workshop T3BOARD13

<f:base/><f:flashMessages /><f:form action="authenticate"> <f:form.textfield name="__authentication[TYPO3][Flow][Security][Authentication][Token][UsernamePassword][username]" /> <f:form.password name="__authentication[TYPO3][Flow][Security][Authentication][Token][UsernamePassword][password]" /> <f:form.submit value="login" /></f:form>

Page 66: TYPO3 Flow 2.0 Workshop T3BOARD13

Security

Cross-Site Request Forgery

_ enables an attacker to execute privileged operations without being authenticated

_ the risk lies in using malicious links or forms while still being authenticated

_ imagine a link coming in through an URL shortener...

Page 67: TYPO3 Flow 2.0 Workshop T3BOARD13

Security

Avoiding Cross-Site Request Forgery

_ add a (truly!) random string token to each link or form

_ make sure this token is correct before executing anything

_ change the token as often as possible to make it impossible to send you a working malicious link while you’re logged in

_ in most cases, we can assume that it should be enough to generate one token when you log in – that’s the default

Page 68: TYPO3 Flow 2.0 Workshop T3BOARD13

Security

CSRF Protection in Flow

_ you must not forget to add that token to any link

_ Flow automatically adds the CSRF token to each

_ link you generate

_ each form you create with Fluid

_ and checks it for every call to a protected action

_ the protection can be disabled using @skipCsrfProtection on an action

Page 69: TYPO3 Flow 2.0 Workshop T3BOARD13

Robert Lemke_robertlemke.com@robertlemke