• Share
  • Email
  • Embed
  • Like
  • Save
  • Private Content
Zend Framework 1.8 workshop
 

Zend Framework 1.8 workshop

on

  • 18,288 views

An introduction to Zend Framework 1.8 using Zend_Tool, Zend_Application, a simple DAO and a very simple model that uses that DAO.

An introduction to Zend Framework 1.8 using Zend_Tool, Zend_Application, a simple DAO and a very simple model that uses that DAO.
In the end you have a fully working application

Statistics

Views

Total Views
18,288
Views on SlideShare
14,235
Embed Views
4,053

Actions

Likes
21
Downloads
668
Comments
5

10 Embeds 4,053

http://www.harikt.com 2770
http://blog.nickbelhomme.com 1015
http://harikt.com 182
http://www.slideshare.net 48
http://translate.googleusercontent.com 12
http://static.slidesharecdn.com 11
http://zend-me.overblog.com 6
http://webcache.googleusercontent.com 5
http://fenix-utp.blogspot.com 3
http://209.85.135.132 1
More...

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel

15 of 5 previous next Post a comment

  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
  • Hi Nick, tanks for you answer,

    exactly, database transactions!!! :)
    is it possible with your example?!!

    again, sorry for my english!
    tanks
    Are you sure you want to
    Your message goes here
    Processing…
  • Hi Giolvani,

    Thank you for your kind words,
    Do you mean database transactions? Could you elaborate?

    Nick
    Are you sure you want to
    Your message goes here
    Processing…
  • Hi Nick!

    sorry for my english!

    first - congratulations for you slide, it is very good!
    second - is it possible with your example to control transaction?

    regards!
    Giolvani
    Are you sure you want to
    Your message goes here
    Processing…
  • Hi Clint,

    Thx for the feedback. More presentations are to follow somewhere within the coming weeks.

    Nick
    Feel free to visit my blog: http://blog.nickbelhomme.com
    Are you sure you want to
    Your message goes here
    Processing…
  • Thank you for this! It was very helpful in getting my first ZF project going.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

    Zend Framework 1.8 workshop Zend Framework 1.8 workshop Presentation Transcript

    • Zend Framework 1.8 workshop Zend Framework Presentation Nick Belhomme 17 June 2009 PHP5 Zend Certified Engineer + Zend Framework Certified Engineer
    • Zend Framework 1.8 workshop About Me: PHP5 Zend Certified Engineer (PHP5 ZCE) Zend Framework Certified Engineer (ZCE-ZF) ZF Contributor since 2008 Freelance Consultant PHP Community active member Nick Belhomme 17 June 2009 PHP5 Zend Certified Engineer + Zend Framework Certified Engineer
    • Zend Framework 1.8 workshop Zend Framework • Full Stack Framework • Component Library Nick Belhomme 17 June 2009 PHP5 Zend Certified Engineer + Zend Framework Certified Engineer
    • Zend Framework 1.8 workshop How to Get It? • Grab it from the download page: http://framework.zend.com/download/latest • Through Subversion: http://framework.zend.com/svn/framework/standard/trunk/ Nick Belhomme 17 June 2009 PHP5 Zend Certified Engineer + Zend Framework Certified Engineer
    • Zend Framework 1.8 workshop How To Install? Add it to your php.ini include_path directive include_path = .:/usr/local/lib/php:/PathToZendFrameworkLibrary That’s it! Nick Belhomme 17 June 2009 PHP5 Zend Certified Engineer + Zend Framework Certified Engineer
    • Zend Framework 1.8 workshop Starting A New Project With Zend_Tool First requirements: • For *nix environments copy the zf.sh and zf.php from the ZF/bin To the same directory as your PHP binary • For Windows: •copy the zf.php and zf.bat to the same directory as your PHP binary •add the php path to your PATH environment Var: # Right-click My Computer and select |Properties. # Select the Environment page. # In the System Variables area, click on PATH. # Add the php path to the end in the format VALUE1;VALUE2;c:php # Click Apply. # The changes take effect immediately. Nick Belhomme 17 June 2009 PHP5 Zend Certified Engineer + Zend Framework Certified Engineer
    • Zend Framework 1.8 workshop Creating the project “MyProject” $ zf create project /path/to/non-existent-dir-called-MyProject Nick Belhomme 17 June 2009 PHP5 Zend Certified Engineer + Zend Framework Certified Engineer
    • Zend Framework 1.8 workshop Adding a “MyProject” vhost Testing environment httpd.conf or httpd-vhosts.conf: <VirtualHost *:80> ServerName MyProject DocumentRoot absolute-path-to-MyProject-folder ErrorLog logsMyProject-error.log CustomLog logsMyProject-access.log common <Directory /> Options FollowSymLinks AllowOverride All Order allow,deny Allow from all Satisfy all </Directory> </VirtualHost> Nick Belhomme 17 June 2009 PHP5 Zend Certified Engineer + Zend Framework Certified Engineer
    • Zend Framework 1.8 workshop For windows: Add the following line to the /etc/host file 127.0.0.1 MyProject Restart apache, fire up the browser and go to: http://MyProject Nick Belhomme 17 June 2009 PHP5 Zend Certified Engineer + Zend Framework Certified Engineer
    • Zend Framework 1.8 workshop Nick Belhomme 17 June 2009 PHP5 Zend Certified Engineer + Zend Framework Certified Engineer
    • Zend Framework 1.8 workshop Creating an Action + View Go to the MyProject directory and issue the following command to create the helloAction in the IndexController and corresponding view: $ zf create action hello index For a full list of commands simply visit: http://framework.zend.com/manual/en/zend.tool.project.create-a-project.html Or issue the command: $ zf --help Nick Belhomme 17 June 2009 PHP5 Zend Certified Engineer + Zend Framework Certified Engineer
    • Zend Framework 1.8 workshop Nick Belhomme 17 June 2009 PHP5 Zend Certified Engineer + Zend Framework Certified Engineer
    • Zend Framework 1.8 workshop Creating a Module and Controller $ zf create module admin $ zf create controller index 1 admin Note: Trying to create a controller that already exists in another Module might result in an error. Depending on the Zend_Tool Verion you have. The solution is to apply the patch from ZF Issue Tracker: http://framework.zend.com/issues/browse/ZF-6853 Note: If you applied this patch, make sure all controller names in the admin module start with Admin_ Nick Belhomme 17 June 2009 PHP5 Zend Certified Engineer + Zend Framework Certified Engineer
    • Zend Framework 1.8 workshop Registering the Module class Bootstrap extends Zend_Application_Bootstrap_Bootstrap { protected function _initAutoloadModuleAdmin() { $autoloader = new Zend_Application_Module_Autoloader(array( 'namespace' => 'Admin', 'basePath' => APPLICATION_PATH.'/modules/admin', )); return $autoloader; } } The method is called automatically by Zend_Application as it starts with _init. You can choose the remainder of the method name as you like. Nick Belhomme 17 June 2009 PHP5 Zend Certified Engineer + Zend Framework Certified Engineer
    • Zend Framework 1.8 workshop Registering the Module myProject/application/configs/application.ini resources.frontController.moduleDirectory = APPLICATION_PATH "/modules" Nick Belhomme 17 June 2009 PHP5 Zend Certified Engineer + Zend Framework Certified Engineer
    • Zend Framework 1.8 workshop Setting up the Mysql Database Add to configs/application.ini [production] =============================== resources.db.adapter = PDO_MYSQL resources.db.params.host = localhost resources.db.params.username = nick resources.db.params.password = 12345 resources.db.params.dbname = myproject Nick Belhomme 17 June 2009 PHP5 Zend Certified Engineer + Zend Framework Certified Engineer
    • Zend Framework 1.8 workshop Create the database table CREATE TABLE users ( id int(11) NOT NULL auto_increment, username varchar(100) NOT NULL, pwd varchar(32) NOT NULL, firstname varchar(20) NOT NULL, PRIMARY KEY (id) ); Nick Belhomme 17 June 2009 PHP5 Zend Certified Engineer + Zend Framework Certified Engineer
    • Zend Framework 1.8 workshop Populate the table INSERT INTO users (username, pwd, firstname) VALUES ('NickBelhomme', md5('hello'), 'Nick'), ('ChanieNavez', md5('strong'), 'Chantal'), ('ElvisP', md5('presley'), 'Elvis'); Nick Belhomme 17 June 2009 PHP5 Zend Certified Engineer + Zend Framework Certified Engineer
    • Zend Framework 1.8 workshop The Model + DAO Nick Belhomme 17 June 2009 PHP5 Zend Certified Engineer + Zend Framework Certified Engineer
    • Zend Framework 1.8 workshop myProject/application/modules/admin/models/dao/Interface.php interface Admin_Model_Dao_Interface { public function find($id); public function insert($username, $pwd, $firstname); public function update($id, $username, $pwd, $firstname); public function delete($id); public function findAll(); } Common interface between all DAOs. Nick Belhomme 17 June 2009 PHP5 Zend Certified Engineer + Zend Framework Certified Engineer
    • Zend Framework 1.8 workshop myProject/application/modules/admin/models/dao/UserDB.php class Admin_Model_Dao_UserDB extends Zend_Db_Table implements Admin_Model_Dao_Interface { protected $_name = 'users'; public function find($id) { Very Simple DAO $id = (int)$id; $row = $this->fetchRow('id = ' . $id); (Data Access Object) if (!$row) { throw new Exception("Count not find row $id"); } return $row; } public function insert($username, $pwd, $firstname) { $data = array( 'username' => $username, 'pwd' => $this->hashPassword($pwd), 'firstname' => $firstname, ); parent::insert($data); } //... } Nick Belhomme 17 June 2009 PHP5 Zend Certified Engineer + Zend Framework Certified Engineer
    • Zend Framework 1.8 workshop myProject/application/modules/admin/models/Dao/UserDB.php class Admin_Model_Dao_UserDB extends Zend_Db_Table implements Admin_Model_Dao_Interface { //… public function update($id, $username, $pwd, $firstname) { $data = array( Very Simple DAO 'username' => $username, 'pwd' => $this->hashPassword($pwd), (Data Access Object) 'firstname' => $firstname, To explain the principle ); of an extra layer parent::update($data, 'id = '. (int)$id); } public function delete($id) { parent::delete('id =' . (int)$id); } public function findAll() { return $this->fetchAll(); } protected function hashPassword($pwd) { return md5($pwd); } } ?> Nick Belhomme 17 June 2009 PHP5 Zend Certified Engineer + Zend Framework Certified Engineer
    • Zend Framework 1.8 workshop myProject/application/modules/admin/models/User.php class Admin_Model_User { const DAO_CLASS = 'Admin_Model_Dao_UserDB'; protected $dao; public function __construct() { $daoClassName = self::DAO_CLASS; $this->dao = new $daoClassName(); The Model } using the DAO. public function find($id) { return $this->dao->find($id); Easy Switching between } Different data resources public function insert($username, $pwd, $firstname) { return $this->dao->insert($username, $pwd, $firstname); } public function update($id, $username, $pwd, $firstname) { return $this->dao->update($id, $username, $pwd, $firstname); } // ... } Nick Belhomme 17 June 2009 PHP5 Zend Certified Engineer + Zend Framework Certified Engineer
    • Zend Framework 1.8 workshop myProject/application/modules/admin/models/User.php <?php class Admin_Model_User { The Model // ... using the DAO. public function delete($id) { Easy Switching between return $this->dao->delete($id); Different data resources } public function findAll() { return $this->dao->findAll(); } } Nick Belhomme 17 June 2009 PHP5 Zend Certified Engineer + Zend Framework Certified Engineer
    • Zend Framework 1.8 workshop The Actions and Views Nick Belhomme 17 June 2009 PHP5 Zend Certified Engineer + Zend Framework Certified Engineer
    • Zend Framework 1.8 workshop Setting up the admin actions $ zf create action add index 1 admin $ zf create action edit index 1 admin $ zf create action delete index 1 admin Again if you are using a version of Zend_Tool which isn’t fully module Aware you can patch the Zend_Tool_Project_Provider_Action with if (self::hasResource($this->_loadedProfile, $name, $controllerName, $module)) Nick Belhomme 17 June 2009 PHP5 Zend Certified Engineer + Zend Framework Certified Engineer
    • Zend Framework 1.8 workshop The Admin_IndexController indexAction public function indexAction() { $users = new Admin_Model_User(); $this->view->users = $users->findAll(); } Nick Belhomme 17 June 2009 PHP5 Zend Certified Engineer + Zend Framework Certified Engineer
    • Zend Framework 1.8 workshop The corresponding index.phtml view script: myProject/application/modules/admin/views/scripts/index/index.phtml <p><a href="<?php echo $this->url(array('controller'=>'index', 'action'=>'add'));?>">Add new user</a></p> <table> <tr> <th>Username</th> <th>First Name</th> <th>&nbsp;</th> </tr> <?php foreach($this->users as $user) : ?> <tr> <td><?php echo $this->escape($user->username);?></td> <td><?php echo $this->escape($user->firstname);?></td> <td> <a href="<?php echo $this->url(array('controller'=>'index', 'action'=>'edit', 'id'=>$user->id));?>">Edit</a> <a href="<?php echo $this->url(array('controller'=>'index', 'action'=>'delete', 'id'=>$user->id));?>">Delete</a> </td> </tr> <?php endforeach; ?> </table> Nick Belhomme 17 June 2009 PHP5 Zend Certified Engineer + Zend Framework Certified Engineer
    • Zend Framework 1.8 workshop Nick Belhomme 17 June 2009 PHP5 Zend Certified Engineer + Zend Framework Certified Engineer
    • Zend Framework 1.8 workshop The Admin_IndexController deleteAction REST: public function deleteAction() irreversible { action use if ($this->getRequest()->isPost()) { POST not $del = $this->getRequest()->getPost('del'); GET if ($del == 'Yes') { $id = $this->getRequest()->getPost('id'); $users = new Admin_Model_User(); $users->delete($id); } $this->_redirect('/admin/'); } else { $id = $this->_getParam('id', 0); $users = new Admin_Model_User(); $this->view->user = $users->find($id); } } Nick Belhomme 17 June 2009 PHP5 Zend Certified Engineer + Zend Framework Certified Engineer
    • Zend Framework 1.8 workshop The corresponding delete.phtml view script: myProject/application/modules/admin/views/scripts/index/delete.phtml <p>Are you sure that you want to delete '<?php echo $this->escape($this->user['username']); ?>' aka '<?php echo $this->escape($this->user['firstname']); ?>'? </p> <form action="<?php echo $this->url(array('action'=>'delete')); ?>" method="post"> <fieldset> <input type="hidden" name="id" value="<?php echo $this->escape($this->user['id']); ?>" /> <input type="submit" name="del" value="Yes" /> <input type="submit" name="del" value="No" /> </fieldset> </form> Nick Belhomme 17 June 2009 PHP5 Zend Certified Engineer + Zend Framework Certified Engineer
    • Zend Framework 1.8 workshop Nick Belhomme 17 June 2009 PHP5 Zend Certified Engineer + Zend Framework Certified Engineer
    • Zend Framework 1.8 workshop The Admin_IndexController addAction public function addAction() { if ($this->getRequest()->isPost()) { $action = $this->getRequest()->getPost('action'); if ($action == 'add') { $username = $this->getRequest()->getPost('username'); $pwd = $this->getRequest()->getPost('pwd'); $firstname = $this->getRequest()->getPost('firstname'); $users = new Admin_Model_User(); $users->insert($username, $pwd, $firstname); } $this->_redirect('/admin/'); } } Nick Belhomme 17 June 2009 PHP5 Zend Certified Engineer + Zend Framework Certified Engineer
    • Zend Framework 1.8 workshop The corresponding add.phtml view script: myProject/application/modules/admin/views/scripts/index/add.phtml <h2>Add a new User</h2> <form action="<?php echo $this->url(array('action'=>'add')); ?>" method="post"> <fieldset> username: <input type="text" name="username"><br/> password: <input type="password" name="pwd"><br/> firstname: <input type="text" name="firstname"><br/> <input type="submit" name="action" value="add" /> </fieldset> </form> Nick Belhomme 17 June 2009 PHP5 Zend Certified Engineer + Zend Framework Certified Engineer
    • Zend Framework 1.8 workshop Nick Belhomme 17 June 2009 PHP5 Zend Certified Engineer + Zend Framework Certified Engineer
    • Zend Framework 1.8 workshop The Admin_IndexController editAction public function editAction() { if ($this->getRequest()->isPost()) { $action = $this->getRequest()->getPost('action'); if ($action == 'edit') { $id = $this->getRequest()->getPost('id'); $username = $this->getRequest()->getPost('username'); $pwd = $this->getRequest()->getPost('pwd'); $firstname = $this->getRequest()->getPost('firstname'); $users = new Admin_Model_User(); $users->update($username, $pwd, $firstname); } $this->_redirect('/admin/'); } else { $id = $this->_getParam('id', 0); $users = new Admin_Model_User(); $this->view->user = $users->find($id); } } Nick Belhomme 17 June 2009 PHP5 Zend Certified Engineer + Zend Framework Certified Engineer
    • Zend Framework 1.8 workshop The corresponding edit.phtml view script: myProject/application/modules/admin/views/scripts/index/edit.phtml <h2>Edit User: <?php echo $this->escape($this->user->username); ?></h2> <form action="<?php echo $this->url(array('action'=>'edit')); ?>" method="post"> <fieldset> <input type="hidden" name="id" value="<?php echo $this->escape($this->user->id); ?>"> username: <input type="text" name="username" value="<?php echo $this->escape($this->user->username); ?>"><br/> password: <input type="password" name="pwd"><br/> firstname: <input type="text" name="firstname" value="<?php echo $this->escape($this->user->firstname); ?>"><br/> <input type="submit" name="action" value="edit" /> </fieldset> </form> Nick Belhomme 17 June 2009 PHP5 Zend Certified Engineer + Zend Framework Certified Engineer
    • Zend Framework 1.8 workshop Nick Belhomme 17 June 2009 PHP5 Zend Certified Engineer + Zend Framework Certified Engineer
    • Zend Framework 1.8 workshop Authentication: Zend_Auth Authentication is loosely defined as determining whether an entity actually is what it purports to be (i.e., identification), based on some set of credentials. http://framework.zend.com/manual/en/zend.auth.html#zend.auth.introduction Nick Belhomme 17 June 2009 PHP5 Zend Certified Engineer + Zend Framework Certified Engineer
    • Zend Framework 1.8 workshop Append the DAO and User Classes interface Admin_Model_Dao_Interface { // … public function findCredentials($username, $pwd); } class Admin_Model_Dao_UserDB extends Zend_Db_Table implements Admin_Model_Dao_Interface { // … public function findCredentials($username, $pwd) { $select = $this->select()->where('username = ?', $username) ->where('pwd = ?', $this->hashPassword($pwd)); $row = $this->fetchRow($select); if($row) { return $row; } return false; } } Nick Belhomme 17 June 2009 PHP5 Zend Certified Engineer + Zend Framework Certified Engineer
    • Zend Framework 1.8 workshop Append the DAO and User Classes class Admin_Model_User { // ... public function findCredentials($username, $password) { return $this->dao->findCredentials($username, $password); } } Nick Belhomme 17 June 2009 PHP5 Zend Certified Engineer + Zend Framework Certified Engineer
    • Zend Framework 1.8 workshop Always use a vendor or project prefix And inform the autoloader of library prefixes Prepend the application bootstrap class Bootstrap extends Zend_Application_Bootstrap_Bootstrap { protected function _initAutoloadModuleDefault() { $autoloader = new Zend_Application_Module_Autoloader(array( 'namespace' => 'Myp', 'basePath' => dirname(__FILE__) )); return $autoloader; } //... } Nick Belhomme 17 June 2009 PHP5 Zend Certified Engineer + Zend Framework Certified Engineer
    • Zend Framework 1.8 workshop Create an application model: The Auth Adapter myProject/application/models/AuthAdapter.php class Myp_Model_AuthAdapter implements Zend_Auth_Adapter_Interface { protected $username; protected $password; protected $user; public function __construct($username, $password) { $this->username = $username; $this->password = $password; $this->user = new Admin_Model_User(); } public function authenticate() { $match = $this->user->findCredentials($this->username, $this->password); if(!$match) { $result = new Zend_Auth_Result(Zend_Auth_Result::FAILURE_CREDENTIAL_INVALID, null); } else { $user = current($match); $result = new Zend_Auth_Result(Zend_Auth_Result::SUCCESS, $user); } return $result; } } Nick Belhomme 17 June 2009 PHP5 Zend Certified Engineer + Zend Framework Certified Engineer
    • Zend Framework 1.8 workshop The IndexController loginAction $ zf create action login index 1 Nick Belhomme 17 June 2009 PHP5 Zend Certified Engineer + Zend Framework Certified Engineer
    • Zend Framework 1.8 workshop The IndexController loginAction public function loginAction() { $auth = Zend_Auth::getInstance(); if(Zend_Auth::getInstance()->hasIdentity()) { $this->_redirect('/index/hello'); } else if ($this->getRequest()->isPost()) { $action = $this->getRequest()->getPost('action'); if ($action == 'login') { $username = $this->getRequest()->getPost('username'); $pwd = $this->getRequest()->getPost('pwd'); $authAdapter = new Myp_Model_AuthAdapter($username, $pwd); $result = $auth->authenticate($authAdapter); if(!$result->isValid()) { switch ($result->getCode()) { case Zend_Auth_Result::FAILURE_CREDENTIAL_INVALID: $this->view->error = 'user credentials not found'; } } else { $this->_redirect('/index/hello'); } } } } Nick Belhomme 17 June 2009 PHP5 Zend Certified Engineer + Zend Framework Certified Engineer
    • Zend Framework 1.8 workshop The corresponding login.phtml view script: myProject/application/views/scripts/index/login.phtml <h2>Login</h2> <form action="<?php echo $this->url(array('action'=>'login')); ?>" method="post"> <fieldset> username: <input type="text" name="username"><br/> password: <input type="password" name="pwd"><br/> <div><?php echo $this->escape($this->error);?></div> <input type="submit" name="action" value="login" /> </fieldset> </form> Nick Belhomme 17 June 2009 PHP5 Zend Certified Engineer + Zend Framework Certified Engineer
    • Zend Framework 1.8 workshop Nick Belhomme 17 June 2009 PHP5 Zend Certified Engineer + Zend Framework Certified Engineer
    • Zend Framework 1.8 workshop The IndexController helloAction public function helloAction() { if(Zend_Auth::getInstance()->hasIdentity()) { $this->view->user = Zend_Auth::getInstance()->getIdentity(); } } Nick Belhomme 17 June 2009 PHP5 Zend Certified Engineer + Zend Framework Certified Engineer
    • Zend Framework 1.8 workshop The corresponding hello.phtml view script: myProject/application/views/scripts/index/hello.phtml <?php if(isset($this->user)):?> welcome <?php echo $this->escape($this->user['firstname']); ?>, to the MyProject application. <?php else: ?> you need to login first. <a href="<?php echo $this->url(array('action' => 'login')); ?>">login</a> <?php endif; ?> Nick Belhomme 17 June 2009 PHP5 Zend Certified Engineer + Zend Framework Certified Engineer
    • Zend Framework 1.8 workshop Nick Belhomme 17 June 2009 PHP5 Zend Certified Engineer + Zend Framework Certified Engineer
    • Zend Framework 1.8 workshop And that is Authentication. if(Zend_Auth::getInstance()->hasIdentity()) Nick Belhomme 17 June 2009 PHP5 Zend Certified Engineer + Zend Framework Certified Engineer
    • Zend Framework 1.8 workshop What about? •Authorization (Zend_Acl) •Filter Input, Escape Output? •Input Validation (Zend_Validate) •Input filtering (Zend_Filter) •Dynamic Forms (Zend_Form) And all the other great stuff???!!!! Nick Belhomme 17 June 2009 PHP5 Zend Certified Engineer + Zend Framework Certified Engineer
    • Zend Framework 1.8 workshop There is always next time! 1 hour courses, remember??? ;) Nick Belhomme 17 June 2009 PHP5 Zend Certified Engineer + Zend Framework Certified Engineer
    • Zend Framework 1.8 workshop Resources •http://blog.nickbelhomme.com •http://www.slideshare.net/weierophinney/zend-framework-workshop-dpc09 •http://en.wikipedia.org/wiki/Zend_Framework •http://blog.astrumfutura.com/archives/373-The-M-in-MVC-Why-Models- are-Misunderstood-and-Unappreciated.html Source code of this workshop available at: http://blog.nickbelhomme.com/wp-content/uploads/workshopzf1.8.zip Nick Belhomme 17 June 2009 PHP5 Zend Certified Engineer + Zend Framework Certified Engineer