150 likes | 395 Views
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…
E N D
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 • Routing • Templating • Plugins • Unit Testing • ORB mapping (pluggable) • CRUD generation • Validation
Object Brokering (Model layer) • Automatically generates fully documented PHP classes from config file • Removes need for repeated DB calls, provides abstraction 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(); } schema.yml propel: weblog_post: _attributes: { phpName: Post } id: title: varchar(255) excerpt: longvarchar body: longvarchar created_at:
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> <?phpforeach ($findable_item_list as $findable_item): ?> <tr> <td><ahref="<?phpecho url_for('findable/edit?id='.$findable_item->getId()) ?>"><?phpecho $findable_item->getId() ?></a></td> <td><?phpecho $findable_item->getName() ?></td> <td><?phpecho $findable_item->getCardinality() ?></td> <td><?phpecho $findable_item->getPoints() ?></td> <td><?phpecho $findable_item->getDescription() ?></td> <td><?phpecho $findable_item->getSFindableCategoryId() ?></td> </tr> <?php endforeach; ?> </tbody> </table> <ahref="<?phpecho 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/imageThumb.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 • Functional test the controllers (WOW!) $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'); $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