Transcription
Building a ZendFramework applicationRob Allen
Rob Allen? PHP developer since 1999Wrote Zend ConfigTutorial at akrabat.comBook!TEK·X 2010Rob Allen http://akrabat.com
What weʼll cover Overview of Zend Framework Getting and installing it The basics of a ZF MVC application An application: todolist MVC application: DB access, layouts etcForms, caching, authentication Please ask questions as we go!TEK·X 2010Rob Allen http://akrabat.com
ZF overview
What is ZF? PHP5 component library MVC Components make a “framework” Open source - BSD license Documented Quality assured Certification Actively maintained by ZendTEK·X 2010Rob Allen http://akrabat.com
ZF Philosophy Use-at-will Simple usage 80% of the time Showcase current trends OO best practiceweb servicesdev best practice: unit testing, documentationfor 2.0: PHP 5.3 namespaces & closuresTEK·X 2010Rob Allen http://akrabat.com
TEK·X 2010Rob Allen http://akrabat.com
The MVC systemTEK·X 2010Rob Allen http://akrabat.com
Routing: 1//news/2008/11 route new Zend Controller Router Route('news/:year/:month',array('controller' 'news','action' 'archive'),);TEK·X 2010Rob Allen http://akrabat.com
Getting andinstalling ZF
Installation http://framework.zend.com/download/latestTEK·X 2010Rob Allen http://akrabat.com
Install Unzip or untar: tar -zxf ZendFramework-1.10.3-minimal.tar.gzunzip ZendFramework-1.10.3-minimal.zip /usr/local/include/zendframeworkc:\zendframework Store in a common placeTEK·X 2010Rob Allen http://akrabat.com
Add to include path# Apache: VirtualHost , Directory or .htaccess:php value include path ".:/usr/local/include/zendframework/library"# or PHP:set include path(implode(PATH SEPARATOR, array('.','c:/zendframework/library',get include path(),)));TEK·X 2010Rob Allen http://akrabat.com
Install zf utility bin/zf.sh or bin/zf.bat Place on path (or create alias): aliaszf /usr/local/include/zendframework/bin/zf.shTEK·X 2010Rob Allen http://akrabat.com
or use PEAR#pear channel-discover pear.zfcampus.orgAdding channel "pear.zfcampus.org" succeededDiscovery of channel "pear.zfcampus.org" succeeded#pear install zfcampus/downloading ZF-1.10.2.tgz .Starting to download ZF-1.10.2.tgz (3,520,994 bytes). done : 3,520,994 bytesinstall ok: channel://pear.zfcampus.org/ZF-1.10.2#which zf/usr/bin/zfTEK·X 2010Rob Allen http://akrabat.com
A Zend FrameworkMVC application
A to-do list application Create a list of todo items with optional notes and due dates Single tasks database table Main list shows open tasks Logged in user sees only their own tasksTEK·X 2010Rob Allen http://akrabat.com
SpecificationA todo item consists of:taskstring (required)notesstring (optional)due datedatetime (optional)completedboolean (required)identifierinteger (required - primary key)created byinteger (for use later)TEK·X 2010Rob Allen http://akrabat.com
Create the projectzf create project todolist# if ZF is not on your path:cd todolist/libraryln -s /usr/local/include/zendframework/library/Zend .# orcp -r /usr/local/include/zendframework/library/Zend .TEK·X 2010Rob Allen http://akrabat.com
Apache vhostvim /etc/apache2/extra/httpd-vhosts.conf# From todolist/docs/README.txt VirtualHost *:80 DocumentRoot "/www/todolist/public"ServerName todolist.localhost# This should be omitted in the production environmentSetEnv APPLICATION ENV development Directory "/www/todolist/public" Options Indexes MultiViews FollowSymLinksAllowOverride AllOrder allow,denyAllow from all /Directory /VirtualHost TEK·X 2010Rob Allen http://akrabat.com
hosts filevim /etc/hostsor notepad c:\windows\system32/drivers/etc/hosts127.0.0.1 todolist.localhostTEK·X 2010Rob Allen http://akrabat.com
Run it!TEK·X 2010Rob Allen http://akrabat.com
DirectoriesTEK·X 2010Rob Allen http://akrabat.com
DirectoriesTEK·X 2010Website filesRob Allen http://akrabat.com
DirectoriesTEK·X 2010BootstrappingRob Allen http://akrabat.com
DirectoriesTEK·X 2010MVCRob Allen http://akrabat.com
DirectoriesTEK·X 2010Unit testingRob Allen http://akrabat.com
n.iniTEK·X 2010Rob Allen http://akrabat.com
.htaccessRewriteEngine OnRewriteCond %{REQUEST FILENAME} -s [OR]RewriteCond %{REQUEST FILENAME} -l [OR]RewriteCond %{REQUEST FILENAME} -dRewriteRule .* - [NC,L]RewriteRule .* index.php [NC,L]TEK·X 2010Rob Allen http://akrabat.com
index.php (environment) ?php// Define path to application directorydefined('APPLICATION PATH') define('APPLICATION PATH',realpath(dirname( FILE ) . '/./application'));// Define application environmentdefined('APPLICATION ENV') define('APPLICATION ENV',(getenv('APPLICATION ENV') ? getenv('APPLICATION ENV') :'production'));// Ensure library/ is on include pathset include path(implode(PATH SEPARATOR, array(realpath(APPLICATION PATH . '/./library'),get include path())));TEK·X 2010Rob Allen http://akrabat.com
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();TEK·X 2010Rob Allen http://akrabat.com
Bootstrap.phpclass Bootstrap extends Zend Application Bootstrap Bootstrap{}TEK·X 2010Rob Allen http://akrabat.com
play startup errors 0phpSettings.display errors 0includePaths.library APPLICATION PATH "/./library"bootstrap.path APPLICATION PATH "/Bootstrap.php"bootstrap.class "Bootstrap"appnamespace irectory APPLICATION PATH playExceptions 0[staging : production][development : production]phpSettings.display startup errors 1phpSettings.display errors s 1TEK·X 2010Rob Allen http://akrabat.com
Action controllers Contain your application workflow Map requests to models and views. Class name: NewsController Action methods: listAction() Home: IndexController::indexAction() ErrorController::errorAction() for 404and 500sTEK·X 2010Rob Allen http://akrabat.com
Views Controller integration using ViewRenderer view property in controller: Folder: application/views/scripts/ Script naming: In view scripts:NewsController::detailAction() news/detail.phtml ? this- escape( this- item- title); ? TEK·X 2010Rob Allen http://akrabat.com
Model There is no Zend Model! Database adapters: Zend Db Table row gateway: Zend Db Table Web services: Zend Service Xxx, Zend Soap ORMs: Doctrine, Propel NoSQL: CouchDB, MongoDB, CassandraTEK·X 2010Rob Allen http://akrabat.com
Letʼs build an app
Database tableCREATE TABLE IF NOT EXISTS tasks (id int NOT NULL AUTO INCREMENT,title varchar(200) NOT NULL,notes text,due date datetime,completed tinyint(1) NOT NULL DEFAULT '0',date created datetime NOT NULL,PRIMARY KEY (id)) ENGINE InnoDB DEFAULT CHARSET utf8;INSERT INTO tasks (title, due date, date created)VALUES ('Task 1', '2010-05-30', NOW());INSERT INTO tasks (title, due date, date created) VALUES('Task 2', '2010-05-010', NOW());TEK·X 2010Rob Allen http://akrabat.com
Zend Db Table modelTEK·X 2010Rob Allen http://akrabat.com
Configure db access[production].resources.db.adapter "Pdo Mysql"resources.db.params.charset "utf8"resources.db.params.host "localhost"resources.db.params.username "todolist"resources.db.params.password "password"resources.db.params.dbname "todolist"resources.db.params.profiler.enabled false[development : production].TEK·X 2010Rob Allen http://akrabat.com
Zend Db Table model zf create db-table Tasks tasksCreating a DbTable at Updating project profile '/www/todolist/.zfproject.xml'class Application Model DbTable Tasksextends Zend Db Table Abstract{protected name 'tasks';protected rowClass 'Application Model DbTable Task';}TEK·X 2010Rob Allen http://akrabat.com
Row objectclass Application Model DbTable Taskextends Zend Db Table Row Abstract{protected function insert(){ this- date created date('Y-m-d H:i:s');}}TEK·X 2010Rob Allen http://akrabat.com
Model access method// Application Model DbTable Taskspublic function fetchOutstanding(){ select this- select(); select- where('completed 0'); select- order(array('date created DESC', 'id ASC'));return this- fetchAll( select);}TEK·X 2010Rob Allen http://akrabat.com
Index controllerclass IndexController extends Zend Controller Action{public function indexAction(){ this- view- title 'Outstanding tasks'; tasksGateway new Application Model DbTable Tasks(); this- view- tasks tasksGateway- fetchOutstanding();}}TEK·X 2010Rob Allen http://akrabat.com
indexAction() view script h1 ?php echo this- escape( this- title); ? /h1 table tr th Task /th th Due /th /tr ?php foreach ( this- tasks as task) : ? tr td ?php echo this- escape( task- title);? /td td ?php echo task- due date ?date('d m Y', strtotime( task- due date)) : '-';? /td /tr ?php endforeach;? /table TEK·X 2010Rob Allen http://akrabat.com
Layouts: Composite viewTEK·X 2010Rob Allen http://akrabat.com
Enable layout zf enable layoutLayouts have been enabled, and a default layout created .phtmlA layout entry has been added to the application config file. ?php echo this- layout()- content; ? TEK·X 2010Rob Allen http://akrabat.com
Layout placeholders// controller action method html view- render('sidebar.phtml'); layout- sidebar html;// view script ?php echo this- layout()- sidebar; ? TEK·X 2010Rob Allen http://akrabat.com
Site-wide layout file ?php this- headMeta()- appendHttpEquiv('Content-Type', 'text/html;charset utf-8'); this- headTitle('Todo List')- setSeparator(' - ');echo this- doctype(); ? html xmlns "http://www.w3.org/1999/xhtml" head ?php echo this- headMeta(); ? ?php echo this- headTitle(); ? ?php echo this- headLink()- appendStylesheet( this- baseUrl().'/css/site.css'); ? /head body div id "content" ?php echo this- layout()- content; ? /div /body /html TEK·X 2010Rob Allen http://akrabat.com
TEK·X 2010Rob Allen http://akrabat.com
Forms
Zend Form architecture Forms: the centre piece Forms are made up of: Elements, Display groups, Sub-forms Each element is made up of: Filters, Validators, DecoratorsTEK·X 2010Rob Allen http://akrabat.com
Create a form zf create form taskCreating a form at /www/todolist/application/forms/Task.phpUpdating project profile '/www/todolist/.zfproject.xml'class Application Form Task extends Zend Form{public function init(){/* Form Elements & Other Definitions Here . */}}TEK·X 2010Rob Allen http://akrabat.com
Add an element title new Zend Form Element Text('title'); title- setRequired(true); title- setLabel('Title'); title- addFilters(array('StringTrim', 'StripTags')); title- addValidators(array(new Zend Validate StringLength(array('min' 5)))); title- addElement( email);TEK·X 2010Rob Allen http://akrabat.com
Submit button element new Zend Form Element Submit('submit-button'); element- setIgnore(true); element- setLabel('Add task'); this- addElement( element);TEK·X 2010Rob Allen http://akrabat.com
Action codepublic function addAction(){ request this- getRequest(); form new Application Form Task();if ( request- isPost()) {if ( form- isValid( request- getPost())) {// success formData form- getValues();}} this- view- form form;}TEK·X 2010Rob Allen http://akrabat.com
View script h1 Add task /h1 ?php this- form- setAction( this- url());echo this- form;? TEK·X 2010Rob Allen http://akrabat.com
Store to model// IndexController::addAction()if ( request- isPost()) {if ( form- isValid( request- getPost())) {// success formData form- getValues(); tasks new Application Model DbTable Tasks(); tasks- addTask( formData); this- helper- redirector('index');}}TEK·X 2010Rob Allen http://akrabat.com
Model methods// Application Model DbTable Taskspublic function addTask(array taskData){ data array('title' taskData['title'],'due date' taskData['due date']); this- insert( data);}public function updateTask( id, taskData){ data array('title' taskData['title'],'due date' taskData['due date']); this- update( data, 'id ' . (int) id);}TEK·X 2010Rob Allen http://akrabat.com
Custom form element// Application Form Task due new Application Form Element Date('due date'); due- setRequired(false); due- setLabel('Due'); this- addElement( due);TEK·X 2010Rob Allen http://akrabat.com
The date element (1)// application/form/element/Date.phpclass Application Form Element Dateextends Zend Form Element Xhtml{public helper 'formDate';public function init (){ this- addValidator(new Zend Validate Date());}TEK·X 2010Rob Allen http://akrabat.com
The date element (2)public function isValid ( value, context null){if (is array( value)) { value value['year'] . '-' . value['month'] . '-' . value['day'];}if( value '--') { value null;}return parent::isValid( value, context);}TEK·X 2010Rob Allen http://akrabat.com
Date view helperclass Zend View Helper FormDate extends Zend View Helper FormElement{public function formDate ( name, value null, attribs null){ day value['day']; dayMultiOptions array('' '');for ( i 1; i 31; i ) { key str pad( i, 2, '0', STR PAD LEFT); dayMultiOptions[ key] str pad( i, 2, '0', STR PAD LEFT);}return this- view- formSelect( name . '[day]', day, dayAttribs, dayMultiOptions) . ' ' . this- view- formSelect( name . '[month]', month, monthAttribs, monthMultiOptions) . ' ' . this- view- formSelect( name . '[year]',TEK·X 2010Rob Allen http://akrabat.com
Caching
PrincipleTEK·X 2010Rob Allen http://akrabat.com
Bootstrap initialisationclass Bootstrap extends Zend Application Bootstrap Bootstrap{function initCache(){ frontendOptions array('lifetime' '7200','automatic serialization' true); backendOptions array('cache dir' APPLICATION PATH . '/./var/cache'); cache Zend Cache::factory('Core', 'File', frontendOptions, backendOptions);return cache;}}TEK·X 2010Rob Allen http://akrabat.com
Current code// Application Model DbTable Taskspublic function fetchOutstanding(){ select this- select(); select- where('completed 0'); select- order(array('due date ASC', 'id ASC'));return this- fetchAll( select);}TEK·X 2010Rob Allen http://akrabat.com
Zend Cache in useUnique idpublic function fetchOutstanding(){ cacheId 'outstandingTasks'; cache this- getCache();Re-use rows cache- load( cacheId);existingif ( rows false) {code select this- select(); select- where('completed 0'); select- order(array('due date ASC', 'id DESC')); rows this- fetchAll( select); cache- save( rows, cacheId, array('tasks'));}return rows;Tag}Store to cacheTEK·X 2010Rob Allen http://akrabat.com
Zend Cache in use// Application Model DbTable Taskspublic function getCache(){if (! this- cache) { fc Zend Controller Front::getInstance(); cache fc- getParam('bootstrap')- getResource('cache'); this- cache cache;}return this- cache;}TEK·X 2010Rob Allen http://akrabat.com
Emptying Zend Cache// Application Model DbTable Tasksprotected function cleanCache(){ this- getCache()- clean(Zend Cache::CLEANING MODE MATCHING TAG,array('tasks'));}TEK·X 2010Rob Allen http://akrabat.com
Authentication
Zend Auth processTEK·X 2010Rob Allen http://akrabat.com
AuthController zf create controller AuthCreating a controller at er.phpCreating an index action method in controller AuthCreating a view script for the index action method at .phtmlCreating a controller test file at ntrollerTest.phpUpdating project profile '/www/todolist/.zfproject.xml' zf create form LoginCreating a form at /www/todolist/application/forms/Login.phpUpdating project profile '/www/todolist/.zfproject.xml'TEK·X 2010Rob Allen http://akrabat.com
Users tableCREATE TABLE IF NOT EXISTS users (id int NOT NULL AUTO INCREMENT,username varchar(50) NOT NULL,password varchar(50) NOT NULL,date created datetime NOT NULL,PRIMARY KEY (id)) ENGINE InnoDB DEFAULT CHARSET utf8;-- insert first userINSERT INTO users (username, password, date created) VALUES('admin', SHA1('vF%sdf&A!agDktpassword'), NOW());TEK·X 2010Rob Allen http://akrabat.com
application.iniauth.salt "vF%sdf&A!agDkt"TEK·X 2010Rob Allen http://akrabat.com
Reverse SHA1 lookupsSHA1('password') df&A!agDktpassword') e6ff3f23138c2975220f93b3ef7ffd159fb9171bTEK·X 2010Rob Allen http://akrabat.com
Login formclass Application Form Login extends Zend Form{public function init(){ this- addElement('text', 'username', array('filters' array('StringTrim', 'StringToLower'),'required' true,'label' 'Username:',)); this- addElement('password', 'password', array('filters' array('StringTrim'),'required' true,'label' 'Password:',));// There's also a submit buttonTEK·X 2010Rob Allen http://akrabat.com
AuthControllerclass AuthController extends Zend Controller Action{public function indexAction(){ form new Application Form Login(); request this- getRequest();if ( request- isPost()) {if ( form- isValid( request- getPost())) {if ( this- process( form- getValues())) {// Success Redirect to the home page this- helper- redirector('index', 'index');}}} this- view- form form;}TEK·X 2010Rob Allen http://akrabat.com
Login formTEK·X 2010Rob Allen http://akrabat.com
AuthControllerprotected function process( values){// Get our authentication adapter and check credentials adapter this- getAuthAdapter( values); auth Zend Auth::getInstance(); result auth- authenticate( adapter);if ( result- isValid()) { data adapter- getResultRowObject(); auth- getStorage()- write( data);return true;}return false;}TEK·X 2010Rob Allen http://akrabat.com
AuthControllerprotected function getAuthAdapter( formData) { dbAdapter Zend Db Table::getDefaultAdapter(); authAdapter new Zend Auth Adapter DbTable( dbAdapter); authAdapter- setTableName('users')- setIdentityColumn('username')- setCredentialColumn('password')- setCredentialTreatment('SHA1(?)'); fc Zend Controller Front::getInstance(); options fc- getParam('bootstrap')- getOptions(); password options['auth']['salt']. formData['password']; authAdapter- setIdentity( formData['username']); authAdapter- setCredential( password);return authAdapter;}TEK·X 2010Rob Allen http://akrabat.com
LoggedInAs view helperclass Zend View Helper LoggedInAs extends Zend View Helper Abstract{public function loggedInAs (){ auth Zend Auth::getInstance();if ( auth- hasIdentity()) { username auth- getIdentity()- username; url this- url(array('controller' 'auth','action' 'logout'), null, true);return 'Welcome '. username.' a href "'. url.'" Logout /a ';} url this- url(array('controller' 'auth', 'action' 'index'));return ' a href "'. url.'" Login /a ';}}TEK·X 2010Rob Allen http://akrabat.com
Layout.phtml div id "header" div id "logged-in-as" ?php echo this- loggedInAs(); ? /div /div TEK·X 2010Rob Allen http://akrabat.com
LoggedInAs in actionTEK·X 2010Rob Allen http://akrabat.com
Access control
ACL JargonRoleTEK·X 2010PrivilegeResourceRob Allen http://akrabat.com
ACL processTEK·X 2010Rob Allen http://akrabat.com
Front Controller plugin// application/plugins/Acl.phpclass Application Plugin Acl extends Zend Controller Plugin Abstract{public function dispatchLoopStartup(Zend Controller Request Abstract request){}}// Controller.plugins.acl Application Plugin AclTEK·X 2010Rob Allen http://akrabat.com
Acl pluginpublic function dispatchLoopStartup(Zend Controller Request Abstract request){ acl this- getAcl(); role this- getRole(); resource request- getControllerName(); privilege request- getActionName(); allowed acl- isAllowed( role, resource, privilege);if (! allowed) { controller 'auth'; action 'index'; redirector new Zend Controller Action Helper Redirector(); redirector- gotoSimpleAndExit( action, controller);}}TEK·X 2010Rob Allen http://akrabat.com
Setup Zend Acl (1)protected function getAcl(){if (null this- acl) { acl new Zend Acl();// Roles acl- addRole('guest'); acl- addRole('user', 'guest'); acl- addRole('admin', 'user');// Resources acl- add(new Zend Acl Resource('index')); acl- add(new Zend Acl Resource('auth')); acl- add(new Zend Acl Resource('error'));TEK·X 2010Rob Allen http://akrabat.com
Setup Zend Acl (2)RoleResourcePrivileges// Rules acl- deny(); acl- allow('user', 'index',array('index', 'add', 'edit', 'view')); acl- allow('admin', 'index', array('delete')); acl- allow('guest', 'auth', null); acl- allow('guest', 'error', null); this- acl acl;}return this- acl;}TEK·X 2010Rob Allen http://akrabat.com
Get current roleprotected function getRole(){ auth Zend Auth::getInstance();if ( auth- hasIdentity()) { identity auth- getIdentity(); role empty( identity- role) ? 'user': identity- role;} else { role 'guest';}return role;}TEK·X 2010Rob Allen http://akrabat.com
Zend Framework simplifiesdevelopment and maintenanceDevote your time to yourdomain models as they are key.
Questions?TEK·X 2010Rob Allen http://akrabat.com
Thank youfeedback: http://joind.in/1562email: rob@akrabat.comtwitter: @akrabatQR CodeTEK·X 2010Rob Allen http://akrabat.com
Overview of Zend Framework Getting and installing it The basics of a ZF MVC application An application: todolist MVC application: DB access, layouts etc Forms, caching, authentication Please ask questions as we go!