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.
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
/** * Conference controller for the Acme.Demo package * * @scope singleton */class ConferenceController extends ActionController {
/** * 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
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.…
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 {
/** * 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;
@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)