symfony: an open-source framework for professionals (php day 2008)
DESCRIPTION
TRANSCRIPT
www.sensiolabs.com
Fabien Potencier
symfony An Open-Source Framework
for Professionals
www.sensiolabs.com
Before we begin
How many have already used symfony for a project,
even a very small personal project?
www.sensiolabs.com
Before we begin
Do you want to do the exercices on your laptop?
www.sensiolabs.com
Sensio • Sensio
– Web Agency – Founded in 1998 – 45 people dedicated to Web dev.
• Open-Source Specialists
• Big corporate customers
Webmarketing Internet Technologies
Sensio Web Agency
symfony framework creator
www.sensiolabs.com
symfony • PHP Web framework • Based on
– 10 years of Sensio experience – Existing Open-Source projects
• Built for : – Professional websites – Complex needs – Demanding environments
www.sensiolabs.com
Framework
www.sensiolabs.com
Whatever the application, a framework is build to ease
development by providing tools for recurrent and boring tasks.
www.sensiolabs.com
Maintainability & Evolutivity
www.sensiolabs.com
Structure & Standardisation
www.sensiolabs.com
MVC
www.sensiolabs.com
Model
www.sensiolabs.com
View
www.sensiolabs.com
Controller
www.sensiolabs.com
Internet
Controleur
Vue
2
Modèle
BDD
3
4
5
6
1
www.sensiolabs.com
Develop Faster & Better
www.sensiolabs.com
Write less code
More time for edge cases, business rules, …
less code
less complexity
less bugs
more productivity
more time
www.sensiolabs.com
Each line of code has an initial cost
… and there is a cost to maintain the line
Kent Beck (based on Yourdon and Constantine)
Costinitial = Costdeveloppement + Costtests
Costmaintenance >> Costinitial
Costmaintenance = Costunderstanding + Costchange + Costtests + Costdeployment
www.sensiolabs.com
symfony
www.sensiolabs.com
An Open-Source Framework
www.sensiolabs.com
MIT Licence
« It is a permissive license, meaning that it permits
reuse within proprietary software on the condition
that the license is distributed with that software. »
www.sensiolabs.com
An Open-Source Documentation
www.sensiolabs.com
• Open-Source documentation – The book (450 pages - GFDL) – Askeet Tutorial (250 pages)
• Translation in 12 langages – 中文 (Chinese)
– Deutsch – Español – Français – Italiano – 日本語 (Japanese)
– Polski – Português – Russian – Ukrainian – Čeština – Nederlands
www.sensiolabs.com
www.sensiolabs.com
A great Community
www.sensiolabs.com
Mailing-list support / forums / IRC
240 available plugins
300k unique visitors per month on the official website www.symfony-project.org
www.sensiolabs.com
« Entreprise » Version
www.sensiolabs.com
Version 1.0 released early 2007
Maintained for 3 ans (early 2010)
~1 release a month (1.0.16 now)
Bugs and security fixes, compatibility with upcoming PHP versions
No new features
Upgrading is simple and safe
www.sensiolabs.com
Let’s start a Project
www.sensiolabs.com
The Project • A simple blog system
– Posts – Categories – Authors – Comments
• Requirements – Maintainable – Customizable – Secure
• Use symfony 1.1
www.sensiolabs.com
List of recent posts
Categories Post information
Excerpt
www.sensiolabs.com
Sidebar customization
Add a comment
List of comments
www.sensiolabs.com
Famfamfam icons
YUI stylesheets
homepage
post
http://www.symfony-project.org/get/design.tgz
www.sensiolabs.com
Project Initialization
www.sensiolabs.com
Bootstrap a symfony Project
1. Install symfony
2. Initialize a new project
3. Configure the Web Server
4. Start coding
www.sensiolabs.com
Installing symfony
Sandbox: Ready-to-run symfony application
PEAR: Install symfony globally on your machine
Subversion: Be free to have several versions around
www.sensiolabs.com
symfony CLI $ symfony
www.sensiolabs.com
Create a new Project
$ mkdir blog
$ cd blog
$ symfony generate:project blog
www.sensiolabs.com
Create a new Application ./symfony help generate:app
www.sensiolabs.com
Create a new secure Application
./symfony generate:app frontend \
--escaping-strategy=on \
--csrf-secret=A$ecret
www.sensiolabs.com
Project, Application, Module, Action
www.sensiolabs.com
Which symfony Version?
./symfony -V
config/ProjectConfiguration.class.php
www.sensiolabs.com
Configure the Web Server <VirtualHost *:80> ServerName myapp.example.com DocumentRoot "/path/to/blog/web" DirectoryIndex index.php
<Directory "/path/to/blog/web"> AllowOverride All Allow from All </Directory>
</VirtualHost>
Web root directory is web/
www.sensiolabs.com
symfony Assets Used by the default pages and the Web Debug Toolbar
Configure the Web Server to serve symfony assets
Or, create a symlink
<VirtualHost *:80> … Alias /sf /$sf_symfony_data_dir/web/sf <Directory "/$sf_symfony_data_dir/web/sf"> AllowOverride All Allow from All </Directory> </VirtualHost>
$ cd web/
$ ln -sf ../lib/vendor/symfony/data/web/sf sf
www.sensiolabs.com
Environments
www.sensiolabs.com
developers customers end users
development environment
staging environment
production environment
www.sensiolabs.com
cache cache cache
debug debug debug
logs logs
stats stats stats
logs
development environment
staging environment
production environment
www.sensiolabs.com
Try a 404 dev environment
prod environment
www.sensiolabs.com
Front Controllers dev environment
prod environment
environment
environment
www.sensiolabs.com
Environment Configuration apps/frontend/config/settings.yml
Web debug toolbar
environment
www.sensiolabs.com
Web Debug Toolbar
Web debug toolbar
www.sensiolabs.com
Create a Module for Posts Create a new ‘post’ module in the ‘frontend’
application
$ php symfony generate:module frontend post
www.sensiolabs.com
Project, Application, Module, Action
www.sensiolabs.com
Action and Template Naming /frontend_dev.php/blog/index
// in apps/frontend/modules/blog/actions/actions.class.php <?php
class blogActions extends sfActions { public function executeIndex() { // do things } }
// in apps/frontend/modules/blog/templates/indexSuccess.php <!–- do things -->
module action
www.sensiolabs.com
Browse
/frontend_dev.php/post/index
www.sensiolabs.com
Create the Blog Homepage
• Copy homepage.html into indexSuccess.php • Copy the images/ and css/ under web/ • Add the base.css CSS in view.yml • Fix images and css paths
apps/frontend/modules/post/templates/indexSuccess.php
/frontend_dev.php/post/index
www.sensiolabs.com
Create an Action to show a Post apps/frontend/modules/post/actions/actions.class.php
/frontend_dev.php/post/show
• Create an empy executeShow() action • Copy post.html into showSuccess.php • Fix images and css paths
www.sensiolabs.com
Project, Application, Module, Action
www.sensiolabs.com
Extract common Code
Homepage specific content
Post page specific content
www.sensiolabs.com
The Layout
header.php
page content
footer.php
page content
layout.php
include
include
decoration
A layout wraps the template content
www.sensiolabs.com
The Layout Move the common code from homepage and post to
the layout
apps/frontend/templates/layout.php
www.sensiolabs.com
Customize the Sidebar and the Title The title depends on the page
The sidebar depends on the page
www.sensiolabs.com
Layout with Several "holes"
Main content
Main content
+ =
Layout Template with slots
Rendered Page
Slot1
Slot
2
Slot 1
Slot 2
A slot content depends on the template context
www.sensiolabs.com
Create Slots for Title and Sidebar apps/frontend/templates/layout.php
www.sensiolabs.com
Fill the Slots
apps/frontend/modules/blog/templates/showSuccess.php
www.sensiolabs.com
Passing Data from Action to Template
apps/frontend/modules/blog/actions/actions.class.php
apps/frontend/modules/blog/templates/indexSuccess.php
www.sensiolabs.com
Make the Counter dynamic
www.sensiolabs.com
Database Schema
A post has an author
A post can be in a category
A post can have comments
www.sensiolabs.com
Propel : The symfony ORM ORM = Object-Relational Mapping Mapping a relational database to an object-oriented
model Database Abstraction
table row, record field, column
class object proterty
Object-Oriented Relational
www.sensiolabs.com
Schema Conventions post: id: # primary key, autoincrement integer author_id: # foreign key to Author created_at: # timestamp, set to current time on creation updated_at: # timestamp, set to current time on update
# column types published_at: timestamp title: varchar(255) content: longvarchar is_spam: boolean
# complex column definitions last_name: { type: varchar(100), index: true, required: true } category_id: { type: integer, foreignTable: category,
foreignReference: id, required: false, onDelete: setnull }
www.sensiolabs.com
Database Schema config/schema.yml
www.sensiolabs.com
Build the Model Classes ./symfony propel:build-model
www.sensiolabs.com
From Schema to Object Model
$ ./symfony propel:build-model
lib/ model/ om/ BasePost.php BasePostPeer.php Post.php PostPeer.php
propel: post: id: ~ name: varchar(255)
1 table > 4 classes?
www.sensiolabs.com
Base and Custom Classes Base classes
Under model/om/, prefixed by Base Generated by Propel Overwritten each time the schema
changes and the model is generated Never edit these files!
lib/ model/ om/ BasePost.php BasePostPeer.php Post.php PostPeer.php
lib/ model/ om/ BasePost.php BasePostPeer.php Post.php PostPeer.php
Custom classes Under model/, no prefix Inherit from Base classes Never overwritten Put custom methods here Override base methods here
www.sensiolabs.com
Peer and Object Classes Peer classes
Suffixed by Peer Useful to retrieve a collection of objects Methods return objects Only static methods (::, self)
lib/ model/ om/ BasePost.php BasePostPeer.php Post.php PostPeer.php
Object classes No suffix Useful to create / inspect / update records Methods return column values Only object methods (->, $this)
lib/ model/ om/ BasePost.php BasePostPeer.php Post.php PostPeer.php
www.sensiolabs.com
Database Initialization
mysqladmin create phpday
./symfony configure:database mysql://localhost/phpday
www.sensiolabs.com
Build the SQL queries ./symfony propel:build-sql
./symfony propel:insert-sql
www.sensiolabs.com
Shortcut for all the previous Tasks
./symfony propel:build-all
www.sensiolabs.com
Initial Data data/fixtures/01-data.yml
Define PKs with names
Use names instead of Pks
Dynamic values
www.sensiolabs.com
Load Data
$ ./symfony propel:data-load frontend
www.sensiolabs.com
Summary of Code Generation
schema.yml
Object model Base, Custom, Peer and object classes
Relational database Tables, columns, keys, indexes
propel:build-model
propel:build-sql propel:insert-sql
1
2
3
www.sensiolabs.com
If the Database preexists the Project
schema.yml
Object model Base, Custom, Peer and object classes
Relational database Tables, columns, keys, indexes
propel:build-model
propel:build-schema
2
3
1
www.sensiolabs.com
Generated Methods of Object Classes
Getter for columns $title = $post->getTitle(); $content = $post->getContent(); $createdAt = $post->getCreatedAt();
Some getters have special options $date = $post->getCreatedAt($dateFormat);
Getter by name $title = $post->getByName('title');
CamelCase version of the column name
www.sensiolabs.com
Generated Methods of Object Classes Manipulate primary keys
$commentId = $comment->getId(); // for composite keys, prefer $commentId = $comment->getPrimaryKey();
Manipulate foreign keys $postId = $comment->getPostId(); // in practice, these methods are not used much // use getter for foreign objects instead $post = $comment->getPost(); // Post object // as the result is an object, you can chain method calls $content = $comment->getPost()->getContent();
One-to-Many smart getters $comments = $post->getCommments(); // Array of Comments $nb = $post->countCommments(); // Integer
www.sensiolabs.com
Get the Posts from the Database
www.sensiolabs.com
What the Model Layer does Action Model Database
PostPeer::doSelect(new Criteria())
SELECT * FROM post
Criteria to SQL translation
resultset Query execution
Array of Post objects Object hydrating
www.sensiolabs.com
What the Model Layer does Template Model Database
$post->getTitle()
String Looking up internal attribute
www.sensiolabs.com
Make the Post show Page dynamic
/frontend_dev.php/post/show?id=1
www.sensiolabs.com
Make the Post show Page dynamic
Display a 404 error if the post does not exist
www.sensiolabs.com
Change the Date Format getPublishedAt() first argument accepts the date()
format or the strftime() format
symfony format_date() helper is i18n aware
www.sensiolabs.com
Helper Groups • Tag • URLs • Assets (images, JavaScript, CSS, …) • Subtemplate inclusion (slot, partial, component) • Links • Form • Javascript and Ajax • Text, number, date manipulation • I18N • …
www.sensiolabs.com
Permalinks • Many applications provide an alternative to
functional URLs • Permalinks look like links to permanent content
while the resource they reference is dynamically generated
• Primarily focused at search engines, permalink often carry more readable data for end users
http://www.symfony-project.org/blog/2008/05/21/new-symfony-security-policy
www.sensiolabs.com
Links to the Post Page apps/frontend/config/routing.yml
lib/model/Post.php
www.sensiolabs.com
Links to the Post Page
apps/frontend/modules/post/templates/indexSuccess.php
apps/frontend/modules/post/actions/actions.class.php
www.sensiolabs.com
Link to the Homepage
www.sensiolabs.com
Add the Comments
apps/frontend/modules/post/templates/showSuccess.php
www.sensiolabs.com
What the Model Layer does Template Model Database
$post->getComments()
SELECT * FROM comment WHERE comment.post_id= ?
resultset Query execution
Array of Comment objects Object hydrating
www.sensiolabs.com
Comment Form
$ ./symfony propel:build-forms
www.sensiolabs.com
Base and Custom Classes Base classes
Under form/base/, prefixed by Base Generated by symfony Overwritten when the schema
changes and the forms are generated
Never edit these files!
lib/ form/ base/ BasePostForm.class.php PostForm.class.php
lib/ form/ base/ BasePost.Form.class.php PostForm.class.php
Custom classes Under form/, no prefix Inherit from Base classes Never overwritten Put custom methods here Override base methods here
www.sensiolabs.com
Create a Comment Form
apps/frontend/modules/post/actions/actions.class.php
apps/frontend/modules/post/templates/showSuccess.php
www.sensiolabs.com
Create a Comment Form
www.sensiolabs.com
Propel Forms • Generated by propel:build-forms • 1 table = 1 form • Model introspection to determine
– The widget – The validation rules
• Automatically converts a form to a Propel object and save it to the database
• Extensible
www.sensiolabs.com
Bind The Form to the Post
www.sensiolabs.com
Customize the Form lib/form/CommentForm.class.php
www.sensiolabs.com
Form Life Cycle
www.sensiolabs.com
Comments
www.sensiolabs.com
Security: XSS
www.sensiolabs.com
Security: CSRF
www.sensiolabs.com
Create the Category Page lib/model/PostPeer.class.php
apps/frontend/modules/blog/actions/actions.class.php
www.sensiolabs.com
Create the Category Page
apps/frontend/config/routing.yml
apps/frontend/templates/layout.php
www.sensiolabs.com
Create a Partial for the List apps/frontend/modules/blog/templates/_list.php
apps/frontend/modules/blog/templates/listByCategorySuccess.php
www.sensiolabs.com
Create a Component
apps/frontend/templates/layout.php
apps/frontend/modules/post/actions/components.class.php
apps/frontend/modules/post/templates/_categories.php
www.sensiolabs.com
Create a Web Service for Posts
apps/frontend/config/routing.yml
apps/frontend/modules/post/templates/indexSuccess.xml.php
www.sensiolabs.com
If we have time… • Functional Tests
• Cache
• CRUD to manage posts
www.sensiolabs.com
Sensio S.A. 26, rue Salomon de Rothschild
92 286 Suresnes Cedex FRANCE
Tél. : +33 1 40 99 80 80 Fax : +33 1 40 99 83 34
Contact Fabien Potencier
http://www.sensiolabs.com/ http://www.symfony-project.com/