Upload
robert-lemke
View
4.913
Download
3
Embed Size (px)
DESCRIPTION
FLOW3 is an application framework which will change the way you code PHP. It aims to back up developers with security and infrastructure while they focus on the application logic. FLOW3 is one of the first application frameworks to choose Domain-Driven Design as its major underlying concept. This approach makes FLOW3 easy to learn and at the same time clean and flexible for even complex projects. Built with PHP 5.3 in mind from the beginning, it features namespaces and has an emphasis on clean, object-oriented code.Thanks to its Doctrine 2 integration, FLOW3 gives you access to a wide range of databases while letting you forget the fact that you’re using a database at all (think objects, not tables). FLOW3’s unique way of supporting Dependency Injection (no configuration necessary) lets you truly enjoy creating a stable and easy-to-test application architecture. Being the only Aspect-Oriented Programming capable PHP framework, FLOW3 allows you to cleanly separate cross cutting concerns like security from your main application logic.In this tutorial we’ll walk you through an imaginary project from scratch. During the journey we’ll visit all important areas of the framework like templating, routing, security and persistence. For every core concept we’ll provide a short introduction, so that the process is comprehensible to all experienced PHP developers.
Citation preview
/** * Conference controller for the Acme.Demo package * * @scope singleton */class ConferenceController extends ActionController {
/** * @inject * @var \Acme\Demo\Domain\Repository\ConferenceRepository */ protected $conferenceRepository;
/** * Shows a list of conferences * * @return void */ public function indexAction() { $this->view->assign('conferences', $this->conferenceRepository->findAll()); }
/** * Shows a single conference object * * @param \Acme\Demo\Domain\Model\Conference $conference The conference to show * @return void */ public function showAction(Conference $conference) { $this->view->assign('conference', $conference); }
/** * Shows a form for creating a new conference object * * @return void */ public function newAction() { }
/** * Adds the given new conference object to the conference repository * * @param \Acme\Demo\Domain\Model\Conference $conference A new conference to add * @return void */ public function createAction(Conference $newConference) { $this->conferenceRepository->add($newConference); $this->flashMessageContainer->add('Created a new conference.'); $this->redirect('index'); }
/** * Shows a form for editing an existing conference object * * @param \Acme\Demo\Domain\Model\Conference $conference The conference to edit * @return void */ public function editAction(Conference $conference) { $this->view->assign('conference', $conference); }
FLOW3 Tutorial
Bastian Waidelich & Robert Lemke
Hanau, Germany
chief "architect" of TYPO3 5.0 and FLOW3
co-founder of the TYPO3 Association
35 years old
lives in Lübeck, Germany
1 wife, 2 daughters, 1 espresso machine
likes drumming
Robert Lemke
Hanau, Germany
FLOW3 core team member since 2008
co-creator of Fluid
30 years old
lives in Cologne, Germany
0 wifes, ? daughters, 1 cafetera
likes climbing & guitar playing
Bastian Waidelich
Hanau, Germany
This Workshop
Morning
• Installation
• Kickstart & Hello World!
• Commands
• Depencency Injection
• Persistence, Doctrine and Domain-Driven Design
• Modelling of an example App
• Kickstarting the example App
Hanau, Germany
This Workshop
Afternoon
• Routing
• Validation
• Property Mapper
• Migrations
• Security / Login
• A Glimpse on TYPO3 Phoenix
Hanau, Germany
At a Glance
FLOW3 is a web application framework
• brings PHP development to a new level
• made for PHP 5.3, full namespaces support
• modular, extensible, package based
• free & Open Source (LGPL v3)
• backed by one of the largest Open Source projects
with 6000+ contributors
Hanau, Germany
Foundation for the Next Generation CMS
TYPO3 5.0 is the all-new Enterprise CMS
• content repository, workspaces, versions, i18n, ExtJS based UI ...
• powered by FLOW3
• compatible code base
• use TYPO3 features in FLOW3 standalone apps as you like
Hanau, Germany
Git Clone
$ git clone --recursive git://git.typo3.org/FLOW3/Distributions/Base.git .Cloning into ....remote: Counting objects: 3837, done.remote: Compressing objects: 100% (2023/2023), done.remote: Total 3837 (delta 2007), reused 2721 (delta 1465)Receiving objects: 100% (3837/3837), 3.49 MiB | 28 KiB/s, done.Resolving deltas: 100% (2007/2007), done.
Hanau, Germany
Set File Permissions
$ sudo ./flow3 core:setfilepermissions robert _www _wwwFLOW3 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:
Hanau, Germany
Set Up Database Connection
Configuration/Settings.yaml
# ## Global Settings ## #
TYPO3: FLOW3: persistence: backendOptions: dbname: 'demo' user: 'demo' password: 'password' host: '127.0.0.1'
# only on Windows: core: phpBinaryPathAndFilename: 'C:/path/to/php.exe'
Hanau, Germany
Set Up Virtual Host
Apache Virtual Host
<VirtualHost *:80> DocumentRoot /opt/local/apache2/htdocs/Talks/FLOW3/Web/ ServerName dev.flow3.rob SetEnv FLOW3_CONTEXT Development</VirtualHost>
<VirtualHost *:80> DocumentRoot /opt/local/apache2/htdocs/Talks/FLOW3/Web/ ServerName flow3.rob SetEnv FLOW3_CONTEXT Production</VirtualHost>
Hanau, Germany
Final Check
Hanau, Germany
Update from Git to Latest State
$ git submodule foreach "git checkout master"
-✂-----✂-----✂-----✂-----✂-----✂-----✂-----✂-----✂-----✂-----✂-----✂-
$ git submodule foreach "git pull --rebase"
Entering 'Build/Common'First, rewinding head to replay your work on top of it...Fast-forwarded master to 6f27f1784240b414e966ce0e5a12e23cb2f7ab02.Entering 'Packages/Application/TYPO3'First, rewinding head to replay your work on top of it...Fast-forwarded master to 5187430ee44d579ae2bac825e2a069c4cd3Acme8a4.Entering 'Packages/Application/TYPO3CR'First, rewinding head to replay your work on top of it...Fast-forwarded master to b1f5331aa51d390fa3d973404Acme1b9fd773f7059.Entering 'Packages/Application/Twitter'Current branch master is up to date.…
Hanau, Germany
Command Line Use
$ ./flow3 helpFLOW3 1.0.0 ("Development" context)usage: ./flow3 <command identifier>
The following commands are currently available:
PACKAGE "TYPO3.FLOW3":-------------------------------------------------------------------------------* flow3:cache:flush Flush all caches cache:warmup Warm up caches
* flow3:core:setfilepermissions Adjust file permissions for CLI and web server access* flow3:core:shell Run the interactive Shell
doctrine:validate Validate the class/table mappings doctrine:create Create the database schema doctrine:update Update the database schema doctrine:entitystatus Show the current status of entities and mappings doctrine:dql Run arbitrary DQL and display results doctrine:migrationstatus Show the current migration status doctrine:migrate Migrate the database schema doctrine:migrationexecute Execute a single migration doctrine:migrationversion Mark/unmark a migration as migrated doctrine:migrationgenerate Generate a new migration
help Display help for a command
package:create Create a new package package:delete Delete an existing package package:activate Activate an available package package:deactivate Deactivate a package package:list List available packages package:import Import a package from a remote location
routing:list List the known routes
security:importpublickey Import a public key security:importprivatekey Import a private key
PACKAGE "TYPO3.KICKSTART":------------------------------------------------------------------------------- kickstart:package Kickstart a new package kickstart:actioncontroller Kickstart a new action controller kickstart:commandcontroller Kickstart a new command controller kickstart:model Kickstart a new domain model kickstart:repository Kickstart a new domain repository
* = compile time command
See './flow3 help <commandidentifier>' for more information about a specific command.
Hanau, Germany
Command Line Use
$ ./flow3 help kickstart:package
Kickstart a new package
COMMAND: typo3.kickstart:kickstart:package
USAGE: ./flow3 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.flow3:package:create (Create a new package)
Hanau, Germany
Hello World!
$ ./flow3 kickstart:package Acme.Demo
Robert LemkeD.P. Fluxtr
time();
5 1 11
Hello World!
Hanau, Germany
Hello World!
<?phpnamespace Acme\Demo\Controller;
use \TYPO3\FLOW3\MVC\Controller\ActionController;
class StandardController extends ActionController { /** * @param string $name * @return string */ public function indexAction($name) { return "Hello $name!"; }}
?>
StandardController.php
Hanau, Germany
Tackling the Heart of Software Development
Domain-Driven DesignA methodology which ...
• results in rich domain models
• provides a common language across the project team
• simplify the design of complex applications
FLOW3 is the first PHP framework tailored to Domain-Driven Design
/** * Paper submitted by a speaker * * @scope prototype * @entity */class Paper {
/** * @var Participant */ protected $author;
/** * @var string */ protected $title;
/** * @var string */ protected $shortAbstract;
/** * @var string */ protected $abstract;
/** * @var \SplObjectStorage */ protected $materials;
/** * @var \Acme\Conference\Domain\Model\SessionType * @validate NotEmpty */ protected $proposedSessionType;
/** * Constructs a new Paper * * @author Robert Lemke <[email protected]> */ public function __construct() { $this->materials = new \SplObjectStorage; }
/** * Sets the author of this paper * * @param \Acme\Conference\Domain\Model\Participant $author * @return void * @author Robert Lemke <[email protected]> */ public function setAuthor(\Acme\Conference\Domain\Model\Participant $author) { $this->author = $author; }
/** * Getter for the author of this paper * * @return \Acme\Conference\Domain\Model\Participant * @author Robert Lemke <[email protected]> */ public function getAuthor() { return $this->author; }
/** * Setter for title * * @param string $title The title of this paper * @return void * @author Robert Lemke <[email protected]> */ public function setTitle($title) { $this->title = $title; }
/** * Getter for title * * @return string The title of this paper * @author Robert Lemke <[email protected]> */ public function getTitle() { return $this->title; }
/** * Setter for the short abstract * * @param string $shortAbstract The short abstract for this paper * @return void * @author Robert Lemke <[email protected]> */ public function setShortAbstract($shortAbstract) { $this->shortAbstract = $shortAbstract; }
/** * Getter for the short abstract * * @return string The short abstract * @author Robert Lemke <[email protected]> */ public function getShortAbstract() { return $this->shortAbstract; }
/** * Setter for abstract * * @param string $abstract The abstract of this paper * @return void * @author Robert Lemke <[email protected]> */ public function setAbstract($abstract) { $this->abstract = $abstract; }
/** * Getter for abstract * * @return string The abstract * @author Robert Lemke <[email protected]> */ public function getAbstract() { return $this->abstract; }
/** * Returns the materials attached to this paper * * @return \SplObjectStorage The materials * @author Robert Lemke <[email protected]> */ public function getMaterials() { return $this->materials; }
/** * Setter for the proposed session type * * @param \Acme\Conference\Domain\Model\SessionType $proposedSessionType The proposed session type * @return void * @author Robert Lemke <[email protected]> */ public function setProposedSessionType(\Acme\Conference\Domain\Model\SessionType $proposedSessionType) { $this->proposedSessionType = $proposedSessionType; }
/** * Getter for the proposed session type * * @return \Acme\Conference\Domain\Model\SessionType The proposed session type * @author Robert Lemke <[email protected]> */ public function getProposedSessionType() { return $this->proposedSessionType; }}?>
Hanau, Germany
Domain-Driven Design
Domain activity or business of the user
Domain-Driven Design is about
• focussing on the domain and domain logic
• accurately mapping the concepts to software
• forming a ubiquitous language among the project members
Hanau, Germany
Domain-Driven Design
Ubiquitous Language
• important prerequisite for successful collaboration
• use the same words for
• discussion
• modeling
• development
• documentation
Hanau, Germany
Domain-Driven Design
Hanau, Germany
Domain: Conference
Hanau, Germany
Domain: Conference
Hanau, Germany
Domain: Conference
Hanau, Germany
Domain: Conference
Robert LemkeD.P. Fluxtr
time();
5 1 11
Kickstarting "Conference"
Hanau, Germany
Domain: Conference
Hanau, Germany
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
Hanau, Germany
Object Management
FLOW3'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)
Hanau, Germany
<?phpnamespace Acme\DemoBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;use Symfony\Component\HttpFoundation\RedirectResponse;use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;use Acme\DemoBundle\GreeterService;
class DemoController extends Controller { /** * @var \Acme\DemoBundle\GreeterService */ protected $greeterService;
/** * @param \Acme\DemoBundle\GreeterService */ public function __construct($greeterService = NULL) { $this->greeterService = $greeterService; } /** * @Route("/hello/{name}", name="_demo_hello") */ public function helloAction($name) { return new Response('Hello ' . $this->greeterService->greet($name), 200, array('Content-Type' => 'text/plain')); }}
Constructor Injection: Symfony 2Warning: might contain errors
(I'm no Symfony expert ...)
Hanau, Germany
<?xml version="1.0" ?>
<container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<services> <service id="acme.demo.greeterservice" class="Acme\DemoBundle\GreeterService" public="false" /> <service id="acme.demo.democontroller" class="Acme\DemoBundle\Controller\DemoController"> <argument type="service" id="acme.demo.greeterservice" /> </service> </services></container>
Constructor Injection: Symfony 2Warning: might contain errors
(I'm no Symfony expert ...)
Hanau, Germany
<?php namespace F3\Demo\Controller;
use F3\FLOW3\MVC\Controller\ActionController;use F3\Demo\Service\GreeterService;
class DemoController extends ActionController { /** * @var \F3\Demo\Service\GreeterService */ protected $greeterService;
/** * @param \F3\Demo\Service\GreeterService */ public function __construct(\F3\Demo\Service\GreeterService $greeterService) { $this->greeterService = $greeterService; } /** * @param string $name */ public function helloAction($name) { return 'Hello ' . $name; }}
Constructor Injection
Hanau, Germany
Constructor Injection
Hanau, Germany
<?php namespace F3\Demo\Controller;
use F3\FLOW3\MVC\Controller\ActionController;use F3\Demo\Service\GreeterService;
class DemoController extends ActionController { /** * @var \F3\Demo\Service\GreeterService */ protected $greeterService;
/** * @param \F3\Demo\Service\GreeterService */ public function injectGreeterService(\F3\Demo\Service\GreeterService $greeterService) { $this->greeterService = $greeterService; } /** * @param string $name */ public function helloAction($name) { return 'Hello ' . $name; }}
Setter Injection
Hanau, Germany
<?php namespace F3\Demo\Controller;
use F3\FLOW3\MVC\Controller\ActionController;use F3\Demo\Service\GreeterService;
class DemoController extends ActionController { /** * @var \F3\Demo\Service\GreeterService * @inject */ protected $greeterService; /** * @param string $name */ public function helloAction($name) { return 'Hello ' . $name; }}
Property Injection
Hanau, Germany
F3\FLOW3\Security\Cryptography\RsaWalletServiceInterface: className: F3\FLOW3\Security\Cryptography\RsaWalletServicePhp scope: singleton properties: keystoreCache: object: factoryObjectName: F3\FLOW3\Cache\CacheManager factoryMethodName: getCache arguments: 1: value: FLOW3_Security_Cryptography_RSAWallet
Objects.yaml
Hanau, Germany
class Customer {
/** * @inject * @var CustomerNumberGenerator */ protected $customerNumberGenerator;
...}
$customer = new Customer();$customer->getCustomerNumber();
Object Management
Hanau, Germany
Object Management
<?phpdeclare(ENCODING = 'utf-8');namespace F3\Conference\Domain\Model\Conference;/** * Autogenerated Proxy Class * @scope prototype * @entity */class Paper extends Paper_Original implements \F3\FLOW3\Object\Proxy\ProxyInterface, \F3\FLOW3\Persistence\Aspect\PersistenceMagicInterface { /** * @var string * @Id * @Column(length="40") * introduced by F3\FLOW3\Persistence\Aspect\PersistenceMagicAspect */ protected $FLOW3_Persistence_Identifier = NULL; private $FLOW3_AOP_Proxy_targetMethodsAndGroupedAdvices = array(); private $FLOW3_AOP_Proxy_groupedAdviceChains = array(); private $FLOW3_AOP_Proxy_methodIsInAdviceMode = array();
/** * Autogenerated Proxy Method */ public function __construct() { $this->FLOW3_AOP_Proxy_buildMethodsAndAdvicesArray(); if (isset($this->FLOW3_AOP_Proxy_methodIsInAdviceMode['__construct'])) { parent::__construct(); } else {
FLOW3 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
Hanau, Germany
Your Own Commands
$ ./flow3 kickstart:commandcontroller Acme.Demo Test
Hanau, Germany
Validation
Validation is about different things
• incoming data needs to be validated for security reasons
• no evil markup in submitted content
• domain model integrity needs to be ensured
• an email needs to be (syntactically) valid
• credit card numbers should consist only of digits
Hanau, Germany
Validation
Validation in FLOW3
• you do not want to code checks into your controllers
• FLOW3 separates validation from your controller’s concerns
• no PHP code needed for validation
• declared through annotations
Hanau, Germany
Validation
Validation Models
• BasePropertiesrules defining the minimum requirements on individual properties of a model
• BaseModelrules or custom validators enforcing the minimum requirements on the combination of properties of a model
• Supplementalrules defining additional requirements on a model for a specific situation (e.g. a certain action method)
Hanau, Germany
Validation
Base Properties
• Validation rules defined directly at the properties
/** * @var string * @validate StringLength(minimum = 10, maximum = 100) */ protected $title;
/** * @var string * @validate StringLength(minimum = 1, maximum = 50) */ protected $author;
Hanau, Germany
Validation
Validators
• validators provided by FLOW3 can be used through their short name
• Count, Float, NotEmpty, RegularExpression, Uuid, DateTime, NumberRange, StringLength, Alphanumeric, Integer, Number, String, EmailAddress, Label, Raw, Text
• custom validators need to implement the ValidatorInterface
• use them by specifying the fully qualified class name
/** * @var \Dambekalns\Stuff\Domain\Model\Stuff * @validate \Dambekalns\Stuff\Domain\Validator\StuffValidator */ protected $stuff;
Hanau, Germany
Property Mapper
Transfer properties from A to B
• Allows for complete or partial copying of objects and object graphs
• Is used by the MVC framework for the mapping of raw GET and POST data to Argument objects
Hanau, Germany
Property Mapper
$articleArray = array( 'headline' => 'Hello World!', 'story' => 'Just a demo ...' );
$article = $mapper->convert($sourceArray, 'Acme.Demo\Domain\Model\Article');
Hanau, Germany
Resource Management
<f:form method="blog" action="update" object="{blog}" name="blog" enctype="multipart/form-data"> <f:if condition="{blog.authorPicture}"> <img src="{f:uri.resource(resource: blog.authorPicture)}" /> </f:if> <label for="authorPicture">Author picture</label> <f:form.upload property="authorPicture" id="authorPicture" /> <f:form.submit value="Update"/> </f:form>
Image Upload
Resources are handled like other properties in a form:
Hanau, Germany
Property Mapper
/** * @return void */ public function initializeUpdateAction() { $this->arguments['article']->getPropertyMappingConfiguration()
->allowCreationForSubProperty('picture'); $this->arguments['article']->getPropertyMappingConfiguration()
->allowModificationForSubProperty('picture'); }
Allow nested object structures
For security reasons the creation of nested structure through the property mapper is disabled by default
Hanau, Germany
Persistence
Object Persistence in the Flow
• based on Doctrine 2
• seamless integration into FLOW3
• 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
Hanau, Germany
Basic Object Persistence
// 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);
Hanau, Germany
Validation and Doctrine Annotations
namespace TYPO3\Blog\Domain\Model;
/** * A Blog object * * @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;
...
}
Hanau, Germany
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.
Hanau, Germany
@var Defines the type of a property, collections can be typed using angle brackets:\Doctrine\Common\Collections\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
Hanau, Germany
Custom Queries using the Query Object Model
/** * A PostRepository */class PostRepository extends \TYPO3\FLOW3\Persistence\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\FLOW3\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\FLOW3\Persistence\QueryInterface::ORDER_DESCENDING) ) ->execute(); }}
Hanau, Germany
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 FLOW3
Hanau, Germany
Schema Management
Running Migrations
• needed after installation or upgrade:
$ ./flow3 doctrine:migrate
Hanau, Germany
Schema Management
$ ./flow3 doctrine:create
$ ./flow3 doctrine:update
Manual database updates
• for simple situations this can be good enough:
• useful when
• you are just starting a project and have never released
Hanau, Germany
Schema Management
$ ./flow3 doctrine:migrationgenerateGenerated new migration class to "…/Version20110608074324.php" from schema differences.$
Generating migrations
• Generated migrations can contain errors and should be checked and adjusted as needed
• Migrations need to be moved to their “owning” package manually
Robert LemkeD.P. Fluxtr
time();
5 1 18
Migrations
Hanau, Germany
Fluid
Example for assigning a string to a Fluid variable:
<!-- in the Fluid template: --> <head> <title>{title}</title> </head>
// in the action controller: $this->view->assign('title', 'Welcome to Fluid');
Hanau, Germany
Fluid
Variables can also be objects:
<!-- in the Fluid template: --> <div class="venue"> <p>Venue Street: {conference.venue.street}</p> </div>
// in the action controller: $this->view->assign('conference', $conference);
Hanau, Germany
Fluid
if-then-else:
<!-- in the Fluid template: --> <f:if condition="{post.comments}"> <f:then>There are some comments.</f:then> <f:else>There are no comments.</f:else> </f:if>
// in the action controller: $this->view->assign('post', $blogPost);
Hanau, Germany
Fluid
for-each:
<!-- in the Fluid template: --> <ul> <f:for each="{ages}" as="age" key="name"> <li>{name} is {age} year old.</li> </f:for> </ul>
// in the action controller: $this->view->assign('ages', array("Karsten" => 34, "Robert" => 35));
Hanau, Germany
Fluid
for-each:
<!-- in the Fluid template: --> <f:if condition="{post.comments}"> <ul> <f:for each="{post.comments}" as="comment" > <li>{post.title}</li> </f:for> </ul> </f:if>
// in the action controller: $this->view->assign('post', $blogPost);
Hanau, Germany
Fluid
View helpers – in this case the link.action view helper:
<!-- in the Fluid template: --> {namespace f=F3\Fluid\ViewHelpers}
<f:link.action action="delete" arguments="{post: post, really: 'yes'}"> Delete this post </f:link.action>
K. Dambekalns & R. LemkeD.P. Fluxtr
time();
5 1 18
Fluent Fluid
Hanau, Germany
Security
Touchless Security, Flow-Style
• security is handled at a central place (through AOP)
• third-party code is as secure as possible by default
• modeled after our experiences in the TYPO3 project and Spring Security (Java framework)
• provides authentication, authorization, validation, filtering ...
• can intercept arbitrary method calls
• transparently filters content through query-rewriting
• extensible for new authentication or authorization mechanisms
Hanau, Germany
Security Policy
K. Dambekalns & R. LemkeD.P. Fluxtr
time();
5 1 18
Users and Login
Hanau, Germany
AOP
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
Hanau, Germany
AOP
FLOW3 uses AOP for ...
• persistence magic
• logging
• debugging
• security
/** * @aspect * @introduce F3\FLOW3\Persistence\Aspect\PersistenceMagicInterface, F3\FLOW3\Persistence\Aspect\
*/class PersistenceMagicAspect { /** * @pointcut classTaggedWith(entity) || classTaggedWith(valueobject) */ public function isEntityOrValueObject() {} /** * @var string * @Id * @Column(length="40") * @introduce F3\FLOW3\Persistence\Aspect\PersistenceMagicAspect->isEntityOrValueObject && filter
*/ protected $FLOW3_Persistence_Identifier; /** * After returning advice, making sure we have an UUID for each and every entity.
* * @param \F3\FLOW3\AOP\JoinPointInterface $joinPoint The current join point
* @return void * @before classTaggedWith(entity) && method(.*->__construct()) */ public function generateUUID(\F3\FLOW3\AOP\JoinPointInterface $joinPoint) {
$proxy = $joinPoint->getProxy(); \F3\FLOW3\Reflection\ObjectAccess::setProperty($proxy, 'FLOW3_Persistence_Identifier',
}
K. Dambekalns & R. LemkeD.P. Fluxtr
time();
5 1 18
The Wizard of AOP
Hanau, Germany
Signal-Slot Event Handling
Signal
• can be fired on any event
• can be freely defined by the developer
Slot
• is invoked when a signal is emitted
• any method can be used as a slot
any signal can be wired to any slot
Hanau, Germany
Signal-Slot Event Handling
/** * @param \F3\Blog\Domain\Model\Post $post * @param \F3\Blog\Domain\Model\Comment $newComment * @return void */ public function createAction(\F3\Blog\Domain\Model\Post $post, \F3\Blog\Domain\Model\Comment $newComment) { $post->addComment($newComment); $this->emitCommentCreated($newComment, $post); … }
/** * @param \F3\Blog\Domain\Model\Comment $comment * @param \F3\Blog\Domain\Model\Post $post * @return void * @signal */ protected function emitCommentCreated(\F3\Blog\Domain\Model\Comment $comment, \F3\Blog\Domain\Model\Post $post) {}
Hanau, Germany
Signal-Slot Event Handling
/** * Invokes custom PHP code directly after the package manager has been * initialized. * * @param \F3\FLOW3\Core\Bootstrap $bootstrap The current bootstrap * @return void */ public function boot(\F3\FLOW3\Core\Bootstrap $bootstrap) { $dispatcher = $bootstrap->getSignalSlotDispatcher(); $dispatcher->connect( 'F3\Blog\Controller\CommentController', 'commentCreated', 'F3\Blog\Service\Notification', 'sendNewCommentNotification' ); }
Signals are wired to Slots in a package’s bootstrap:
Hanau, Germany
Signal-Slot Event Handling
/** * @param \F3\Blog\Domain\Model\Comment $comment * @param \F3\Blog\Domain\Model\Post $post * @return void */ public function sendNewCommentNotification(\F3\Blog\Domain\Model\Comment $comment, \F3\Blog\Domain\Model\Post $post) { $mail = new \F3\SwiftMailer\Message(); $mail ->setFrom(array('[email protected] ' => 'John Doe')) ->setTo(array('[email protected] ' => 'Karsten Dambekalns')) ->setSubject('New comment on blog post "' . $post->getTitle() . '"') ->setBody($comment->getContent()) ->send(); }
Any method can be a slot:
Hanau, Germany
Roadmap
http://forge.typo3.org/projects/flow3-distribution-base/roadmap
Hanau, Germany
Conference App
git://git.typo3.org/TYPO3v5/Distributions/Conference.git
Hanau, Germany
Blog App
git://git.typo3.org/FLOW3/Applications/Blog.git
Hanau, Germany
Thank You & Have Fun!
• FLOW3: http://flow3.typo3.org
• Blog: http://robertlemke.de/blog
• Twitter: @robertlemke / @mrbasti
• Feedback: [email protected] / [email protected]