Zend Framework Workshop, DPC09

37,030 views
35,237 views

Published on

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.

Published in: Technology
1 Comment
88 Likes
Statistics
Notes
  • Great Frame work 2011 waiting

    http://www.touchyard.com
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
No Downloads
Views
Total views
37,030
On SlideShare
0
From Embeds
0
Number of Embeds
1,252
Actions
Shares
0
Downloads
1,500
Comments
1
Likes
88
Embeds 0
No embeds

No notes for slide
  • Zend Framework Workshop, DPC09

    1. Zend Framework Workshop Matthew Weier O'Phinney Project Lead Zend Framework Dutch PHP Conference 11 April 2009 Amsterdam
    2. About me: <ul><li>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 </li></ul>Photo © 2009, Chris Shiflett
    6. What is Zend Framework?
    7. Full Stack Framework?
    8. Component Library?
    9. Both.
    10.  
    11. Today's Roadmap
    12. <ul><li>Quick Start
    13. Models
    14. Service Layers </li><ul><li>Authentication and Authorization
    15. Zend_Form
    16. Plugin Loading </li></ul><li>Controllers
    17. Views and Layouts
    18. Service Endpoints </li></ul>
    19. Quick Start or, “How I learned to use and love Zend_Tool”
    20. <ul><li>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 </li></ul>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. <ul><li>Never tie your models directly to data access </li><ul><li>Makes testing hard
    43. Makes refactoring later to use caching or an SOA hard
    44. Makes restructuring your database problematic (your code could break) </li></ul></ul>
    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. <ul><li>Data !== Database
    66. Your Data Access Object could access a service... </li></ul>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. <ul><li>Resource marshalling and dependency injection
    80. Application specific logic: </li><ul><li>Authentication and Authorization
    81. Input filtering/data validation
    82. Search indexing
    83. etc. </li></ul></ul>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. <ul><li>Authentication adapters perform the work of authentication
    87. Zend_Auth handles authentication persistence </li></ul>
    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. <ul><li>Roles – what requests the action
    96. Resources – what is being acted upon
    97. Rights – the privileges a role has for a given resource </li></ul>The three “R”s:
    98. <ul><li>Implement Zend_Acl_Role_Interface </li><ul><li>One method: getRoleId() </li></ul><li>Can simply use Zend_Acl_Role, but this doesn't tie directly to your models </li></ul>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. <ul><li>Implement Zend_Acl_Resource_Interface </li><ul><li>One method: getResourceId() </li></ul><li>Can simply use Zend_Acl_Resource, but this doesn't tie directly to your models </li></ul>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. <ul><li>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 </li></ul>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. <ul><li>Remember the three “R”s; check that: </li><ul><li>The Role
    107. acting on the resource
    108. has rights to perform the action </li></ul><li>Use the isAllowed() method </li></ul>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. <ul><li>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 </li></ul>Why Zend_Form?
    114. <ul><li>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 </li></ul>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. <ul><li>Within Zend_Form, provides normalization chains
    121. Normalizes input prior to validation, or on requesting the element value
    122. Can be used standalone </li></ul>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. <ul><li>Zend_Form: </li><ul><li>Aggregates elements and groups (display groups and sub forms)
    126. Provides full and partial validation of the elements </li></ul><li>Elements: </li><ul><li>Aggregate metadata
    127. Contain filter and validation chains </li></ul></ul>Zend_Form
    128. <ul><li>Display Groups: </li><ul><li>Aggregate elements purely for display purposes </li></ul><li>Sub forms: </li><ul><li>Aggregate elements related semantically
    129. Provides namespacing of elements via array notation </li></ul></ul>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. <ul><li>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 </li></ul>Zend_Loader_PluginLoader
    140. <ul><li>Zend_Application_Bootstrap
    141. Zend_Form
    142. Zend_View
    143. Zend_Amf_Server
    144. Zend_Paginator
    145. Zend_Controller_Action_HelperBroker
    146. More... </li></ul>Plugin-aware components
    147. <ul><li>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 </li></ul>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. <ul><li>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. </li></ul>Override existing plugins
    156. Controllers or, “Route this!”
    157. How it all works
    158.  
    159. <ul><li>Receive request
    160. Route request
    161. Dispatch loop </li><ul><li>Pre-dispatch
    162. Dispatch controller action
    163. Post-dispatch </li></ul><li>Send response </li></ul>Or, more simply:
    164. <ul><li>Zend_Controller_Front
    165. Zend_Controller_Plugin
    166. Zend_Controller_Router
    167. Zend_Controller_Dispatcher
    168. Zend_Controller_Action_Helper
    169. Zend_Controller_Action </li></ul>Components
    170. <ul><li>Handles entire request life-cycle
    171. Executes all other components
    172. Singleton: Zend_Controller_Front::getInstance() </li></ul>Front Controller
    173. <ul><li>Bookend each major responsibility: </li><ul><li>Routing
    174. Dispatch loop
    175. Dispatch </li></ul><li>Used for site-wide actions </li><ul><li>Layouts
    176. Error handling </li></ul><li>Not handled via PluginLoader (yet!) </li></ul>Plugins
    177. <ul><li>Default routing: /controller/action[/key/value/k/v]
    178. Can register alternate routes: </li><ul><li>Static routes
    179. “ Normal” routes /with/:placeholder/:segments
    180. “ Regex” routes for fine-grained control </li></ul><li>First matched route wins
    181. LIFO stack </li></ul>Routing
    182. <ul><li>Resolves controller to a class
    183. Instantiates controller
    184. Executes requested action </li></ul>Dispatching
    185. <ul><li>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) </li></ul>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. <ul><li>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” </li></ul>Action controllers
    195. <ul><li>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” </li></ul>Action methods
    197. <ul><li>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 </li></ul>Features of controllers
    200. <ul><li>Injects view object into controllers
    201. Renders views during p ostDispatch() </li><ul><li>Standardizes view script resolution to <controller>/<action>.phtml </li></ul><li>Simply an action helper; retrieve it using $this->_helper->viewRenderer </li></ul>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. <ul><li>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 </li><ul><li>Helpers called as if they were view object methods
    211. Resolve to a class and method
    212. Override helpers locally if desired </li></ul></ul>Zend_View
    213. <ul><li>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 </li></ul>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 <ul><li>Json
    218. Navigation
    219. PaginationControl
    220. ServerUrl
    221. Translate
    222. Url </li></ul><ul><li>Cycle
    223. HtmlFlash
    224. HtmlList
    225. HtmlObject
    226. HtmlPage
    227. HtmlQuicktime </li></ul>
    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. <ul><li>Aggregate content
    230. Rendering the helper concatenates aggregated content
    231. Typically used to aggregate information for the layout </li></ul>“ 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 <ul><li>InlineScript
    234. Layout
    235. Partial
    236. PartialLoop
    237. Placeholder </li></ul><ul><li>Doctype
    238. HeadLink
    239. HeadMeta
    240. HeadScript
    241. HeadStyle
    242. HeadTitle </li></ul>
    243. Rendering Forms
    244. First, a word on Decorators
    245. <ul><li>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 ! </li></ul>
    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. <ul><li>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 </li></ul>
    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. <ul><li>Decorators are plugins
    257. Can be rendered individually </li><ul><li>Retrieve decorator and render
    258. Use overloading to render individual decorators </li></ul></ul>
    259. Rendering individual decorators <?php $element = $this ->form->foo; echo $element ->getDecorator( 'label' ) ->render( '' ); // or: echo $element ->renderLabel();
    260. <ul><li>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 </li></ul>Recommendations
    263. Form-related view helpers <ul><li>FormPassword
    264. Form
    265. FormRadio
    266. FormReset
    267. FormSelect
    268. FormSubmit
    269. FormTextarea
    270. FormText
    271. Fieldset </li></ul><ul><li>FormButton
    272. FormCheckbox
    273. FormErrors
    274. FormFile
    275. FormHidden
    276. FormImage
    277. FormLabel
    278. FormMultiCheckbox
    279. FormNote </li></ul>
    280. Layouts
    281. <ul><li>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 </li></ul>
    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. <ul><li>$this->_helper->viewRenderer->setNoRender(true);
    286. $this->_helper->layout->disableLayout(); </li></ul>Disabling views and layouts
    287. <ul><li>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 </li></ul>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. <ul><li>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! </li></ul>
    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. <ul><li>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 </li></ul>Recommendations
    301. Conclusions or, “The fat guy sings”
    302. <ul><li>Spend your time on your domain models and service layers </li><ul><li>Use Zend Framework components judiciously and to aid development
    303. Don't confine yourself to ZF </li></ul><li>Expose your service layer via the MVC and server classes so as to allow applications to be built on top of your work </li></ul>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

    ×