Zend Framework Workshop, DPC09
Upcoming SlideShare
Loading in...5
×
 

Like this? Share it with your network

Share

Zend Framework Workshop, DPC09

on

  • 33,293 views

Zend Framework workshop presented at the Dutch PHP Conference, 2009. This was a six-hour long tutorial showing a number of components and one recommended approach to writing Zend Framework ...

Zend Framework workshop presented at the Dutch PHP Conference, 2009. This was a six-hour long tutorial showing a number of components and one recommended approach to writing Zend Framework applications.

Statistics

Views

Total Views
33,293
Views on SlideShare
32,083
Embed Views
1,210

Actions

Likes
88
Downloads
1,485
Comments
1

32 Embeds 1,210

http://mateusztymek.pl 468
http://www.kimbs.cn 289
http://www.slideshare.net 134
http://dariuszsadowski.blogspot.com 83
http://javoft.net 57
http://kbs.kimbs.cn 53
http://localhost 29
http://jimbot-portal.ru 19
http://onwebdev.blogspot.com 13
http://blog.gabrieleromanato.com 10
http://www.e-presentations.us 9
http://bpclan.ru 7
http://dev.mateusztymek.pl 6
http://coderwall.com 4
http://www.kimbs-local.info 3
http://karpati.org 3
http://test.idoc.vn 2
http://translate.googleusercontent.com 2
http://static.slidesharecdn.com 2
http://www.mefeedia.com 2
http://kimbs-local.info 2
http://kimbs.info 2
http://cpb2.altego.net 2
http://east-tel.ru 1
http://www.php-talks.com 1
http://onwebdev.blogspot.in 1
http://webcache.googleusercontent.com 1
http://localhost:3000 1
http://www.it-efficiency.net 1
http://127.0.0.1:82 1
http://tricklet.net 1
https://tasks.crowdflower.com 1
More...

Accessibility

Categories

Upload Details

Uploaded via as OpenOffice

Usage Rights

CC Attribution-NonCommercial-ShareAlike LicenseCC Attribution-NonCommercial-ShareAlike LicenseCC Attribution-NonCommercial-ShareAlike License

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
  • Great Frame work 2011 waiting

    http://www.touchyard.com
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

Zend Framework Workshop, DPC09 Presentation Transcript

  • 1. Zend Framework Workshop Matthew Weier O'Phinney Project Lead Zend Framework Dutch PHP Conference 11 April 2009 Amsterdam
  • 2. About me:
    • ZF Contributor since January 2006
    • 3. Assigned to the ZF team in July 2007
    • 4. Promoted to Software Architect in April 2008
    • 5. Project Lead since April 2009
    Photo © 2009, Chris Shiflett
  • 6. What is Zend Framework?
  • 7. Full Stack Framework?
  • 8. Component Library?
  • 9. Both.
  • 10.  
  • 11. Today's Roadmap
  • 12.
    • Quick Start
    • 13. Models
    • 14. Service Layers
      • Authentication and Authorization
      • 15. Zend_Form
      • 16. Plugin Loading
    • Controllers
    • 17. Views and Layouts
    • 18. Service Endpoints
  • 19. Quick Start or, “How I learned to use and love Zend_Tool”
  • 20.
    • In bin/zf.sh or bin/zf.bat of your ZF install (choose based on your OS)
    • 21. Place bin/ in your path, or create an alias on your path: alias zf=/path/to/bin/zf.sh
    • 22. Or use pear.zfcampus.org PEAR channel
    Locate the zf utility
  • 23. Create the project # Unix: % zf.sh create project quickstart # DOS/Windows: C:> zf.bat create project quickstart
  • 24. Add ZF to the project # Symlink: % cd library; ln -s path/to/ZendFramework/library/Zend . # Copy: % cd library; cp -r path/to/ZendFramework/library/Zend .
  • 25. Create a vhost <VirtualHost *: 80 > ServerAdmin you@atyour.tld DocumentRoot /abs/path/to/quickstart/public ServerName quickstart <Directory /abs/path/to/quickstart/public > DirectoryIndex index.php AllowOverride All Order allow,deny Allow from all </Directory> </VirtualHost>
  • 26. Add a hosts entry 127.0.0.1 quickstart
  • 27. Fire up your browser!
  • 28. Looking under the hood
  • 29. The directory tree quickstart |-- application | |-- Bootstrap.php | |-- configs | | `-- application.ini | |-- controllers | | |-- ErrorController.php | | ` -- IndexController.php | |-- models | `-- views | |-- helpers | ` -- scripts | |-- error | | `-- error.phtml | ` -- index | `-- index.phtml |-- library |-- public | ` -- index.php `-- tests |-- application | ` -- bootstrap.php |-- library | `-- bootstrap.php ` -- phpunit.xml 14 directories, 10 files
  • 30. The bootstrap <?php class Bootstrap extends Zend_Application_Bootstrap_Bootstrap { }
  • 31. Configuration [production] phpSettings.display_startup_errors = 0 phpSettings.display_errors = 0 includePaths.library = APPLICATION_PATH &quot;/../library&quot; bootstrap.path = APPLICATION_PATH &quot;/Bootstrap.php&quot; bootstrap.class = &quot;Bootstrap&quot; resources.frontController.controllerDirectory = APPLICATION_PATH &quot;/controllers&quot; [staging : production] [testing : production] phpSettings.display_startup_errors = 1 phpSettings.display_errors = 1 [development : production] phpSettings.display_startup_errors = 1 phpSettings.display_errors = 1
  • 32. Index (default) controller <?php class IndexController extends Zend_Controller_Action { public function init() { /* Initialize action controller here */ } public function indexAction() { // action body } }
  • 33. Error controller class ErrorController extends Zend_Controller_Action { public function errorAction() { $errors = $this ->_getParam( 'error_handler' ); switch ( $errors ->type) { case 'EXCEPTION_NO_CONTROLLER': case 'EXCEPTION_NO_ACTION': // 404 error -- controller or action not found $this ->getResponse()->setHttpResponseCode( 404 ); $this ->view->message = 'Page not found' ; break ; default: // application error $this ->getResponse()->setHttpResponseCode( 500 ); $this ->view->message = 'Application error' ; break ; } $this ->view->exception = $errors ->exception; $this ->view->request = $errors ->request; } }
  • 34. Index view (home page) <center> <div id = &quot;welcome&quot; > <h1> Welcome to the Zend Framework! </h1> <h3> This is your project's main page </h3> <!-- and a little bit more markup --> </div> </center>
  • 35. Error view <h1>An error occurred</h1> <h2><?php echo $this ->message ?></h2> <?php if ( 'development' == APPLICATION_ENV): ?> <h3>Exception information:</h3> <p> <b>Message:</b> <?php echo $this ->exception->getMessage() ?> </p> <h3>Stack trace:</h3> <pre> <?php echo $this ->exception->getTraceAsString() ?> </pre> <h3>Request Parameters:</h3> <pre> <?php var_dump ( $this ->request->getParams()) ?> </pre> <?php endif ?>
  • 36. .htaccess file SetEnv APPLICATION_ENV development RewriteEngine On RewriteCond %{REQUEST_FILENAME} -s [OR] RewriteCond %{REQUEST_FILENAME} -l [OR] RewriteCond %{REQUEST_FILENAME} -d RewriteRule ^.*$ - [NC,L] RewriteRule ^.*$ index.php [NC,L]
  • 37. index.php (environment) <?php // Define path to application directory defined ( 'APPLICATION_PATH' ) || define ( 'APPLICATION_PATH' , realpath ( dirname (__FILE__) . '/../application' )); // Define application environment defined ( 'APPLICATION_ENV' ) || define ( 'APPLICATION_ENV' , ( getenv ( 'APPLICATION_ENV' ) ? getenv ( 'APPLICATION_ENV' ) : 'production' )); // Ensure library/ is on include_path set_include_path( implode (PATH_SEPARATOR, array ( realpath (APPLICATION_PATH . '/../library' ), get_include_path(), )));
  • 38. index.php (application) /** Zend_Application */ require_once 'Zend/Application.php' ; // Create application, bootstrap, and run $application = new Zend_Application( APPLICATION_ENV, APPLICATION_PATH . '/configs/application.ini' ); $application ->bootstrap() ->run();
  • 39. Models or, “Classes are an OOP developer's play-dough”
  • 40. First, there is no Zend_Model. Stop asking.
  • 41. What you shouldn't do
  • 42.
    • Never tie your models directly to data access
      • Makes testing hard
      • 43. Makes refactoring later to use caching or an SOA hard
      • 44. Makes restructuring your database problematic (your code could break)
  • 45. Models are just classes. Create classes.
  • 46. Where? quickstart |-- application | |-- Bootstrap.php | |-- configs | | `-- application.ini | |-- controllers | |-- models | | |-- Team.php | | ` -- User.php | `-- views |-- library |-- public ` -- tests
  • 47. Models usually have metadata or attributes; define them
  • 48. Class properties <?php class App_Model_User { protected $_email ; protected $_firstname ; protected $_id ; protected $_lastname ; protected $_password ; protected $_role = 'pig' ; protected $_username ; }
  • 49. Models usually have behavior surrounding the metadata; define accessors and mutators
  • 50. Accessors and mutators <?php class App_Model_User { /* ... properties ... */ public function setEmail( $email ) { } public function getEmail() { } public function setFirstname( $name ) { } public function getFirstname() { } public function setLastname( $name ) { } public function getLastname() { } /* ... and so on ... */ }
  • 51. Models usually have behaviors ; define some methods
  • 52. Methods <?php class App_Model_User { /* ... */ public function walk() { } public function sleep () { } }
  • 53. Now, persist your models
  • 54. Define a schema CREATE TABLE &quot;user&quot; ( id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, username VARCHAR ( 64 ) NOT NULL UNIQUE , email VARCHAR ( 64 ) NOT NULL , firstname VARCHAR ( 64 ), lastname VARCHAR ( 64 ), role VARCHAR ( 16 ) NOT NULL DEFAULT 'pig' );
  • 55. Tell your application about your database [production] ; ... resources.db.adapter = &quot;pdo_sqlite&quot; resources.db.params.dbname = APPLICATION_PATH &quot;/../data/db/users.db&quot; [staging : production] [testing : production] ; ... resources.db.params.dbname = APPLICATION_PATH &quot;/../data/db/users-test.db&quot; [development : production] ; ... resources.db.params.dbname = APPLICATION_PATH &quot;/../data/db/users-dev.db&quot;
  • 56. Use a Table Data Gateway – Zend_Db_Table <?php class App_Model_DbTable_User extends Zend_Db_Table_Abstract { protected $_name = 'user' ; } quickstart |-- application | |-- models | | |-- DbTable | | | `-- User.php | | |-- Team.php | | ` -- User.php | `-- views |-- library |-- public |-- tests
  • 57. Tangent: a bit about Zend_Db_Table and relational datasets
  • 58. Zend_Db_Table Relations (parent table) <?php class App_Model_DbTable_Team extends Zend_Db_Table_Abstract { protected $_name = 'team' ; protected $_dependentTables = array ( 'App_Model_DbTable_TeamUser ' ); }
  • 59. Zend_Db_Table Relations (child table) <?php class App_Model_DbTable_TeamUser extends Zend_Db_Table_Abstract { protected $_name = 'team_user' ; protected $_referenceMap = array ( 'Team' => array ( 'columns' => 'team_id' , 'refTableClass' => 'App_Model_DbTable_Team' , 'refColumns' => 'id' ), ); }
  • 60. Zend_Db_Table Using Relations <?php $users = $team ->findDependentRowset( 'App_Model_DbTable_TeamUser' ); $team = $teamUser ->findParentRow();
  • 61. Zend_Db_Table Using many-to-many relations <?php $users = $team ->findManyToManyRowset( 'App_Model_DbTable_User' , 'App_Model_DbTable_TeamUser' , );
  • 62. … and now back to our story
  • 63. Create a Data Mapper: map your Model to the DAO <?php class App_Model_UserDataMapper { public function save(App_Model_User $user ) { $dao = $this ->getDao(); $id = $user ->getId(); if (! $id ) { $id = $dao ->insert( $user ->toArray()); $record = $dao ->find( $id )-> current (); } else { $dao ->update( $user ->toArray(), array ( 'id = ?' , $id )); $record = $dao ->find( $id )-> current (); } $user ->setOptions( $record ->toArray()); return $user ; } }
  • 64. Data Mappers: Great for caching! <?php class App_Model_UserDataMapper { public function fetchAll() { $cache = $this ->getCache(); if (! $users = $cache ->load( 'users' )) { $records = $this ->getDao()->fetchAll( '1' ); $users = array (); foreach ( $records as $record ) { $users [] = new App_Model_User( $record ->toArray()); } $cache ->save( $users , 'users' ); } return $users ; } }
  • 65.
    • Data !== Database
    • 66. Your Data Access Object could access a service...
    Note on Data Access Objects
  • 67. Data Access to a service: <?php class App_Model_Dao_User { public function fetchAll( $criteria ) { return $this ->getXmlRpcClient() ->fetch( $criteria ); } }
  • 68. Of names and autoloading
  • 69. Always use a vendor or project prefix
  • 70. Inform the autoloader of library prefixes
  • 71. Registering namespaces with the autoloader <?php require_once 'Zend/Loader/Autoloader.php' ; $autoloader = Zend_Loader_Autoloader::getInstance(); $autoloader ->registerNamespace( 'Scrum_' ); [production] autoloaderNamespaces[] = &quot;Scrum_&quot; ; ...
  • 72. Resources can be autoloaded, too
  • 73. Autoloading standard application resources <?php class Bootstrap extends Zend_Application_Bootstrap_Bootstrap { protected function _initResourceLoader() { $loader = new Zend_Application_Module_Autoloader( array ( 'namespace' => 'App' , 'basePath' => dirname (__FILE__), ) ); return $loader ; } }
  • 74. Conventional directory structure quickstart |-- application | |-- forms => App_Form_ | |-- models => App_Model_ | | |-- DbTable => App_Model_DbTable | | | `-- User.php => App_Model_DbTable_User | | |-- Team.php => App_Model_User | |-- plugins => App_Plugin_ | |-- services => App_Service_ | |-- views | | |-- filters => App_View_Filter | | |-- helpers => App_View_Helper
  • 75. Service Layers or, “Re-use this!”
  • 76. Applications are like onions; they have layers. Photo © 2008, Mike Chaput-Branson
  • 77. The Service Layer provides application logic on top of your models
  • 78. Service layer in perspective Data Access Objects and Data store(s) Data Mappers Domain Models Service Layer
  • 79.
    • Resource marshalling and dependency injection
    • 80. Application specific logic:
      • Authentication and Authorization
      • 81. Input filtering/data validation
      • 82. Search indexing
      • 83. etc.
    What's found in Service Layers?
  • 84. Authentication: Zend_Auth
  • 85. Authentication is the act of verifying somebody is who they say they are
  • 86.
    • Authentication adapters perform the work of authentication
    • 87. Zend_Auth handles authentication persistence
  • 88. Authentication adapter <?php class App_Model_AuthAdapter implements Zend_Auth_Adapter_Interface { protected $_user ; public function __construct(App_Model_User $user ) { $this ->_user = $user ; } // ... }
  • 89. Authentication adapter (cont.) public function authenticate() { $user = $this ->_user; $mapper = $user ->getDataMapper(); $matches = $mapper ->fetchAll( array ( 'username = ?' => $user ->username, 'password = ?' => md5 ( $user ->password), )); if (empty( $matches )) { $result = new Zend_Auth_Result(- 3 , null); } else { $user = current ( $matches ); $result = new Zend_Auth_Result( 1 , $user ->toArray()); } return $result ; }
  • 90. Initial “user” service class App_Service_User { protected $_userModel ; protected $_userModelOptions ; public function __construct( $options = array ()) { } public function getUserModelOptions() { } public function setUserModelOptions( array $options ) { } public function getUserModel( $newInstance = true) { if ( $newInstance ) { return new App_Model_User( $this ->getUserModelOptions()); } if (null === $this ->_userModel) { $this ->setUserModel(new App_Model_User( $this ->getUserModelOptions())) ; } return $this ->_userModel; } public function setUserModel( $model ) { } }
  • 91. “ Login” method class App_Service_User { // ... public function login( array $data ) { $user = $this ->getUserModel(); $user ->setOptions( $data ); $authAdapter = new App_Model_AuthAdapter( $user ); $auth = Zend_Auth::getInstance(); $result = $auth ->authenticate( $authAdapter ); if (! $result ->isValid()) { return $result ; } return true; } }
  • 92. Retrieving an authenticated user $auth = Zend_Auth::getInstance(); if ( $auth ->hasIdentity()) { $user = new App_Model_User( $auth ->getIdentity() ); }
  • 93. Authorization: Zend_Acl
  • 94. Authorization is the act of determining if somebody has permissions to perform an action on a given resource
  • 95.
    • Roles – what requests the action
    • 96. Resources – what is being acted upon
    • 97. Rights – the privileges a role has for a given resource
    The three “R”s:
  • 98.
    • Implement Zend_Acl_Role_Interface
      • One method: getRoleId()
    • Can simply use Zend_Acl_Role, but this doesn't tie directly to your models
    Roles
  • 99. Revisiting the User model Making it an ACL role <?php class App_Model_User implements Zend_Acl_Role_Interface { // ... protected $_role = 'pig' ; public function getRoleId() { return $this ->_role; } }
  • 100.
    • Implement Zend_Acl_Resource_Interface
      • One method: getResourceId()
    • Can simply use Zend_Acl_Resource, but this doesn't tie directly to your models
    Resources
  • 101. The “sprint” model Making it an ACL resource <?php class Scrum_Model_Sprint implements Zend_Acl_Resource_Interface { // ... public function getResourceId() { return 'sprint' ; } }
  • 102.
    • Zend_Acl denies all access by default
    • 103. You must whitelist role access to resources
    • 104. You can also blacklist , though this is less common or necessary
    Rights
  • 105. Defining ACLs <?php class Scrum_Model_Acl extends Zend_Acl { public function __construct() { $this ->addRole( 'pig' ) ->addRole( 'chicken' ) ->addRole( 'scrummaster' , array ( 'pig' )); $this ->add( 'sprint' ) ->add( 'backlog' ); $this ->allow( 'pig' , 'sprint' , array ( 'read' , 'write' )) ->allow( 'pig' , 'backlog' , array ( 'read' )) ->allow( 'scrummaster' , 'backlog' , array ( 'write' )) ->allow( 'chicken' , 'backlog' , array ( 'read' )) ->allow( 'chicken' , 'sprint' , array ( 'read' )); } }
  • 106.
    • Remember the three “R”s; check that:
      • The Role
      • 107. acting on the resource
      • 108. has rights to perform the action
    • Use the isAllowed() method
    Checking ACLs
  • 109. Authorization <?php class Scrum_Service_Sprint { public function create( array $data ) { $acl = $this ->getAcl(); $sprint = $this ->getSprintModel(); $user = $this ->getCurrentUser(); if (! $acl ->isAllowed( $user , $sprint , 'write' )) { throw new Scrum_Service_InvalidUserException(); } // ... } }
  • 110. Data filtering and Validation: Zend_Form, Zend_Filter, and Zend_Validate
  • 111.
    • Acts as a general purpose normalization and validation filter
    • 112. Form decorators make it trivial to create the markup needed to capture data
    • 113. Encapsulates i18n/l10n concerns for validation error reporting
    Why Zend_Form?
  • 114.
    • Within Zend_Form, provides validation chains
    • 115. Provides validation error reporting
    • 116. Marking an element as “required” prepends a “NotEmpty” validator to the validation chain
    • 117. Can be used standalone
    Zend_Validate
  • 118. Zend_Validate Adding validators to an element <?php // Args: // - validator &quot;short&quot; name // - break on failure flag // - Options to pass to validator constructor $element ->addValidator( 'Regex' , true, array ( '/^[a-z][a-z0-9_.-]{2,}$/' ) );
  • 119. Zend_Validate Adding validators at element creation <?php $form ->addElement( 'text' , 'username' , array ( 'required' => true, 'validators' => array ( array ( 'Regex' , true, array ( '/^[a-z][a-z0-9_.-]{2,}$/' ) ), ), ));
  • 120.
    • Within Zend_Form, provides normalization chains
    • 121. Normalizes input prior to validation, or on requesting the element value
    • 122. Can be used standalone
    Zend_Filter
  • 123. Zend_Filter Adding filters to an element <?php // Arguments: // - String: filter &quot;short&quot; name // - OR Array: // - Filter &quot;short&quot; name // - array of constructor options $element ->addFilter( 'StringToLower' , );
  • 124. Zend_Filter Adding filters at element creation <?php $form ->addElement( 'text' , 'username' , array ( 'filters' => array ( 'StringToLower' , ) ));
  • 125.
    • Zend_Form:
      • Aggregates elements and groups (display groups and sub forms)
      • 126. Provides full and partial validation of the elements
    • Elements:
      • Aggregate metadata
      • 127. Contain filter and validation chains
    Zend_Form
  • 128.
    • Display Groups:
      • Aggregate elements purely for display purposes
    • Sub forms:
      • Aggregate elements related semantically
      • 129. Provides namespacing of elements via array notation
    Zend_Form
  • 130. Zend_Form Defining forms class App_Form_User extends Zend_Form { public function init() { $this ->addElement( 'text' , 'username' , array ( 'label' => 'Username:' , 'required' => true, 'filters' => array ( 'StringToLower' , ), 'validators' => array ( array ( 'Regex' , true, array ( '/^[a-z][a-z0-9_.-]{2,}$/' )), ), )); // ... } }
  • 131. Zend_Form A note on buttons... <?php class App_Form_User extends Zend_Form { public function init() { // ... $this ->addElement( 'submit' , 'submit' , array ( 'label' => 'Done' , 'required' => false, 'ignore' => true, )); } }
  • 132. Zend_Form Variant: Dojo-aware form <?php class App_Form_User extends Zend_Dojo_Form { public function init() { $this ->addElement( 'TextBox' , 'username' , array ( // ...
  • 133. Using forms as validators in your Service Layer
  • 134. Form accessors <?php class App_Service_User { protected $_form = array (); public function setUserForm(Zend_Form $form ) { $this ->_form[ 'user' ] = $form ; } public function getUserForm() { if (empty( $this ->_form[ 'user' ])) { $this ->setUserForm(new App_Form_User()); } return $this ->_form[ 'user' ]; } }
  • 135. Form as validator <?php class App_Service_User { public function add( array $data ) { $form = $this ->getUserForm(); if (! $form ->isValid( $data )) { return false; } $model = $this ->getUserModel(); $model ->setOptions( $form ->getValues()); return $model ->save(); } }
  • 136. Interlude: Plugin architectures or, “Override anything!”
  • 137.
    • Aggregates prefix => path pairs: Zend_Validate_ => Zend/Validate/
    • 138. Acts as a LIFO stack
    • 139. Specify class name minus prefix to load: $class = $loader->load('StringTrim'); // Zend_Filter_StringTrim
    Zend_Loader_PluginLoader
  • 140.
    • Zend_Application_Bootstrap
    • 141. Zend_Form
    • 142. Zend_View
    • 143. Zend_Amf_Server
    • 144. Zend_Paginator
    • 145. Zend_Controller_Action_HelperBroker
    • 146. More...
    Plugin-aware components
  • 147.
    • Use short names instead of Really_Long_Underscored_Names
    • 148. Define plugins via configuration options instead of in your code
    • 149. Drop overrides into your filesystem, and get behavior changes
    Benefits of plugins
  • 150. Example validate plugin <?php // in library/My/Validate/IsFoo.php class My_Validate_IsFoo extends Zend_Validate_Abstract { const NOT_FOO = 'notFoo' ; protected $_messageTemplates = array ( self::NOT_FOO => 'Value %value% is not foo' , ); public function isValid( $value ) { if (! $value == 'foo' ) { $this ->_error(self::NOT_FOO); return false; } return true; } }
  • 151. Registering the prefix-path <?php // General: $loader ->addPrefixPath( 'My_Validate' , 'My/Validate' ); // Specific: Zend_Form_Element: $element ->addPrefixPath( 'My_Validate' , 'My/Validate' , 'validate' );
  • 152. Retrieving the class <?php // returns 'My_Validate_IsFoo' $class = $loader ->load( 'IsFoo' );
  • 153.
    • Create a plugin with the same “short” name as an existing plugin. E.g., to override Zend_Validate_IsInt, create a class My_Validate_IsInt.
    • 154. Register the new prefix-path with the loader. E.g., $loader->addPrefixPath('My_Validate', 'My/Validate');
    • 155. The new class will now be used.
    Override existing plugins
  • 156. Controllers or, “Route this!”
  • 157. How it all works
  • 158.  
  • 159.
    • Receive request
    • 160. Route request
    • 161. Dispatch loop
      • Pre-dispatch
      • 162. Dispatch controller action
      • 163. Post-dispatch
    • Send response
    Or, more simply:
  • 164.
    • Zend_Controller_Front
    • 165. Zend_Controller_Plugin
    • 166. Zend_Controller_Router
    • 167. Zend_Controller_Dispatcher
    • 168. Zend_Controller_Action_Helper
    • 169. Zend_Controller_Action
    Components
  • 170.
    • Handles entire request life-cycle
    • 171. Executes all other components
    • 172. Singleton: Zend_Controller_Front::getInstance()
    Front Controller
  • 173.
    • Bookend each major responsibility:
      • Routing
      • 174. Dispatch loop
      • 175. Dispatch
    • Used for site-wide actions
      • Layouts
      • 176. Error handling
    • Not handled via PluginLoader (yet!)
    Plugins
  • 177.
    • Default routing: /controller/action[/key/value/k/v]
    • 178. Can register alternate routes:
      • Static routes
      • 179. “ Normal” routes /with/:placeholder/:segments
      • 180. “ Regex” routes for fine-grained control
    • First matched route wins
    • 181. LIFO stack
    Routing
  • 182.
    • Resolves controller to a class
    • 183. Instantiates controller
    • 184. Executes requested action
    Dispatching
  • 185.
    • Obviate the need to create custom base controller classes
    • 186. Re-usable functionality that requires integration with or access from action controllers
    • 187. Have pre-/postDispatch hooks, allowing for task automation
    • 188. Zend_Controller_Action_HelperBroker: Combination static registry and service finder (uses PluginLoader internally)
    Action Helpers
  • 189. Action Helpers Registering prefix paths <?php // Register prefix path My_Foo => My/Foo/ Zend_Controller_Action_HelperBroker::addPrefix( 'My_Foo' ); // Register prefix path My_Foo => some/foo/ Zend_Controller_Action_HelperBroker::addPath( 'some/foo/' , 'My_Foo' );
  • 190. Action Helpers Registering helpers <?php // Register concrete instance Zend_Controller_Action_HelperBroker::addHelper( $helperInstance ); // Implicitly register, by using plugin loader $helper = Zend_Controller_Action_HelperBroker::getStaticHelper( 'ShortName' );
  • 191. Action Helpers Creating helpers <?php class My_Helper_InjectUser extends Zend_Controller_Action_Helper_Abstract { public function preDispatch() { $controller = $this ->getActionController(); if (!isset( $controller ->user)) { $controller ->user = $this ->getUser(); } } }
  • 192.
    • Named “ <name>Controller ”
    • 193. In modules, module name is used as prefix: “ Foo_BarController ”
    • 194. Can use MixedCase; in URL, words are dash-separated: “ FooBarController ” is referred to as “foo-bar”
    Action controllers
  • 195.
    • Named “ <name>Action() ”
    • 196. Can use MixedCase; in URL, words are dash- or dot-separated: “ fooBarAction ” is referred to as “foo-bar” or “foo.bar”
    Action methods
  • 197.
    • Public $view property is the view object
    • 198. Protected $_helper property refers to the helper broker: $this->_helper->foo retrieves “foo” action helper
    • 199. getRequest() and getResponse() provide access to request environment and response payload, respectively
    Features of controllers
  • 200.
    • Injects view object into controllers
    • 201. Renders views during p ostDispatch()
      • Standardizes view script resolution to <controller>/<action>.phtml
    • Simply an action helper; retrieve it using $this->_helper->viewRenderer
    ViewRenderer
  • 202. Using Zend_Tool to create controllers and actions # Create a &quot;foo&quot; controller: zf create controller foo # Create it in the &quot;bar&quot; module: zf create controller foo -m bar # Create a &quot;baz&quot; action method # (and view script) # in the foo controller: zf create action baz foo
  • 203. Tree after using tool quickstart |-- application | |-- controllers | | |-- ErrorController.php | | |-- IndexController.php | | `-- FooController.php | |-- modules | | |-- bar | | | |-- controllers | | | | ` -- FooController.php | `-- views | | |-- scripts | | | |-- error | | | |-- index | | | |-- foo | | | | ` -- bar.phtml
  • 204. FooController <?php class FooController extends Zend_Controller_Action { public function indexAction() { } public function bazAction() { } }
  • 205. Doing some work <?php class UserController extends Zend_Controller_Action { public function init() { $this ->service = new App_Service_User(); } public function listAction() { $this ->view->users = $this ->service->fetchAll(); } }
  • 206. Utilizing the request object <?php class UserController extends Zend_Controller_Action { // ... public function loginAction() { $result = $this ->service->login( $this ->getRequest()->getPost()); if ( $result instanceof App_Model_User) { $this ->redirect( '/' ); } $this ->view->loginForm = $this ->service->getLoginForm(); } }
  • 207. Views or, “Pics, or it didn't happen!”
  • 208.
    • PHP as a template language
    • 209. LIFO stack of view script paths used to resolve script path to render
    • 210. Helper plugins for extending view functionality
      • Helpers called as if they were view object methods
      • 211. Resolve to a class and method
      • 212. Override helpers locally if desired
    Zend_View
  • 213.
    • View variables are registered as object properties
    • 214. View scripts operate in the view object's variable scope; “$this” in a view script is the view object
    • 215. Use PHP shorthand/alternate notations for readability
    Zend_View
  • 216. Example view script <h2>Registered users</h2> <ul> <?php foreach ( $this ->users as $user ): ?> <li><?php echo $this ->escape( $user ->username) ?></li> <?php endforeach ?> </ul>
  • 217. Common helpers
    • Json
    • 218. Navigation
    • 219. PaginationControl
    • 220. ServerUrl
    • 221. Translate
    • 222. Url
    • Cycle
    • 223. HtmlFlash
    • 224. HtmlList
    • 225. HtmlObject
    • 226. HtmlPage
    • 227. HtmlQuicktime
  • 228. Using helpers <?php $this ->doctype( 'XHTML1_STRICT' ); $this ->headTitle( 'Example Helpers' ); ?> <h2><?php echo $this ->translate( 'headers' ) ?></h2> <?php echo $this ->partial( 'content.phtml' ) ?>
  • 229.
    • Aggregate content
    • 230. Rendering the helper concatenates aggregated content
    • 231. Typically used to aggregate information for the layout
    “ Placeholder” helpers
  • 232. Placeholder example <?php $this ->headTitle( 'Foo' ); $this ->headTitle( 'Bar' ); $this ->headTitle()->setSeparator( ' - ' ); echo $this ->headTitle(); // &quot;Foo - Bar&quot;
  • 233. Placeholder helpers
    • InlineScript
    • 234. Layout
    • 235. Partial
    • 236. PartialLoop
    • 237. Placeholder
    • Doctype
    • 238. HeadLink
    • 239. HeadMeta
    • 240. HeadScript
    • 241. HeadStyle
    • 242. HeadTitle
  • 243. Rendering Forms
  • 244. First, a word on Decorators
  • 245.
    • A Decorator typically wraps the functionality of an existing object, and selectively modifies its behavior
    • 246. Alternately, a decorator may be used to create a representation of an object
    • 247. In Zend_Form, decorators pull metadata from forms and elements to create representations. Many decorators proxy to view helpers !
  • 248. The simplest decorator <?php class My_Decorator_SimpleInput extends Zend_Form_Decorator_Abstract { protected $_format = '<label for=&quot;%s&quot;>%s</label> <input id=&quot;%s&quot; name=&quot;%s&quot; type=&quot;text&quot; value=&quot;%s&quot;/>' ; public function render( $content ) { $element = $this ->getElement(); $name = htmlentities ( $element ->getFullyQualifiedName()); $label = htmlentities ( $element ->getLabel()); $id = htmlentities ( $element ->getId()); $value = htmlentities ( $element ->getValue()); $markup = sprintf ( $this ->_format, $name , $label , $id , $name , $value ); return $markup ; } }
  • 249. … and the output <label for = &quot;bar[foo]&quot; > Foo </label> <input id = &quot;bar-foo&quot; name = &quot;bar[foo]&quot; type = &quot;text&quot; value = &quot;test&quot; />
  • 250.
    • You can layer decorators to aggregate output from multiple decorators
    • 251. Decorators are processed in FIFO order
    • 252. Generated content is passed to the next decorator as input
    • 253. Typically, append, prepend, or replace content
  • 254. Consider two decorators … <?php class My_Decorator_Input extends Zend_Form_Decorator_Abstract { } <?php class My_Decorator_Label extends Zend_Form_Decorator_Abstract { } Layer them: <?php $element ->addPrefixPath( 'My_Decorator' , 'My/Decorator' , 'decorator' ); $element ->setDecorators( array ( 'Input' , 'Label' , ));
  • 255. Typically, then, render the form: <?php echo $this ->form ?>
  • 256.
    • Decorators are plugins
    • 257. Can be rendered individually
      • Retrieve decorator and render
      • 258. Use overloading to render individual decorators
  • 259. Rendering individual decorators <?php $element = $this ->form->foo; echo $element ->getDecorator( 'label' ) ->render( '' ); // or: echo $element ->renderLabel();
  • 260.
    • Either pass the form to the view, or pull it from the model within the view
    • 261. Configure decorators in the view, if customizations are needed (view logic)
    • 262. For truly complex markup, render individual decorators within markup
    Recommendations
  • 263. Form-related view helpers
    • FormPassword
    • 264. Form
    • 265. FormRadio
    • 266. FormReset
    • 267. FormSelect
    • 268. FormSubmit
    • 269. FormTextarea
    • 270. FormText
    • 271. Fieldset
    • FormButton
    • 272. FormCheckbox
    • 273. FormErrors
    • 274. FormFile
    • 275. FormHidden
    • 276. FormImage
    • 277. FormLabel
    • 278. FormMultiCheckbox
    • 279. FormNote
  • 280. Layouts
  • 281.
    • Zend_Layout implements the Composite View and Two Step View design patterns
    • 282. The internal implementation utilizes front controller plugins, action helpers , and view helpers
    • 283. Layout scripts are simply view scripts
  • 284. Sample layout script <?php echo $this ->doctype() ?> <html xmlns= &quot;http://www.w3.org/1999/xhtml&quot; > <head> <?php echo $this ->headMeta() ?> <?php echo $this ->headTitle() ?> <?php echo $this ->headLink() ?> <?php echo $this ->headScript() ?> </head> <body> <?php echo $this ->layout()->content ?> </body> </html>
  • 285.
    • $this->_helper->viewRenderer->setNoRender(true);
    • 286. $this->_helper->layout->disableLayout();
    Disabling views and layouts
  • 287.
    • View scripts should be concise
    • 288. Hint to the layout script from your view scripts (not your controllers!)
    • 289. Layouts should use placeholders as much as possible
    • 290. Setup common layout elements in a bootstrap resource
    Recommendations
  • 291. Bootstrapping placeholders <?php class Bootstrap extends Zend_Application_Bootstrap_Bootstrap { protected function _initLayoutPlaceholders() { $this ->bootstrap( 'view' ); $view = $this ->getResource( 'view' ); $view ->doctype( 'XHTML1_STRICT' ); $view ->headTitle() ->setSeparator( ' - ' ) ->append( 'My site' ); } }
  • 292. Service Endpoints or, “Got API?”
  • 293.
    • All server components implement PHP's SoapServer API
    • 294. All public methods of attached classes are exposed
    • 295. Reflection on docblock annotations provides parameter and return value types to the service. You must provide @param for each parameter, and @return always!
  • 296. Example server <?php define ( 'APPLICATION_PATH' , dirname ( dirname (__FILE__)) . '/application' ) require_once 'Zend/Application.php' ; $app = new Zend_Application( getenv ( 'APPLICATION_ENV' ), APPLICATION_PATH . '/configs/xmlrpc.ini' ); $app ->bootstrap(); $server = new Zend_XmlRpc_Server(); $server ->setClass( 'App_Service_User' ); $response = $server ->handle(); echo $response ;
  • 297.
    • Create bootstrap classes specific to your servers
    • 298. Re-use application configuration, when possible
    • 299. Use namespaces when the service protocol allows (XML-RPC, JSON-RPC)
    • 300. Write decorators for your service layer so as not to expose methods related to implementation
    Recommendations
  • 301. Conclusions or, “The fat guy sings”
  • 302.
    • Spend your time on your domain models and service layers
      • Use Zend Framework components judiciously and to aid development
      • 303. Don't confine yourself to ZF
    • Expose your service layer via the MVC and server classes so as to allow applications to be built on top of your work
    Recommendations
  • 304. Questions? or, “Your turn!”
  • 305. Thank you. http://framework.zend.com/ http://twitter.com/weierophinney http://slideshare.net/weierophinney Feedback? http://joind.in/talk/view/603