PHP + Symfony

advertisement
PHP + Symfony
Rapid application development
Jonathan Bell
Nov 18, 2010
A Developer’s User Story
•
•
•
•
It’s 5:00pm on Sunday
Need to have a web app together for Monday
Want to sleep tonight
Want to have a “complete app” (validation,
security, etc)
• Don’t have time machine
• EJB? No…
• .Net? No…
Enter Symfony
Symfony is a code generator/MVC
framework for PHP. Sounds a lot like
ruby on rails. Why PHP?
PHP is super C-like, might be easier to
pick up
• ORB mapping
(pluggable)
• CRUD generation
• Validation
•
•
•
•
Routing
Templating
Plugins
Unit Testing
Object Brokering (Model layer)
• Automatically generates fully
documented PHP classes from config file
• Removes need for repeated DB calls,
provides abstraction
schema.yml
propel:
weblog_post:
_attributes: { phpName: Post }
id:
title:
varchar(255)
excerpt: longvarchar
body:
longvarchar
created_at:
PHP Code
$p = new Post();
$p->setTitle(“Test Post”);
$p->setBody(“the body”);
$p->save();
$posts = PostPeer::doSelect(new
Criteria());
foreach($posts as $post)
{
print “Post: “.$post->getTitle();
}
CRUD Generation + Validation (Controller +
View)
• Web programmer’s worst nightmare:
writing huge forms with dozens of
inputs, associated form processors
and validators
• Symfony to the rescue: generates
template Create/Read/Update/Delete
• Very, very easy to customize once
generated
symfony propel:generate-module admin findable FindableItem
indexSuccess.php:
<h1>Findable List</h1>
<table>
<thead>
<tr>
<th>Id</th>
<th>Name</th>
<th>Cardinality</th>
<th>Points</th>
<th>Description</th>
<th>S findable category</th>
</tr>
</thead>
<tbody>
<?php foreach ($findable_item_list as $findable_item): ?>
<tr>
<td><a href="<?php echo url_for('findable/edit?id='.$findable_item->getId()) ?>"><?php echo $findable_item->getId() ?></a></td>
<td><?php echo $findable_item->getName() ?></td>
<td><?php echo $findable_item->getCardinality() ?></td>
<td><?php echo $findable_item->getPoints() ?></td>
<td><?php echo $findable_item->getDescription() ?></td>
<td><?php echo $findable_item->getSFindableCategoryId() ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<a href="<?php echo url_for('findable/new') ?>">New</a>
actions.class.php:
public function executeIndex(sfWebRequest $request)
{
$this->findable_item_list = FindableItemPeer::doSelect(new Criteria());
}
Validation
• Free with form generation!
• We already defined the form “widgets” in
the original .yml file, including basic data
ranges
BaseFindableItemForm
$this->setValidators(array(
'id'
=> new sfValidatorPropelChoice(array('model' => 'FindableItem', 'column' => 'id', 'required' => false)),
'name'
=> new sfValidatorString(array('max_length' => 255, 'required' => false)),
'cardinality'
=> new sfValidatorInteger(array('required' => false)),
'points'
=> new sfValidatorInteger(array('required' => false)),
'description'
=> new sfValidatorString(array('required' => false)),
's_findable_category_id' => new sfValidatorPropelChoice(array('model' => 'FindableCategory', 'column' => 'id')),
));
Can easily override this in the implementation for
FindableItemForm with new validators. Or, don’t
bother and leave it at default!
Aspect Oriented Development?
• Symfony supports “behaviors”
• Automatically creates many hook-spots
• Classic example: “paranoid” behavior
– Replace deletes with setting a flag
– Augment selects to avoid records with that flag
Routing
• Problem: We all love having simple
looking URLs:
– http://cusearch09.com/user/image/5/320
– Instead of
http://cusearch09.com/user/upload/imageT
humb.php?id=5&width=320
• Solution:
– Apache mod_rewrite
• No... too complicated and hard to change on the
fly. Who wants to deal with RegExp?
– Symfony:
• routing.yml
resized_image:
url:
image/:id/:width
param: {module: upload, action: imageThumb}
Templating
•
•
•
•
Generic template setup
Allows for “partial” templates
Inheritance!
Helpers
– link_to()
– img_tag()
Plugins
• Easy to extend Symfony with plugins
• Follow observer pattern
– static public function
listenToSomeSortOfEvent(sfEvent $event)
– Hooks your plugin to any part of Symfony
• Uses: objects, aspects
– Security? AJAX?
Free Lime Tests!
(note: not Lime’s Disease tests)
Unit test the model
$t = new lime_test(1, new lime_output_color());
$t->diag('->retrieveByUsername()');
$user = UserPeer::retrieveByUsername('jbell');
$t->is($user->getLastName(), 'Bell', '->retrieveByUsername() returns the User for the given username');
Functional test the controllers (WOW!)
$b = new sfTestBrowser();
$b->get('/foobar/edit/id/1');
$request = $b->getRequest();
$context = $b->getContext();
$response = $b->getResponse(); // Get access to the lime_test methods via the test() method
$b->test()->is($request->getParameter('id'), 1);
$b->test()->is($response->getStatuscode(), 200);
$b->test()->is($response->getHttpHeader('content-type'), 'text/html;charset=utf-8');
$b->test()->like($response->getContent(), '/edit/');
But wait, there’s more!
• Who would want to run each test
manually, or write a script even (bah!)
• Put all tests in the “unit” folder
• then...
• $ symfony test:all
A code review: Rentpost
• Managing landlord/tenant relations
• PHP, Symfony, AJAX
Download