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

Zend Framework Workshop, DPC09

on

  • 33,097 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,097
Views on SlideShare
31,894
Embed Views
1,203

Actions

Likes
88
Downloads
1,484
Comments
1

32 Embeds 1,203

http://mateusztymek.pl 465
http://www.kimbs.cn 285
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 Zend Framework Workshop, DPC09 Presentation Transcript

  • Zend Framework Workshop Matthew Weier O'Phinney Project Lead Zend Framework Dutch PHP Conference 11 April 2009 Amsterdam
  • About me:
    • ZF Contributor since January 2006
    • Assigned to the ZF team in July 2007
    • Promoted to Software Architect in April 2008
    • Project Lead since April 2009
    Photo © 2009, Chris Shiflett
  • What is Zend Framework?
  • Full Stack Framework?
  • Component Library?
  • Both.
  •  
  • Today's Roadmap
    • Quick Start
    • Models
    • Service Layers
      • Authentication and Authorization
      • Zend_Form
      • Plugin Loading
    • Controllers
    • Views and Layouts
    • Service Endpoints
  • Quick Start or, “How I learned to use and love Zend_Tool”
    • In bin/zf.sh or bin/zf.bat of your ZF install (choose based on your OS)
    • Place bin/ in your path, or create an alias on your path: alias zf=/path/to/bin/zf.sh
    • Or use pear.zfcampus.org PEAR channel
    Locate the zf utility
  • Create the project # Unix: % zf.sh create project quickstart # DOS/Windows: C:> zf.bat create project quickstart
  • 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 .
  • 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>
  • Add a hosts entry 127.0.0.1 quickstart
  • Fire up your browser!
  • Looking under the hood
  • 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
  • The bootstrap <?php class Bootstrap extends Zend_Application_Bootstrap_Bootstrap { }
  • 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
  • Index (default) controller <?php class IndexController extends Zend_Controller_Action { public function init() { /* Initialize action controller here */ } public function indexAction() { // action body } }
  • 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; } }
  • 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>
  • 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 ?>
  • .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]
  • 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(), )));
  • 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();
  • Models or, “Classes are an OOP developer's play-dough”
  • First, there is no Zend_Model. Stop asking.
  • What you shouldn't do
    • Never tie your models directly to data access
      • Makes testing hard
      • Makes refactoring later to use caching or an SOA hard
      • Makes restructuring your database problematic (your code could break)
  • Models are just classes. Create classes.
  • Where? quickstart |-- application | |-- Bootstrap.php | |-- configs | | `-- application.ini | |-- controllers | |-- models | | |-- Team.php | | ` -- User.php | `-- views |-- library |-- public ` -- tests
  • Models usually have metadata or attributes; define them
  • Class properties <?php class App_Model_User { protected $_email ; protected $_firstname ; protected $_id ; protected $_lastname ; protected $_password ; protected $_role = 'pig' ; protected $_username ; }
  • Models usually have behavior surrounding the metadata; define accessors and mutators
  • 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 ... */ }
  • Models usually have behaviors ; define some methods
  • Methods <?php class App_Model_User { /* ... */ public function walk() { } public function sleep () { } }
  • Now, persist your models
  • 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' );
  • 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;
  • 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
  • Tangent: a bit about Zend_Db_Table and relational datasets
  • 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 ' ); }
  • 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' ), ); }
  • Zend_Db_Table Using Relations <?php $users = $team ->findDependentRowset( 'App_Model_DbTable_TeamUser' ); $team = $teamUser ->findParentRow();
  • Zend_Db_Table Using many-to-many relations <?php $users = $team ->findManyToManyRowset( 'App_Model_DbTable_User' , 'App_Model_DbTable_TeamUser' , );
  • … and now back to our story
  • 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 ; } }
  • 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 ; } }
    • Data !== Database
    • Your Data Access Object could access a service...
    Note on Data Access Objects
  • Data Access to a service: <?php class App_Model_Dao_User { public function fetchAll( $criteria ) { return $this ->getXmlRpcClient() ->fetch( $criteria ); } }
  • Of names and autoloading
  • Always use a vendor or project prefix
  • Inform the autoloader of library prefixes
  • 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; ; ...
  • Resources can be autoloaded, too
  • 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 ; } }
  • 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
  • Service Layers or, “Re-use this!”
  • Applications are like onions; they have layers. Photo © 2008, Mike Chaput-Branson
  • The Service Layer provides application logic on top of your models
  • Service layer in perspective Data Access Objects and Data store(s) Data Mappers Domain Models Service Layer
    • Resource marshalling and dependency injection
    • Application specific logic:
      • Authentication and Authorization
      • Input filtering/data validation
      • Search indexing
      • etc.
    What's found in Service Layers?
  • Authentication: Zend_Auth
  • Authentication is the act of verifying somebody is who they say they are
    • Authentication adapters perform the work of authentication
    • Zend_Auth handles authentication persistence
  • Authentication adapter <?php class App_Model_AuthAdapter implements Zend_Auth_Adapter_Interface { protected $_user ; public function __construct(App_Model_User $user ) { $this ->_user = $user ; } // ... }
  • 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 ; }
  • 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 ) { } }
  • “ 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; } }
  • Retrieving an authenticated user $auth = Zend_Auth::getInstance(); if ( $auth ->hasIdentity()) { $user = new App_Model_User( $auth ->getIdentity() ); }
  • Authorization: Zend_Acl
  • Authorization is the act of determining if somebody has permissions to perform an action on a given resource
    • Roles – what requests the action
    • Resources – what is being acted upon
    • Rights – the privileges a role has for a given resource
    The three “R”s:
    • Implement Zend_Acl_Role_Interface
      • One method: getRoleId()
    • Can simply use Zend_Acl_Role, but this doesn't tie directly to your models
    Roles
  • 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; } }
    • Implement Zend_Acl_Resource_Interface
      • One method: getResourceId()
    • Can simply use Zend_Acl_Resource, but this doesn't tie directly to your models
    Resources
  • The “sprint” model Making it an ACL resource <?php class Scrum_Model_Sprint implements Zend_Acl_Resource_Interface { // ... public function getResourceId() { return 'sprint' ; } }
    • Zend_Acl denies all access by default
    • You must whitelist role access to resources
    • You can also blacklist , though this is less common or necessary
    Rights
  • 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' )); } }
    • Remember the three “R”s; check that:
      • The Role
      • acting on the resource
      • has rights to perform the action
    • Use the isAllowed() method
    Checking ACLs
  • 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(); } // ... } }
  • Data filtering and Validation: Zend_Form, Zend_Filter, and Zend_Validate
    • Acts as a general purpose normalization and validation filter
    • Form decorators make it trivial to create the markup needed to capture data
    • Encapsulates i18n/l10n concerns for validation error reporting
    Why Zend_Form?
    • Within Zend_Form, provides validation chains
    • Provides validation error reporting
    • Marking an element as “required” prepends a “NotEmpty” validator to the validation chain
    • Can be used standalone
    Zend_Validate
  • 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,}$/' ) );
  • 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,}$/' ) ), ), ));
    • Within Zend_Form, provides normalization chains
    • Normalizes input prior to validation, or on requesting the element value
    • Can be used standalone
    Zend_Filter
  • 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' , );
  • Zend_Filter Adding filters at element creation <?php $form ->addElement( 'text' , 'username' , array ( 'filters' => array ( 'StringToLower' , ) ));
    • Zend_Form:
      • Aggregates elements and groups (display groups and sub forms)
      • Provides full and partial validation of the elements
    • Elements:
      • Aggregate metadata
      • Contain filter and validation chains
    Zend_Form
    • Display Groups:
      • Aggregate elements purely for display purposes
    • Sub forms:
      • Aggregate elements related semantically
      • Provides namespacing of elements via array notation
    Zend_Form
  • 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,}$/' )), ), )); // ... } }
  • 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, )); } }
  • Zend_Form Variant: Dojo-aware form <?php class App_Form_User extends Zend_Dojo_Form { public function init() { $this ->addElement( 'TextBox' , 'username' , array ( // ...
  • Using forms as validators in your Service Layer
  • 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' ]; } }
  • 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(); } }
  • Interlude: Plugin architectures or, “Override anything!”
    • Aggregates prefix => path pairs: Zend_Validate_ => Zend/Validate/
    • Acts as a LIFO stack
    • Specify class name minus prefix to load: $class = $loader->load('StringTrim'); // Zend_Filter_StringTrim
    Zend_Loader_PluginLoader
    • Zend_Application_Bootstrap
    • Zend_Form
    • Zend_View
    • Zend_Amf_Server
    • Zend_Paginator
    • Zend_Controller_Action_HelperBroker
    • More...
    Plugin-aware components
    • Use short names instead of Really_Long_Underscored_Names
    • Define plugins via configuration options instead of in your code
    • Drop overrides into your filesystem, and get behavior changes
    Benefits of plugins
  • 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; } }
  • Registering the prefix-path <?php // General: $loader ->addPrefixPath( 'My_Validate' , 'My/Validate' ); // Specific: Zend_Form_Element: $element ->addPrefixPath( 'My_Validate' , 'My/Validate' , 'validate' );
  • Retrieving the class <?php // returns 'My_Validate_IsFoo' $class = $loader ->load( 'IsFoo' );
    • 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.
    • Register the new prefix-path with the loader. E.g., $loader->addPrefixPath('My_Validate', 'My/Validate');
    • The new class will now be used.
    Override existing plugins
  • Controllers or, “Route this!”
  • How it all works
  •  
    • Receive request
    • Route request
    • Dispatch loop
      • Pre-dispatch
      • Dispatch controller action
      • Post-dispatch
    • Send response
    Or, more simply:
    • Zend_Controller_Front
    • Zend_Controller_Plugin
    • Zend_Controller_Router
    • Zend_Controller_Dispatcher
    • Zend_Controller_Action_Helper
    • Zend_Controller_Action
    Components
    • Handles entire request life-cycle
    • Executes all other components
    • Singleton: Zend_Controller_Front::getInstance()
    Front Controller
    • Bookend each major responsibility:
      • Routing
      • Dispatch loop
      • Dispatch
    • Used for site-wide actions
      • Layouts
      • Error handling
    • Not handled via PluginLoader (yet!)
    Plugins
    • Default routing: /controller/action[/key/value/k/v]
    • Can register alternate routes:
      • Static routes
      • “ Normal” routes /with/:placeholder/:segments
      • “ Regex” routes for fine-grained control
    • First matched route wins
    • LIFO stack
    Routing
    • Resolves controller to a class
    • Instantiates controller
    • Executes requested action
    Dispatching
    • Obviate the need to create custom base controller classes
    • Re-usable functionality that requires integration with or access from action controllers
    • Have pre-/postDispatch hooks, allowing for task automation
    • Zend_Controller_Action_HelperBroker: Combination static registry and service finder (uses PluginLoader internally)
    Action Helpers
  • 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' );
  • 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' );
  • 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(); } } }
    • Named “ <name>Controller ”
    • In modules, module name is used as prefix: “ Foo_BarController ”
    • Can use MixedCase; in URL, words are dash-separated: “ FooBarController ” is referred to as “foo-bar”
    Action controllers
    • Named “ <name>Action() ”
    • Can use MixedCase; in URL, words are dash- or dot-separated: “ fooBarAction ” is referred to as “foo-bar” or “foo.bar”
    Action methods
    • Public $view property is the view object
    • Protected $_helper property refers to the helper broker: $this->_helper->foo retrieves “foo” action helper
    • getRequest() and getResponse() provide access to request environment and response payload, respectively
    Features of controllers
    • Injects view object into controllers
    • Renders views during p ostDispatch()
      • Standardizes view script resolution to <controller>/<action>.phtml
    • Simply an action helper; retrieve it using $this->_helper->viewRenderer
    ViewRenderer
  • 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
  • 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
  • FooController <?php class FooController extends Zend_Controller_Action { public function indexAction() { } public function bazAction() { } }
  • 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(); } }
  • 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(); } }
  • Views or, “Pics, or it didn't happen!”
    • PHP as a template language
    • LIFO stack of view script paths used to resolve script path to render
    • Helper plugins for extending view functionality
      • Helpers called as if they were view object methods
      • Resolve to a class and method
      • Override helpers locally if desired
    Zend_View
    • View variables are registered as object properties
    • View scripts operate in the view object's variable scope; “$this” in a view script is the view object
    • Use PHP shorthand/alternate notations for readability
    Zend_View
  • 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>
  • Common helpers
    • Json
    • Navigation
    • PaginationControl
    • ServerUrl
    • Translate
    • Url
    • Cycle
    • HtmlFlash
    • HtmlList
    • HtmlObject
    • HtmlPage
    • HtmlQuicktime
  • Using helpers <?php $this ->doctype( 'XHTML1_STRICT' ); $this ->headTitle( 'Example Helpers' ); ?> <h2><?php echo $this ->translate( 'headers' ) ?></h2> <?php echo $this ->partial( 'content.phtml' ) ?>
    • Aggregate content
    • Rendering the helper concatenates aggregated content
    • Typically used to aggregate information for the layout
    “ Placeholder” helpers
  • Placeholder example <?php $this ->headTitle( 'Foo' ); $this ->headTitle( 'Bar' ); $this ->headTitle()->setSeparator( ' - ' ); echo $this ->headTitle(); // &quot;Foo - Bar&quot;
  • Placeholder helpers
    • InlineScript
    • Layout
    • Partial
    • PartialLoop
    • Placeholder
    • Doctype
    • HeadLink
    • HeadMeta
    • HeadScript
    • HeadStyle
    • HeadTitle
  • Rendering Forms
  • First, a word on Decorators
    • A Decorator typically wraps the functionality of an existing object, and selectively modifies its behavior
    • Alternately, a decorator may be used to create a representation of an object
    • In Zend_Form, decorators pull metadata from forms and elements to create representations. Many decorators proxy to view helpers !
  • 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 ; } }
  • … 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; />
    • You can layer decorators to aggregate output from multiple decorators
    • Decorators are processed in FIFO order
    • Generated content is passed to the next decorator as input
    • Typically, append, prepend, or replace content
  • 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' , ));
  • Typically, then, render the form: <?php echo $this ->form ?>
    • Decorators are plugins
    • Can be rendered individually
      • Retrieve decorator and render
      • Use overloading to render individual decorators
  • Rendering individual decorators <?php $element = $this ->form->foo; echo $element ->getDecorator( 'label' ) ->render( '' ); // or: echo $element ->renderLabel();
    • Either pass the form to the view, or pull it from the model within the view
    • Configure decorators in the view, if customizations are needed (view logic)
    • For truly complex markup, render individual decorators within markup
    Recommendations
  • Form-related view helpers
    • FormPassword
    • Form
    • FormRadio
    • FormReset
    • FormSelect
    • FormSubmit
    • FormTextarea
    • FormText
    • Fieldset
    • FormButton
    • FormCheckbox
    • FormErrors
    • FormFile
    • FormHidden
    • FormImage
    • FormLabel
    • FormMultiCheckbox
    • FormNote
  • Layouts
    • Zend_Layout implements the Composite View and Two Step View design patterns
    • The internal implementation utilizes front controller plugins, action helpers , and view helpers
    • Layout scripts are simply view scripts
  • 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>
    • $this->_helper->viewRenderer->setNoRender(true);
    • $this->_helper->layout->disableLayout();
    Disabling views and layouts
    • View scripts should be concise
    • Hint to the layout script from your view scripts (not your controllers!)
    • Layouts should use placeholders as much as possible
    • Setup common layout elements in a bootstrap resource
    Recommendations
  • 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' ); } }
  • Service Endpoints or, “Got API?”
    • All server components implement PHP's SoapServer API
    • All public methods of attached classes are exposed
    • Reflection on docblock annotations provides parameter and return value types to the service. You must provide @param for each parameter, and @return always!
  • 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 ;
    • Create bootstrap classes specific to your servers
    • Re-use application configuration, when possible
    • Use namespaces when the service protocol allows (XML-RPC, JSON-RPC)
    • Write decorators for your service layer so as not to expose methods related to implementation
    Recommendations
  • Conclusions or, “The fat guy sings”
    • Spend your time on your domain models and service layers
      • Use Zend Framework components judiciously and to aid development
      • 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
  • Questions? or, “Your turn!”
  • Thank you. http://framework.zend.com/ http://twitter.com/weierophinney http://slideshare.net/weierophinney Feedback? http://joind.in/talk/view/603