Scaling API-first – The story of a global engineering organization
symfony: An Open-Source Framework for Professionals (PHP Day 2008)
1. symfony
An Open-Source Framework
for Professionals
Fabien Potencier
www.sensiolabs.com
2. Before we begin
How many have already
used symfony for a project,
even a very small personal project?
www.sensiolabs.com
3. Before we begin
Do you want to do
the exercices on your laptop?
www.sensiolabs.com
4. Sensio
• Sensio Sensio
Web Agency
– Web Agency
Internet
Webmarketing
– Founded in 1998 Technologies
– 45 people dedicated to Web dev.
• Open-Source Specialists symfony framework
creator
• Big corporate customers
www.sensiolabs.com
5. 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
16. Write less code
less code
less complexity
less bugs
more productivity
more time
More time for edge cases, business rules, …
www.sensiolabs.com
17. Each line of code has an initial cost
Costinitial = Costdeveloppement + Costtests
… and there is a cost to maintain the line
Costmaintenance >> Costinitial
Costmaintenance = Costunderstanding + Costchange + Costtests + Costdeployment
Kent Beck (based on Yourdon and Constantine)
www.sensiolabs.com
20. 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
25. Mailing-list support / forums / IRC
240 available plugins
300k unique visitors per month on the official
website www.symfony-project.org
www.sensiolabs.com
27. 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
34. Bootstrap a symfony Project
1. Install symfony
2. Initialize a new project
3. Configure the Web Server
4. Start coding
www.sensiolabs.com
35. 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
41. Which symfony Version?
./symfony -V
config/ProjectConfiguration.class.php
www.sensiolabs.com
42. 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 Web root directory is web/
</Directory>
</VirtualHost>
www.sensiolabs.com
43. symfony Assets
Used by the default pages and the Web Debug Toolbar
Configure the Web Server to serve symfony assets
<VirtualHost *:80>
…
Alias /sf /$sf_symfony_data_dir/web/sf
<Directory "/$sf_symfony_data_dir/web/sf">
AllowOverride All
Allow from All
</Directory>
</VirtualHost>
Or, create a symlink
$ cd web/
$ ln -sf ../lib/vendor/symfony/data/web/sf sf
www.sensiolabs.com
53. Action and Template Naming
/frontend_dev.php/blog/index
module action
// 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 -->
www.sensiolabs.com
55. Create the Blog Homepage
apps/frontend/modules/post/templates/indexSuccess.php
• 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
/frontend_dev.php/post/index
www.sensiolabs.com
56. Create an Action to show a Post
apps/frontend/modules/post/actions/actions.class.php
• Create an empy executeShow() action
• Copy post.html into showSuccess.php
• Fix images and css paths
/frontend_dev.php/post/show
www.sensiolabs.com
58. Extract common Code
Post page specific content
Homepage specific content
www.sensiolabs.com
59. The Layout
A layout wraps the template content
header.php
page content
include
decoration
page content
include
footer.php
layout.php
www.sensiolabs.com
60. The Layout
Move the common code from homepage and post to
the layout
apps/frontend/templates/layout.php
www.sensiolabs.com
61. Customize the Sidebar and the Title
The title depends on the page
The sidebar depends on the page
www.sensiolabs.com
62. Layout with Several "holes"
A slot content depends on the template context
Slot1
Main
content
Slot 2
Main + =
content
Slot 1
Slot 2
Layout Template Rendered
with slots Page
www.sensiolabs.com
63. Create Slots for Title and Sidebar
apps/frontend/templates/layout.php
www.sensiolabs.com
64. Fill the Slots
apps/frontend/modules/blog/templates/showSuccess.php
www.sensiolabs.com
65. Passing Data from Action to Template
apps/frontend/modules/blog/actions/actions.class.php
apps/frontend/modules/blog/templates/indexSuccess.php
www.sensiolabs.com
67. Database Schema
A post has an author
A post can be in a category
A post can have comments
www.sensiolabs.com
68. Propel : The symfony ORM
ORM = Object-Relational Mapping
Mapping a relational database to an object-oriented
model
Database Abstraction
Relational Object-Oriented
table class
row, record object
field, column proterty
www.sensiolabs.com
69. 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
71. Build the Model Classes
./symfony propel:build-model
www.sensiolabs.com
72. From Schema to Object Model
$ ./symfony propel:build-model
propel: lib/
post: model/
id: ~ om/
name: varchar(255) BasePost.php
BasePostPeer.php
Post.php
PostPeer.php
1 table > 4 classes?
www.sensiolabs.com
73. Base and Custom Classes
lib/ Base classes
model/
om/ Under model/om/, prefixed by Base
BasePost.php
BasePostPeer.php
Generated by Propel
Post.php Overwritten each time the schema
PostPeer.php
changes and the model is generated
Never edit these files!
lib/ Custom classes
model/
om/ Under model/, no prefix
BasePost.php
BasePostPeer.php
Inherit from Base classes
Post.php Never overwritten
PostPeer.php
Put custom methods here
www.sensiolabs.com
Override base methods here
74. Peer and Object Classes
lib/ Peer classes
model/
om/ Suffixed by Peer
BasePost.php Useful to retrieve a collection of objects
BasePostPeer.php
Post.php Methods return objects
PostPeer.php
Only static methods (::, self)
lib/ Object classes
model/
No suffix
om/
BasePost.php
Useful to create / inspect / update
BasePostPeer.php records
Post.php Methods return column values
PostPeer.php Only object methods (->, $this)
www.sensiolabs.com
76. Build the SQL queries
./symfony propel:build-sql
./symfony propel:insert-sql
www.sensiolabs.com
77. Shortcut for all the previous Tasks
./symfony propel:build-all
www.sensiolabs.com
78. Initial Data
data/fixtures/01-data.yml
Define PKs with names
Use names instead of Pks
Dynamic values
www.sensiolabs.com
79. Load Data
$ ./symfony propel:data-load frontend
www.sensiolabs.com
80. Summary of Code Generation
2 Object model
propel:build-model Base, Custom,
Peer and object classes
1
schema.yml
3 propel:build-sql
propel:insert-sql
Relational database
Tables, columns, keys, indexes
www.sensiolabs.com
81. If the Database preexists the Project
3 Object model
propel:build-model Base, Custom,
Peer and object classes
2
schema.yml
1
propel:build-schema
Relational database
Tables, columns, keys, indexes
www.sensiolabs.com
82. Generated Methods of Object Classes
Getter for columns CamelCase version
$title = $post->getTitle();
$content = $post->getContent(); of the column name
$createdAt = $post->getCreatedAt();
Some getters have special options
$date = $post->getCreatedAt($dateFormat);
Getter by name
$title = $post->getByName('title');
www.sensiolabs.com
83. 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
85. What the Model Layer does
Action Model Database
PostPeer::doSelect(new Criteria())
Criteria to SQL translation
SELECT * FROM post
Query execution
resultset
Object hydrating
Array of Post objects
www.sensiolabs.com
86. What the Model Layer does
Template Model Database
$post->getTitle()
Looking up internal attribute
String
www.sensiolabs.com
87. Make the Post show Page dynamic
/frontend_dev.php/post/show?id=1
www.sensiolabs.com
88. Make the Post show Page dynamic
Display a 404 error if the post does not exist
www.sensiolabs.com
89. 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
90. 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
91. 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
92. Links to the Post Page
apps/frontend/config/routing.yml
lib/model/Post.php
www.sensiolabs.com
93. Links to the Post Page
apps/frontend/modules/post/templates/indexSuccess.php
apps/frontend/modules/post/actions/actions.class.php
www.sensiolabs.com
95. Add the Comments
apps/frontend/modules/post/templates/showSuccess.php
www.sensiolabs.com
96. What the Model Layer does
Template Model Database
$post->getComments()
SELECT * FROM comment
WHERE comment.post_id= ?
Query execution
resultset
Array of Comment objects
Object hydrating
www.sensiolabs.com
97. Comment Form
$ ./symfony propel:build-forms
www.sensiolabs.com
98. Base and Custom Classes
lib/ Base classes
form/
Under form/base/, prefixed by Base
base/
BasePostForm.class.php Generated by symfony
PostForm.class.php
Overwritten when the schema
changes and the forms are
generated
Never edit these files!
lib/ Custom classes
form/
base/ Under form/, no prefix
BasePost.Form.class.php Inherit from Base classes
PostForm.class.php
Never overwritten
Put custom methods here
www.sensiolabs.com
Override base methods here
99. Create a Comment Form
apps/frontend/modules/post/actions/actions.class.php
apps/frontend/modules/post/templates/showSuccess.php
www.sensiolabs.com
101. 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
108. Create the Category Page
lib/model/PostPeer.class.php
apps/frontend/modules/blog/actions/actions.class.php
www.sensiolabs.com
109. Create the Category Page
apps/frontend/config/routing.yml
apps/frontend/templates/layout.php
www.sensiolabs.com
110. Create a Partial for the List
apps/frontend/modules/blog/templates/_list.php
apps/frontend/modules/blog/templates/listByCategorySuccess.php
www.sensiolabs.com