The document discusses creating a new command controller class called TestCommandController for the Acme.Demo package using the flow3 kickstart:commandcontroller command, which will generate a standard command controller class with a sample command method that can then be built upon. The command controller provides a way to extend the functionality of the FLOW3 framework through additional commands implemented as classes.
1. Bastian Waidelich & Robert Lemke
/**
* Conference controller for the Acme.Demo package
*
* @scope singleton
*/
class ConferenceController extends ActionController {
/**
* @inject
* @var AcmeDemoDomainRepositoryConferenceRepository
*/
protected $conferenceRepository;
/**
* Shows a list of conferences
*
* @return void
*/
public function indexAction() {
$this->view->assign('conferences', $this->conferenceRepository->findAll());
FLOW3 Tutorial
}
/**
* Shows a single conference object
*
2. Hanau, Germany
Robert Lemke
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
3. Hanau, Germany
Bastian Waidelich
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
4. 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
6. 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
7. 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
9. Hanau, Germany
Set File Permissions
$ sudo ./flow3 core:setfilepermissions robert _www _www
FLOW3 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.
Linux:
$ sudo usermod -a -G www-data robert
Mac OS X:
$ sudo dscl . -append /Groups/_www GroupMembership robert
10. 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'
11. 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>
13. 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.
…
14. Hanau, Germany
Command Line Use
$ ./flow3 help
FLOW3 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
15. 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)
17. H e ll o Wo r ld !
5 1 1 1
Ro bert Lem ke
D.P. F l u x t r
time ();
18. Hanau, Germany
Hello World!
StandardController.php
<?php
namespace AcmeDemoController;
use TYPO3FLOW3MVCControllerActionController;
class StandardController extends ActionController {
/**
* @param string $name
* @return string
*/
public function indexAction($name) {
return "Hello $name!";
}
}
?>
19. Hanau, Germany
Tackling the Heart of Software Development
/**
Domain-Driven Design * Paper submitted by
*
a speaker
* @scope prototype
* @entity
A methodology which ... */
class Paper {
• results in rich domain models /**
* @var Participant
*/
• provides a common language protected $author;
across the project team /**
* @var string
*/
• simplify the design of complex protected $title;
applications /**
* @var string
*/
protected $shortAbstra
ct;
FLOW3 is the first PHP framework
/**
tailored to Domain-Driven Design * @var string
*/
protected $abstract;
20. 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
21. Hanau, Germany
Domain-Driven Design
Ubiquitous Language
• important prerequisite for successful
collaboration
• use the same words for
• discussion
• modeling
• development
• documentation
29. 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
30. 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)
31. Hanau, Germany
War
Constructor Injection: Symfony 2 nin
g
(I'm
:m
igh
no t co
Sym
f nta
ony
exp in erro
<?php ert ..
.) rs
namespace AcmeDemoBundleController;
use SymfonyBundleFrameworkBundleControllerController;
use SymfonyComponentHttpFoundationRedirectResponse;
use SensioBundleFrameworkExtraBundleConfigurationRoute;
use SensioBundleFrameworkExtraBundleConfigurationTemplate;
use AcmeDemoBundleGreeterService;
class DemoController extends Controller {
/**
* @var AcmeDemoBundleGreeterService
*/
protected $greeterService;
/**
* @param AcmeDemoBundleGreeterService
*/
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'));
}
32. Hanau, Germany
War
Constructor Injection: Symfony 2 nin
g
(I'm
:m
igh
no t co
Sym
f nta
ony
exp in erro
ert ..
.) rs
<?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="AcmeDemoBundleGreeterService" public="false" />
<service id="acme.demo.democontroller" class="AcmeDemoBundleControllerDemoController">
<argument type="service" id="acme.demo.greeterservice" />
</service>
</services>
</container>
33. Hanau, Germany
Constructor Injection
<?php
namespace F3DemoController;
use F3FLOW3MVCControllerActionController;
use F3DemoServiceGreeterService;
class DemoController extends ActionController {
/**
* @var F3DemoServiceGreeterService
*/
protected $greeterService;
/**
* @param F3DemoServiceGreeterService
*/
public function __construct(F3DemoServiceGreeterService $greeterService) {
$this->greeterService = $greeterService;
}
/**
* @param string $name
*/
public function helloAction($name) {
return 'Hello ' . $name;
}
}
41. 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
42. 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
43. Hanau, Germany
Validation
Validation Models
• BaseProperties
rules defining the minimum requirements on individual properties of a
model
• BaseModel
rules or custom validators enforcing the minimum requirements on the
combination of properties of a model
• Supplemental
rules defining additional requirements on a model for a specific
situation (e.g. a certain action method)
44. 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;
45. 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 DambekalnsStuffDomainModelStuff
* @validate DambekalnsStuffDomainValidatorStuffValidator
*/
protected $stuff;
46. 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
48. Hanau, Germany
Resource Management
Image Upload
Resources are handled like other properties in a form:
<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>
49. Hanau, Germany
Property Mapper
Allow nested object structures
For security reasons the creation of nested structure through the
property mapper is disabled by default
/**
* @return void
*/
public function initializeUpdateAction() {
$this->arguments['article']->getPropertyMappingConfiguration()
->allowCreationForSubProperty('picture');
$this->arguments['article']->getPropertyMappingConfiguration()
->allowModificationForSubProperty('picture');
}
50. 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
51. 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);
52. Hanau, Germany
Validation and Doctrine Annotations
namespace TYPO3BlogDomainModel;
/**
* A Blog object
*
* @Entity
*/
class Blog {
/**
* @var string
* @validate Text, StringLength(minimum = 1, maximum = 80)
* @Column(length="80")
*/
protected $title;
/**
* @var DoctrineCommonCollectionsCollection<TYPO3BlogDomainModelPost>
* @OneToMany(mappedBy="blog")
* @OrderBy({"date" = "DESC"})
*/
protected $posts;
...
}
53. 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 Defines relations to other entities. Unlike with
@OneToMany vanilla Doctrine targetEntity does not have to be
@ManyToMany given but will be reused from the @var
@OneToOne annotation.
cascade can be used to cascade operation to
related objects.
54. Hanau, Germany
Persistence-related Annotations
@var Defines the type of a property, collections can be
typed using angle brackets:
DoctrineCommonCollectionsCollection<TYPO3ConferenceDomainModelComment>
@transient The property will be ignored, it will neither be
persisted nor reconstituted
@identity Marks the property as part of an objects identity
55. Hanau, Germany
Custom Queries using the Query Object Model
/**
* A PostRepository
*/
class PostRepository extends TYPO3FLOW3PersistenceRepository {
/**
* Finds posts by the specified tag and blog
*
* @param TYPO3BlogDomainModelTag $tag
* @param TYPO3BlogDomainModelBlog $blog The blog the post must refer to
* @return TYPO3FLOW3PersistenceQueryResultInterface The posts
*/
public function findByTagAndBlog(TYPO3BlogDomainModelTag $tag,
TYPO3BlogDomainModelBlog $blog) {
$query = $this->createQuery();
return $query->matching(
$query->logicalAnd(
$query->equals('blog', $blog),
$query->contains('tags', $tag)
)
)
->setOrderings(array(
'date' => TYPO3FLOW3PersistenceQueryInterface::ORDER_DESCENDING)
)
->execute();
}
}
56. 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
58. Hanau, Germany
Schema Management
Manual database updates
• for simple situations this can be good enough:
$ ./flow3 doctrine:create
$ ./flow3 doctrine:update
• useful when
• you are just starting a project and have never released
59. Hanau, Germany
Schema Management
Generating migrations
$ ./flow3 doctrine:migrationgenerate
Generated new migration class to "…/Version20110608074324.php"
from schema differences.
$
• Generated migrations can contain errors and should be checked
and adjusted as needed
• Migrations need to be moved to their “owning” package manually
60. M ig rat io n s
5 8 1 1
Ro bert Lem ke
D.P. F l u x t r
time ();
61. Hanau, Germany
Fluid
Example for assigning a string to a Fluid variable:
// in the action controller:
$this->view->assign('title', 'Welcome to Fluid');
<!-- in the Fluid template: -->
<head>
<title>{title}</title>
</head>
62. Hanau, Germany
Fluid
Variables can also be objects:
// in the action controller:
$this->view->assign('conference', $conference);
<!-- in the Fluid template: -->
<div class="venue">
<p>Venue Street: {conference.venue.street}</p>
</div>
63. Hanau, Germany
Fluid
if-then-else:
// in the action controller:
$this->view->assign('post', $blogPost);
<!-- 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>
64. Hanau, Germany
Fluid
for-each:
// in the action controller:
$this->view->assign('ages', array("Karsten" => 34, "Robert" => 35));
<!-- in the Fluid template: -->
<ul>
<f:for each="{ages}" as="age" key="name">
<li>{name} is {age} year old.</li>
</f:for>
</ul>
65. Hanau, Germany
Fluid
for-each:
// in the action controller:
$this->view->assign('post', $blogPost);
<!-- 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>
66. Hanau, Germany
Fluid
View helpers – in this case the link.action view helper:
<!-- in the Fluid template: -->
{namespace f=F3FluidViewHelpers}
<f:link.action action="delete" arguments="{post: post, really: 'yes'}">
Delete this post
</f:link.action>
67. F lue n t F lu id
5 8 1 1
K. Damb ek aln s & R. Lem ke
D.P. F l u x t r
time ();
68. 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
70. Us e rs a n d L o g in
5 8 1 1
K. Damb ek aln s & R. Lem ke
D.P. F l u x t r
time ();
71. 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
72. Hanau, Germany
AOP
/**
* @aspect
FLOW3 uses AOP for ... * @introduce
*/
F3FLOW3Pers
istenceAspec
tPersistence
class Persist MagicInterfac
enceMagicAspe e, F3FLO
ct {
• persistence magic
/**
* @pointcut c
lassTaggedWit
*/ h(entity) ||
classTaggedWi
th(valueobjec
• logging public functi
on isEntityOr
ValueObject()
{}
t)
/**
* @var string
• debugging
* @Id
* @Column(len
gth="40")
* @introduce
F3FLOW3Pers
*/ istenceAspec
tPersistence
• security protected $FL
OW3_Persisten
ce_Identifier
;
MagicAspect->
isEnti
/**
* After retur
ning advice,
* making sure w
e have an UUI
* @param F3 D for each an
FLOW3AOPJoi d every
* @return voi nPointInterfa
d ce $joinPoint
* @before cla The current j
ssTaggedWith( oin po
*/ entity) && me
thod(.*->__co
public functi nstruct())
on generateUU
$proxy = $joi ID(F3FLOW3
nPoint->getPr AOPJoinPoint
oxy(); Interface $jo
F3FLOW3Ref inPoint)
lectionObjec
} tAccess::setP
roperty($prox
y , 'FLOW3_Per
siste
73. Th e Wiz a rd o f AOP
5 8 1 1
K. Damb ek aln s & R. Lem ke
D.P. F l u x t r
time ();
74. 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
76. Hanau, Germany
Signal-Slot Event Handling
Signals are wired to Slots in a package’s bootstrap:
/**
* Invokes custom PHP code directly after the package manager has been
* initialized.
*
* @param F3FLOW3CoreBootstrap $bootstrap The current bootstrap
* @return void
*/
public function boot(F3FLOW3CoreBootstrap $bootstrap) {
$dispatcher = $bootstrap->getSignalSlotDispatcher();
$dispatcher->connect(
'F3BlogControllerCommentController', 'commentCreated',
'F3BlogServiceNotification', 'sendNewCommentNotification'
);
}
77. Hanau, Germany
Signal-Slot Event Handling
Any method can be a slot:
/**
* @param F3BlogDomainModelComment $comment
* @param F3BlogDomainModelPost $post
* @return void
*/
public function sendNewCommentNotification(F3BlogDomainModelComment $comment,
F3BlogDomainModelPost $post) {
$mail = new F3SwiftMailerMessage();
$mail
->setFrom(array('john@doe.org ' => 'John Doe'))
->setTo(array('karsten@typo3.org ' => 'Karsten Dambekalns'))
->setSubject('New comment on blog post "' . $post->getTitle() . '"')
->setBody($comment->getContent())
->send();
}