Getting Into FLOW3 (TYPO312CA)
-
Upload
robert-lemke -
Category
Technology
-
view
1.810 -
download
1
description
Transcript of Getting Into FLOW3 (TYPO312CA)
Robert Lemke
Getting intoFLOW3
Québec City, CanadaT3CON12
Québec City, CanadaT3CON12
project founder of FLOW3 and TYPO3 “Phoenix”
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
Robert Lemke
StartersInstallation
Kickstart & Hello World!
Controller, Actions, Arguments & HTTP
Domain-Driven Design
Doctrine
Forms, Validation
Main Dishes
Resources, Image Upload
Session Handling
User, Account, Authentication
Authorization
DesertsCaching
Logging
Signal-Slot
Routing
Testing
Deployment
I18n
Espresso
?
Québec City, CanadaT3CON12
At a Glance
FLOW3 is a web application platform
• holistic concept for your apps
• modular, extensible, package based
• pedantically clean with focus on quality
• puts a smile on developer’s faces
• free & Open Source (LGPL v3)
• backed by one of the largest Open Source projects
Québec City, CanadaT3CON12
Foundation for the Next Generation CMS
TYPO3 “Phoenix” is the all-new Enterprise CMS
• content repository, workspaces, versions, i18n, modular UI ...
• powered by FLOW3
• compatible code base
• use TYPO3 features in FLOW3 standalone apps as you like
Québec City, CanadaT3CON12
FLOW3 Website and Download
#
Québec City, CanadaT3CON12
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.
Québec City, CanadaT3CON12
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:
Québec City, CanadaT3CON12
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'
Québec City, CanadaT3CON12
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>
Québec City, CanadaT3CON12
Final Check
Robert LemkeD.P. Fluxtr
time();
5 1 11
Clone the Beast
Québec City, CanadaT3CON12
Biggest Book Store: Amazon
Québec City, CanadaT3CON12
Biggest River: Amazon River
Québec City, CanadaT3CON12
Smallest River: Roe River
Québec City, CanadaT3CON12
Smallest River: Roe River
Québec City, CanadaT3CON12
Smallest River: Roe River
Québec City, CanadaT3CON12
Smallest River: Roe River
Smallest Book Store: Roe Books
Sketchy Model
Robert LemkeD.P. Fluxtr
time();
5 1 12
Just Code a Shop
Québec City, CanadaT3CON12
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
/** * A Book * * @FLOW3\Scope(“prototype”) * @FLOW3\Entity */class Book {
/** * @var string */ protected $title;
/** * @var string */ protected $isbn;
/** * @var string */ protected $description;
/** * @var integer */ protected $price;
/** * @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; }}?>
Québec City, CanadaT3CON12
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
Québec City, CanadaT3CON12
Domain-Driven Design
Ubiquitous Language
• important prerequisite for successful collaboration
• use the same words for
• discussion
• modeling
• development
• documentation
Québec City, CanadaT3CON12
Domain-Driven Design
Building Blocks
• Entity: An object that is not defined by its attributes, but rather by a thread of continuity and its identity.
• Value Object: An object that contains attributes but has no conceptual identity. They should be treated as immutable.
• Aggregate: A collection of objects that are bound together by a root entity, otherwise known as an aggregate root. The aggregate root guarantees the consistency of changes being made within the aggregate by forbidding external objects from holding references to its members.
Québec City, CanadaT3CON12
Domain-Driven Design
Building Blocks
• Service: When an operation does not conceptually belong to any object. Following the natural contours of the problem, you can implement these operations in services.
• Repository: methods for retrieving domain objects should delegate to a specialized Repository object such that alternative storage implementations may be easily interchanged.
Québec City, CanadaT3CON12
Domain-Driven Design
Québec City, CanadaT3CON12
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)
Québec City, CanadaT3CON12
namespace Acme\Demo\Controller;
use TYPO3\FLOW3\Mvc\Controller\ActionController;use Acme\Demo\Service\GreeterService;
class DemoController extends ActionController { /** * @var \Acme\Demo\Service\GreeterService */ protected $greeterService;
/** * @param \Acme\Demo\Service\GreeterService */ public function __construct(GreeterService $greeterService) { $this->greeterService = $greeterService; } /** * @param string $name */ public function helloAction($name) { return $this->greeterService->greet($name); }}
Constructor Injection
Québec City, CanadaT3CON12
namespace Acme\Demo\Controller;
use TYPO3\FLOW3\MVC\Controller\ActionController;use Acme\Demo\Service\GreeterService;
class DemoController extends ActionController { /** * @var \Acme\Demo\Service\GreeterService */ protected $greeterService;
/** * @param \Acme\Demo\Service\GreeterService */ public function injectGreeterService(GreeterService $greeterService) { $this->greeterService = $greeterService; } /** * @param string $name */ public function helloAction($name) { return $this->greeterService->greet($name); }}
Setter Injection
Québec City, CanadaT3CON12
namespace TYPO3\Demo\Controller;
use TYPO3\FLOW3\Annotations as FLOW3;use TYPO3\FLOW3\MVC\Controller\ActionController;use Acme\Demo\Service\GreeterService;
class DemoController extends ActionController { /** * @var \TYPO3\Demo\Service\GreeterService * @FLOW3\Inject */ protected $greeterService; /** * @param string $name */ public function helloAction($name) { return $this->greeterService->greet($name); }}
Property Injection
Québec City, CanadaT3CON12
TYPO3\FLOW3\Security\Cryptography\RsaWalletServiceInterface: className: TYPO3\FLOW3\Security\Cryptography\RsaWalletServicePhp scope: singleton properties: keystoreCache: object: factoryObjectName: TYPO3\FLOW3\Cache\CacheManager factoryMethodName: getCache arguments: 1: value: FLOW3_Security_Cryptography_RSAWallet
Objects.yaml
Québec City, CanadaT3CON12
class Customer {
/** * @FLOW3\Inject * @var \Acme\CustomerNumberGenerator */ protected $customerNumberGenerator; ...}
$customer = new Customer();$customer->getCustomerNumber();
Object Management
Québec City, CanadaT3CON12
Object Management<?phpdeclare(ENCODING = 'utf-8');namespace TYPO3\Conference\Domain\Model\Conference;use TYPO3\FLOW3\Annotations as FLOW3;/** * Autogenerated Proxy Class * @FLOW3\Scope(“prototype”) * @FLOW3\Entity */class Paper extends Paper_Original implements \TYPO3\FLOW3\Object\Proxy\ProxyInterface, \TYPO3\FLOW3\Persistence\Aspect\PersistenceMagicInterface { /** * @var string * @ORM\Id * @ORM\Column(length="40") * introduced by TYPO3\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
Québec City, CanadaT3CON12
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);
Québec City, CanadaT3CON12
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;
...
}
Québec City, CanadaT3CON12
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.
Québec City, CanadaT3CON12
@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
Québec City, CanadaT3CON12
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(); }}
Québec City, CanadaT3CON12
Schema Management
Running Migrations
• needed after installation or upgrade:
$ ./flow3 doctrine:migrate
Québec City, CanadaT3CON12
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
Québec City, CanadaT3CON12
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
Québec City, CanadaT3CON12
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
Québec City, CanadaT3CON12
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)
Québec City, CanadaT3CON12
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;
Québec City, CanadaT3CON12
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;
Québec City, CanadaT3CON12
Property Mapper
$articleArray = array( 'headline' => 'Hello World!', 'story' => 'Just a demo ...' );
$article = $mapper->convert($sourceArray, 'Acme.Demo\Domain\Model\Article');
Québec City, CanadaT3CON12
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:
Québec City, CanadaT3CON12
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
Québec City, CanadaT3CON12
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');
Québec City, CanadaT3CON12
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);
Québec City, CanadaT3CON12
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);
Québec City, CanadaT3CON12
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));
Québec City, CanadaT3CON12
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);
Québec City, CanadaT3CON12
Fluid
View helpers – in this case the link.action view helper:
<!-- in the Fluid template: --> {namespace f=TYPO3\Fluid\ViewHelpers}
<f:link.action action="delete" arguments="{post: post, really: 'yes'}"> Delete this post </f:link.action>
Québec City, CanadaT3CON12
Security Policy
Québec City, CanadaT3CON12
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...
Québec City, CanadaT3CON12
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
Québec City, CanadaT3CON12
Security
CSRF Protection in FLOW3
• you must not forget to add that token to any link
• FLOW3 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
Québec City, CanadaT3CON12
Roadmap
http://forge.typo3.org/projects/flow3-distribution-base/roadmap
Québec City, CanadaT3CON12
Conference App
git://git.typo3.org/TYPO3v5/Distributions/Conference.git
Québec City, CanadaT3CON12
Blog App
git://git.typo3.org/FLOW3/Applications/Blog.git
Rossmann
• second biggest drug store in Germany
• 5,13 billion ! turnover
• 31,000 employees
Customer Database
• custom persistence with CouchDB
• SOAP support
• continuous delivery
• cluster setup
by networkteam, Kiel
Amadeus
• world’s biggest e-ticket provider
• 217 markets
• 948 million billable transactions / year
• 2,7 billion ! revenue
Social Media Suite
• central hub for social media activities for potentially thousands of travel agencies
• advanced form engine
• various detail improvements
• uses an early version of TYPO3 Phoenix
by AKOM360, Munich
“Our senior developers are extremely happy with FLOW3 –
it is definitely the most capable PHP framework we have come across so far.”
Fabian PfützeProject Lead
?
Québec City, CanadaT3CON12
Thanks for having me!
Slides: http://slideshare.net/robertlemke
Examples: http://github.com/robertlemke
Blog: http://robertlemke.com
Twitter: @robertlemke
Feedback: [email protected]
FLOW3: http://flow3.typo3.org