Zend Framework MVC driven ExtJS Thorsten Suckow-Homberg, K&K GmbH Sourc{ - SenchaDev Conference May 5-7, 2011 Croatia, Split
Who am I? ● Thorsten Suckow-Homberg ● Born 1976, Aachen, Germany ● Webdeveloper since 1999 ● Senior Software Developer for K&K GmbH, Aachen ● Focus on planning, architecture and deployment of web based software
Who am I? … oh, and UI programming, of course: Author of: ● conjoon – http://www.conjoon.org ● Ext.ux.Livegrid – http://www.ext-livegrid.com ● various other open source ExtJS extensions/ components ● Numerous bug reports, rants and proposals over at the sencha forums :) (MindPatterns)
This talk will show you... ● … how you should plan your directory layout in larger projects
This talk will show you... ● … how you should plan your directory layout in larger projects ● … what is application context - and how to use it to your advantage
This talk will show you... ● … how you should plan your directory layout in larger projects ● … what is application context - and how to use it to your advantage ● … how to use Ext.Direct with existing ZF backend code
This talk will show you... ● … how you should plan your directory layout in larger projects ● … what is application context - and how to use it to your advantage ● … how to use Ext.Direct with existing ZF backend code ● In short: ...why Zend Framework could become the framework of your choice when combining Ext and PHP
The Basics Directory Layout
The Basics – Directory Layout ● Top level should be a directory named after your project (obviously) ● ...containing three child directories:
The Basics – Directory Layout build-tools ● Real tools, like: ● yuicompressor ● phing ● ant
The Basics – Directory Layout build-tools ● Real tools, like: ● yuicompressor ● phing ● ant ● (XML-)build scripts ● code sanity ● tests ● deployment ● etc... in short: Continuous Integration[1]!
The Basics – Directory Layout vendor ● All the third party libs you're using in your code ● ExtJS ● ZendFramework ● etc.
The Basics – Directory Layout vendor ● All the third party libs you're using in your code ● ExtJS ● ZendFramework ● etc. Note: ● files from separate repository vendor branch will be merged into this directory ● Best case: developers will not touch the vendor directory ● Read [2] for more infos on how to use vendor branches
The Basics – Directory Layout src
The Basics – Directory Layout src ● Everything you're actually coding
The Basics – Directory Layout src ● Everything you're actually coding: ● overrides ● extensions ● backend code ● code that might refer to vendor code ● … in short: all application-specific code your team writes
The Basics – Directory Layout corelib
The Basics – Directory Layout corelib ● js – your client libraries (ExtJS, own implementations)
The Basics – Directory Layout corelib ● js – your client libraries (ExtJS, own implementations) ● php – your backend code (including tests)
The Basics – Directory Layout datastore ● data storage definition/structure goes here
The Basics – Directory Layout datastore ● data storage definition/structure goes here Note: ● build scripts can refer to the structure file when deploying an application
The Basics – Directory Layout www ● one step more towards a „callable“ application
The Basics – Directory Layout application
The Basics – Directory Layout application ● ZF specific „frontend“ code (controllers, templates)
The Basics – Directory Layout application ● ZF specific „frontend“ code (controllers, templates) ● and: – Meta-Information files for your application – caching directories – etc.
The Basics – Directory Layout htdocs
The Basics – Directory Layout htdocs ● Finally! The document root for your application ● the only „public“ folder
The Basics – Directory Layout htdocs ● Finally! The document root for your application ● the only „public“ folder Note: ● Build-process focuses on this folder
The Basics – Directory Layout „This layout gets way too complex!“ Don't go berserk! There's a solution to all of it!
The Basics – Directory Layout „Do I need to run a build process every time a line of code changed?“ „How do I get the resources from vendor into my src folder where – obviously - running code will resist?“
The Basics – Directory Layout working copy (development): /var/www/your_project/vendor/ext-ux-util-messagebus /var/www/your_project/src/www/htdocs/index.php
The Basics – Directory Layout working copy (development): /var/www/your_project/vendor/ext-ux-util-messagebus /var/www/your_project/src/www/htdocs/index.php <?php if ($isDeployment) { ?> <script type="text/javascript" src="/js/ext-ux-util-messagebus/src/messagebus.js"></script> <?php } else { ?> <script type="text/javascript" src="/vendor/ext-ux-util-messagebus/src/messagebus.js"></script> <?php } ?> index.phtml
The Basics – Directory Layout working copy (development): /var/www/your_project/vendor/ext-ux-util-messagebus /var/www/your_project/src/www/htdocs/index.php <?php if ($isDeployment) { ?> <script type="text/javascript" src="/js/ext-ux-util-messagebus/src/messagebus.js"></script> <?php } else { ?> <script type="text/javascript" src="/vendor/ext-ux-util-messagebus/src/messagebus.js"></script> <?php } ?> index.phtml # Alias for Ext.ux.util.MessageBus Alias /js/ext-ux-util-messagebus "/htdocs/your_project/vendor/ext-ux-util-messagebus/src" <Directory "/htdocs/your_project/vendor/ext-ux-util-messagebus/src"> Order allow,deny Allow from all </Directory> apache.conf
The Basics – Directory Layout working copy (development): /var/www/your_project/vendor/ext-ux-util-messagebus /var/www/your_project/src/www/htdocs/index.php <?php if ($isDeployment) { ?> <script type="text/javascript" src="/js/ext-ux-util-messagebus/src/messagebus.js"></script> <?php } else { ?> <script type="text/javascript" src="/vendor/ext-ux-util-messagebus/src/messagebus.js"></script> <?php } ?> index.phtml # Alias for Ext.ux.util.MessageBus Alias /js/ext-ux-util-messagebus "/htdocs/your_project/vendor/ext-ux-util-messagebus/src" <Directory "/htdocs/your_project/vendor/ext-ux-util-messagebus/src"> Order allow,deny Allow from all </Directory> apache.conf <script type="text/javascript" src="/js/ext-ux-util-messagebus/src/messagebus.js"></script> index.phtml
The Basics – Directory Layout working copy (development): /var/www/your_project/vendor/ext-ux-util-messagebus /var/www/your_project/src/www/htdocs/index.php # Alias for Ext.ux.util.MessageBus Development: Alias /js/ext-ux-util-messagebus Virtual
The Basics – Directory Layout working copy (development): /var/www/your_project/vendor/ext-ux-util-messagebus /var/www/your_project/src/www/htdocs/index.php # Alias for Ext.ux.util.MessageBus Development: Alias /js/ext-ux-util-messagebus Virtual Build: ... <target name="build_js"> <delete dir="./build/js/ext-ux-util-messagebus" /> <copy todir="./build/js/ext-ux-util-messagebus" includeemptydirs="true"> <fileset dir="./vendor/ext-ux-util-messagebus"> <exclude name="**/.svn" /> </fileset> </copy> </target> ... Build process
The Basics – Directory Layout working copy (development): /var/www/your_project/vendor/ext-ux-util-messagebus /var/www/your_project/src/www/htdocs/index.php # Alias for Ext.ux.util.MessageBus Development: Alias /js/ext-ux-util-messagebus Virtual Build: ... <target name="build_js"> <delete dir="./build/js/ext-ux-util-messagebus" /> <copy todir="./build/js/ext-ux-util-messagebus" includeemptydirs="true"> <fileset dir="./vendor/ext-ux-util-messagebus"> <exclude name="**/.svn" /> </fileset> </copy> </target> ... Build process <script type="text/javascript" src="/js/ext-ux-util-messagebus/src/messagebus.js"></script> index.phtml
The Basics – Directory Layout Cons ● will need some webserver configuration to get things working ● build-files depend on initial layout, changes mean adjustment at several locations at once ● needs documentation for your dev team ● users need to know how to set up their dev environment when they check out „including sources“
The Basics – Directory Layout Pros ● No symlinks –> not f***ing up the repository ● Code structured after purpose ● No need to run builds after you've changed one line of code (except for tests, of course) ● Clean approach towards build- and development-code ● Makes build-definitions easy due to strictly defined layout Advice: ● Use svn.ignore and template files! - it will help you and you're coworkers to set up things on different machines
Context based data What is a „context“?
Context based data Application Server
Context based data Application Server send/receive Desktop PC
Context based data Application Server send/receive send/receive Desktop PC Mobile
Context based data Application Server send/receive send/receive Desktop PC Mobile Context „default“
Context based data Application Server send/receive send/receive Desktop PC Mobile Context „default“ Context „mobile“
Context based data Why do we need it?
Context based data ● Different devices need different views ● Content delivery optimizations ● One domain serves all (www.senchadevcon.eu vs. m.senchadevcon.eu) ● Specific data format (send/receive) might not be available on devices used by our users
Context based data What do we need?
Context based data class Zend_Controller_Action_Helper_ContextSwitch ContextSwitch.php
Context based data class Zend_Controller_Action_Helper_ContextSwitch ContextSwitch.php ● Action helper that will detect context based requests ● Capable of sending specially formatted responses based on detected context and configuration ● Available as a default action helper provided by Zend Framework ● For more informations on how to use action helper, see [3]
Context based data How does it work?
Context based data - detection We can define a context based on (virtually) all data that's available during runtime!
Context based data - detection We can define a context based on (virtually) all data that's available during runtime! For example: http://myproject.com/user/reception/get.user/format/json url
Context based data - detection We can define a context based on (virtually) all data that's available during runtime! For example: http://myproject.com/user/reception/get.user/format/json url module
Context based data - detection We can define a context based on (virtually) all data that's available during runtime! For example: http://myproject.com/user/reception/get.user/format/json url controller
Context based data - detection We can define a context based on (virtually) all data that's available during runtime! For example: http://myproject.com/user/reception/get.user/format/json url action
Context based data - detection We can define a context based on (virtually) all data that's available during runtime! For example: http://myproject.com/user/reception/get.user/format/json url parameter/value pair
Context based data - detection We can define a context based on (virtually) all data that's available during runtime! For example: http://myproject.com/user/reception/get.user/format/json url $_GET['format'] == 'json'
Context based data - detection We can define a context based on (virtually) all data that's available during runtime! For example: http://myproject.com/user/reception/get.user/format/json url $_GET['format'] == 'json' ● Application is now in „json“ context – i.e., return all responses json formatted, since the client told us so!
Context based data - detection We can define a context based on (virtually) all data that's available during runtime! For example: http://myproject.com/user/reception/get.user/format/json url $_GET['format'] == 'json' ● Application is now in „json“ context – i.e., return all responses json formatted, since the client told us so! ● In fact, this is a default option coming with the ContextSwitch action helper
Context based data - detection We can define a context based on (virtually) all data that's available during runtime! Another example: ... $userAgent = strtolower($_SERVER['HTTP_USER_AGENT']); // request coming from ipad? if (strpos($userAgent, 'ipad') !== false) { $this->currentContext = 'ipad'; // request coming from android? } else if (strpos($userAgent), 'android') !== false) { $this->currentContext = 'android'; } ... User Agent ● Context set by detection, not manually enforced by parameters
Context based data So what can it do for me?
Context based data ● Detect devices/users (webbrowser, mobile, bots)
Context based data ● Detect devices/users (webbrowser, mobile, bots) ● One codebase for all devices: „Write once, run anywhere“
Context based data ● Detect devices/users (webbrowser, mobile, bots) ● One codebase for all devices: „Write once, run anywhere“
Context based data ● Detect devices/users (webbrowser, mobile, bots) ● One codebase for all devices: „Write once, run anywhere“
Context based data ● Detect devices/users (webbrowser, mobile, bots) ● One codebase for all devices:„Write once, run anywhere“ ● View variables will either be assigned to templates (plain html mixed with PHP for example) or transformed to json (xml etc.) – automatically – no need to implement special logic as long as your client can handle the response ● Requesting different formats is often just a thing of switching a parameter at client site ● Makes even delivering views from the backend very easy! ● Test a context by switching a parameter ● Only views? No, different business logic depending on the context, too!
Context based data - example class User_ReceptionController extends Zend_Controller_Action() { public function init() { // define the actions that should consider different contexts $this->_helper->contextSwitch() ->addActionContext('get.user', 'json') ->initContext(); } /** * This method will return user data to the client as requested. */ public function getUserAction() { ... $this->view->userName = 'Peter Griffin'; $this->view->userEmail = 'peter@birdistheword.com'; } } ReceptionController.php
Context based data - example class User_ReceptionController extends Zend_Controller_Action() { public function init() { // define the actions that should consider different contexts $this->_helper->contextSwitch() ->addActionContext('get.user', 'json') implement parent's init() method ->initContext(); } to add action contexts... /** * This method will return user data to the client as requested. */ public function getUserAction() { ... $this->view->userName = 'Peter Griffin'; $this->view->userEmail = 'peter@birdistheword.com'; } } ReceptionController.php
Context based data - example class User_ReceptionController extends Zend_Controller_Action() { public function init() { // define the actions that should consider different contexts $this->_helper->contextSwitch() ->addActionContext('get.user', 'json') ->initContext(); } /** * This method will return user data to the client as requested. ...which happens here: */ public function getUserAction() { ... $this->view->userName = 'Peter Griffin'; $this->view->userEmail = 'peter@birdistheword.com'; } } ReceptionController.php
Context based data - example class User_ReceptionController extends Zend_Controller_Action() { public function init() { // define the actions that should consider different contexts $this->_helper->contextSwitch() ->addActionContext('get.user', 'json') ->initContext(); } /** * This method will return user data to the client as requested. getUserAction() will now... */ public function getUserAction() { ... $this->view->userName = 'Peter Griffin'; $this->view->userEmail = 'peter@birdistheword.com'; } } ReceptionController.php
Context based data - example class User_ReceptionController extends Zend_Controller_Action() { public function init() { // define the actions that should consider different contexts $this->_helper->contextSwitch() ->addActionContext('get.user', 'json') ->initContext(); } /** * This method will return user data response json-formatted if … return the to the client as requested. */ the get-Parameter „format“ was set to „json“ public function getUserAction() { ... $this->view->userName = 'Peter Griffin'; $this->view->userEmail = 'peter@birdistheword.com'; } } ReceptionController.php
Context based data - example class User_ReceptionController extends Zend_Controller_Action() { public function init() { // define the actions that should consider different contexts $this->_helper->contextSwitch() ->addActionContext('get.user', 'json') ->initContext(); } /** * This method will return user data to the client as requested. */ public function getUserAction() { ... $this->view->userName = 'Peter Griffin'; $this->view->userEmail = 'peter@birdistheword.com'; } We have just added an action context to this action } ReceptionController.php
Context based data - example class User_ReceptionController extends Zend_Controller_Action() { public function init() { // define the actions that should consider different contexts $this->_helper->contextSwitch() ->addActionContext('get.user', 'json') ->initContext(); } /** * This method will return user data to the client as requested. */ public function getUserAction() { ... $this->view->userName = 'Peter Griffin'; $this->view->userEmail = 'peter@birdistheword.com'; } We have just added an action context to this action } ReceptionController.php
Context based data - example class User_ReceptionController extends Zend_Controller_Action() { public function init() { // define the actions that should consider different contexts $this->_helper->contextSwitch() ->addActionContext('get.user', 'json') ->initContext(); } /** * This method will return user data to the client as requested. */ Call business logic... public function getUserAction() { ... $this->view->userName = 'Peter Griffin'; $this->view->userEmail = 'peter@birdistheword.com'; } } ReceptionController.php
Context based data - example class User_ReceptionController extends Zend_Controller_Action() { public function init() { // define the actions that should consider different contexts $this->_helper->contextSwitch() ->addActionContext('get.user', 'json') ->initContext(); } /** * This method will return user data to the client as requested. */ And finally: Assign values to the public function getUserAction() controller's view { ... $this->view->userName = 'Peter Griffin'; $this->view->userEmail = 'peter@birdistheword.com'; } } ReceptionController.php
Context based data - example class User_ReceptionController extends Zend_Controller_Action() { ... public function getUserAction() { ... $this->view->userName = 'Peter Griffin'; $this->view->userEmail = 'peter@birdistheword.com'; } } ReceptionController.php Ext.Ajax.request({ url : „/user/reception/get.user/format/json“, success : function(response) { console.log(response.responseText); } }); client.js
Context based data - example class User_ReceptionController extends Zend_Controller_Action() { ... public function getUserAction() { ... $this->view->userName = 'Peter Griffin'; $this->view->userEmail = 'peter@birdistheword.com'; } Module } ReceptionController.php Ext.Ajax.request({ url : „/user/reception/get.user/format/json“, success : function(response) { console.log(response.responseText); } }); client.js
Context based data - example class User_ReceptionController extends Zend_Controller_Action() { ... public function getUserAction() { ... $this->view->userName = 'Peter Griffin'; $this->view->userEmail = 'peter@birdistheword.com'; } Controller } ReceptionController.php Ext.Ajax.request({ url : „/user/reception/get.user/format/json“, success : function(response) { console.log(response.responseText); } }); client.js
Context based data - example class User_ReceptionController extends Zend_Controller_Action() { ... public function getUserAction() { ... $this->view->userName = 'Peter Griffin'; $this->view->userEmail = 'peter@birdistheword.com'; } Action } ReceptionController.php Ext.Ajax.request({ url : „/user/reception/get.user/format/json“, success : function(response) { console.log(response.responseText); } }); client.js
Context based data - example Note ● This illustration explains how a request gets processed by Zend Framework [6] ● We'll use it to show how ContextSwitch changes the ResponseObject based on the application's context
Context based data - example Request Client invokes request
Context based data - example Request Router Zend Framework routes to User_ReceptionController::getUserAction()
Context based data - example Request Router pre Any plugins defined? Dispatch Signal a preDispatch to them!
Context based data - example Request Router pre Any plugins defined? Dispatch Signal a preDispatch to them! public function preDispatch() { } Zend_Controller_Action_Helper_Abstract
Context based data - example Request Router pre Dispatch the action – Dispatch getUserAction() gets processed! Dispatch
Context based data - example Request Router pre Dispatch Dispatch Action Controller public function getUserAction() { ... $this->view->userName = 'Peter Griffin'; $this->view->userEmail = 'peter@birdistheword.com'; } ReceptionController.php
Context based data - example Request Router pre Dispatch Action Dispatch Controller Any plugins defined? post Signal a postDispatch to them! Dispatch
Context based data - example Request Router pre Dispatch Action Dispatch Controller Any plugins defined? post Signal a postDispatch to them! Dispatch public function postDispatch() { $context = $this->getCurrentContext(); ... //$this->postJsonContext(); } ContextSwitch.php
Context based data - example Request Router pre Dispatch Action Dispatch Controller post Dispatch $response->setHeader('Content-Type', 'application/json'); ContextSwitch.php actions left? Response Object
Context based data - example Request Router pre Dispatch Action Dispatch Controller post Dispatch $response->setHeader('Content-Type', 'application/json'); ContextSwitch.php actions left? $response->setBody(Zend_Json::encode($view->getVars())); ContextSwitch.php Response Object
Context based data - example Request Router pre Dispatch Action Dispatch Controller post Dispatch actions left? send response Response Object
Context based data - example Request Router pre Dispatch Action Dispatch Controller post Dispatch actions left? {'userName' : „Peter Griffin“, 'userEmail' : „peter@birdistheword.com“} console send response Response Object
Context based data AWESOME! This makes coding a real blast!
Skeptikal Hippo It makes sense when working with Ext 1.* and 2.*, but what about Ext.direct.*?
Ext.direct.* What is Ext.direct.*?
Ext.direct.* „Ext Direct is a platform and language agnostic technology to remote server-side methods to the client-side.“[4]
Ext.direct.* ● That new kid in class no one wants to play with
Ext.direct.* ● That new kid in class no one wants to play with ● Bugs
Ext.direct.* ● That new kid in class no one wants to play with ● Bugs ● Clumsy
Ext.direct.* ● That new kid in class no one wants to play with ● Bugs ● Clumsy ● Implementation too complex
Ext.direct.* ● That new kid in class no one wants to play with ● Bugs ● Clumsy ● Implementation too complex ● The bad thing: – Sencha started to focus remote functionality in their library on Ext.direct.* with 3.*
Ext.direct.* ● That new kid in class no one wants to play with ● Bugs ● Clumsy ● Implementation too complex ● The bad thing: – Sencha started to focus remote functionality in their library on Ext.direct.* with 3.* ● The good thing: – Sencha eliminated almost all implementation issues
Ext.direct.* - advantages ● Let's you call remote procedures directly from client code class User_ReceptionController extends Zend_Controller_Action { public function getUserAction() { ... } } ReceptionController.php
Ext.direct.* - advantages ● Let's you call remote procedures directly from client code class User_ReceptionController extends Zend_Controller_Action { public function getUserAction() { ... } } ReceptionController.php user.reception.getUser(); client.js
Ext.direct.* - advantages ● Let's you call remote procedures directly from client code class User_ReceptionController extends Zend_Controller_Action { public function getUserAction() { ... } } ReceptionController.php module user.reception.getUser(); client.js
Ext.direct.* - advantages ● Let's you call remote procedures directly from client code class User_ReceptionController extends Zend_Controller_Action { public function getUserAction() { ... } } ReceptionController.php controller user.reception.getUser(); client.js
Ext.direct.* - advantages ● Let's you call remote procedures directly from client code class User_ReceptionController extends Zend_Controller_Action { public function getUserAction() { ... } } ReceptionController.php action user.reception.getUser(); client.js
Ext.direct.* - advantages ● Can stack remote procedure calls and send them as one „batched request“ class Groupware_AccountController extends Zend_Controller_Action { public function getEmailAccountsAction(){ ... } public function getFeedAccountsAction(){ ... } } GroupwareController.php
Ext.direct.* - advantages ● Can stack remote procedure calls and send them as one „batched request“ class Groupware_AccountController extends Zend_Controller_Action { public function getEmailAccountsAction(){ ... } public function getFeedAccountsAction(){ ... } } GroupwareController.php groupware.account.getEmailAccounts(); client.js
Ext.direct.* - advantages ● Can stack remote procedure calls and send them as one „batched request“ class Groupware_AccountController extends Zend_Controller_Action { public function getEmailAccountsAction(){ ... } public function getFeedAccountsAction(){ ... } } GroupwareController.php groupware.account.getEmailAccounts(); client.js
Ext.direct.* - advantages ● Can stack remote procedure calls and send them as one „batched request“ class Groupware_AccountController extends Zend_Controller_Action { public function getEmailAccountsAction(){ ... } public function getFeedAccountsAction(){ ... } } GroupwareController.php groupware.account.getEmailAccounts(); groupware.account.getFeedAccounts(); client.js Firebug
Ext.direct.* - advantages ● Can stack remote procedure calls and send them as one „batched request“ class Groupware_AccountController extends Zend_Controller_Action { public function getEmailAccountsAction(){ ... } public function getFeedAccountsAction(){ ... } } GroupwareController.php groupware.account.getEmailAccounts(); groupware.account.getFeedAccounts(); client.js POST http://sourcedevcon/groupware/account/get.email.accounts POST http://sourcedevcon/groupware/account/get.feed.accounts Firebug
Ext.direct.* - advantages ● Can stack remote procedure calls and send them as one „batched request“ class Groupware_AccountController extends Zend_Controller_Action { public function getEmailAccountsAction(){ ... } public function getFeedAccountsAction(){ ... } } GroupwareController.php groupware.account.getEmailAccounts(); groupware.account.getFeedAccounts(); client.js POST http://sourcedevcon/groupware/account/get.email.accounts POST http://sourcedevcon/groupware/account/get.feed.accounts Firebug POST http://sourcedevcon/groupware Firebug
Ext.direct.* - advantages ● Can stack remote procedure calls and send them as one „batched request“ class Groupware_AccountController extends Zend_Controller_Action { public function getEmailAccountsAction(){ ... } public function getFeedAccountsAction(){ ... } } GroupwareController.php groupware.account.getEmailAccounts(); groupware.account.getFeedAccounts(); client.js POST http://sourcedevcon/groupware/account/get.email.accounts POST http://sourcedevcon/groupware/account/get.feed.accounts Firebug POST http://sourcedevcon/groupware extDirectData [{"action":"account","method":"getEmailAccounts","data":null,"type":"rpc","tid":1}, {"action":"account","method":"getFeedAccounts","data":null,"type":"rpc","tid":2}] Firebug
Ext.direct.* Batched request – what gives?
Ext.direct.* ● Can reduce serverload (1 request with n calls to specific actions instead of n calls)
Ext.direct.* ● Can reduce serverload (1 request with n calls to specific actions instead of n calls) ● Reduces possibility of losing connections (http allows for n concurrent connections to one domain at a time)
Ext.direct.* ● Can reduce serverload (1 request with n calls to specific actions instead of n calls) ● Reduces possibility of losing connections (http allows for n concurrent connections to one domain at a time) This alone are tremendously important reasons for you to go ahead and make friendship with that new kid in class!
Ext.direct.* w/ Zend Framework Unfortunately, not supported out of the box :(
Ext.direct.* w/ Zend Framework … so let's find a solution. Let's sum up our goals:
Ext.direct.* w/ Zend Framework ● Switch client code to Ext.direct.* without having to change backend source code ● Add another tier of complexity and automatically create backend methods/client code. Tests, changing naming conventions etc. have to be added by hand then. ● Keep our backend testable, reusable and make sure it runs with any other client library/ implementation
Ext.direct.* w/ Zend Framework ● Switch client code to Ext.direct.* without having to change backend source code ● Add another tier of complexity and automatically create backend methods/client code. Tests, changing naming conventions etc. have to be added by hand then. ● Keep our backend testable, reusable and make sure it runs with any other client library/ implementation
Ext.direct.* w/ Zend Framework ● Switch client code to Ext.direct.* without having to change backend source code ● Add another tier of complexity and automatically create backend methods/client code. Tests, changing naming conventions etc. have to be added by hand then. ● Keep our backend testable, reusable and make sure it runs with any other client library/ implementation
Ext.direct.* w/ Zend Framework ● Switch client code to Ext.direct.* without having to change backend source code ● Add another tier of complexity and automatically create backend methods/client code. Tests, changing naming conventions etc. have to be added by hand then. ● Keep our backend testable, reusable and make sure it runs with any other client library/ implementation
Ext.direct.* w/ Zend Framework What we need to do:
Ext.direct.* w/ Zend Framework – Show requests sent by Ext.direct.* their way through Zend Framework to their module/controller/action – Teach Zend Framework how to distinguish between old fashioned and merged requests – Show Zend Framework how to disassemble 1 request into n requests – Give Zend Framework a sandbox where it can work/play/you name it with a disamssembled request – Collect sandboxed requests and merge them back into one response
Ext.direct.* w/ Zend Framework Change Ext.direct.RemotingProvider URLs on the fly
Ext.direct.* w/ Zend Framework Remember? The Ext.direct.RemotingProvider exposes access to server side methods on the client. By mapping those remote methods to the client, there is almost no need anymore to spray URLs around the source like crazy. It establishes a connection between the client and the backend by providing a programmer friendly interface.
Ext.direct.* w/ Zend Framework Ext.direct.Manager.addProvider({ id : 'eu.sourcedevcon.provider', enableUrlEncode : 'extDirectData', url : './groupware', format : 'json', type : 'zend', actions : { account : [{ name : 'getFeedAccounts' }, { name : 'getEmailAccounts' }] }, namespace : 'eu.sourcedevcon.provider' }); client.js
Ext.direct.* w/ Zend Framework Ext.direct.Manager.addProvider({ id : 'eu.sourcedevcon.provider', enableUrlEncode : 'extDirectData', url : './groupware', URL format : 'json', type : 'zend', actions : { account : [{ name : 'getFeedAccounts' }, { name : 'getEmailAccounts' }] }, namespace : 'eu.sourcedevcon.provider' }); client.js
Ext.direct.* w/ Zend Framework Ext.direct.Manager.addProvider({ id : 'eu.sourcedevcon.provider', enableUrlEncode : 'extDirectData', url : './groupware', URL format : 'json', type : 'zend', actions : { account : [{ „action“ name : 'getFeedAccounts' }, { name : 'getEmailAccounts' }] }, namespace : 'eu.sourcedevcon.provider' }); client.js
Ext.direct.* w/ Zend Framework Ext.direct.Manager.addProvider({ id : 'eu.sourcedevcon.provider', enableUrlEncode : 'extDirectData', url : './groupware', URL format : 'json', type : 'zend', actions : { account : [{ „action“ name : 'getFeedAccounts' }, { name : 'getEmailAccounts' method }] }, namespace : 'eu.sourcedevcon.provider' }); client.js
Ext.direct.* w/ Zend Framework Ext.direct.Manager.addProvider({ id : 'eu.sourcedevcon.provider', enableUrlEncode : 'extDirectData', url : './groupware', format : 'json', type : 'zend', actions : { account : [{ name : 'getFeedAccounts' }, { name : 'getEmailAccounts' }] }, namespace : 'eu.sourcedevcon.provider' }); client.js eu.sourcedevcon.provider.account.getEmailAccounts(); client.js
Ext.direct.* w/ Zend Framework Ext.direct.Manager.addProvider({ id : 'eu.sourcedevcon.provider', enableUrlEncode : 'extDirectData', url : './groupware', format : 'json', type : 'zend', actions : { account : [{ name : 'getFeedAccounts' }, { name : 'getEmailAccounts' }] }, namespace : 'eu.sourcedevcon.provider' }); namespace client.js eu.sourcedevcon.provider.account.getEmailAccounts(); client.js
Ext.direct.* w/ Zend Framework Ext.direct.Manager.addProvider({ id : 'eu.sourcedevcon.provider', enableUrlEncode : 'extDirectData', url : './groupware', format : 'json', type : 'zend', actions : { account : [{ name : 'getFeedAccounts' }, { name : 'getEmailAccounts' }] action }, namespace : 'eu.sourcedevcon.provider' }); client.js eu.sourcedevcon.provider.account.getEmailAccounts(); client.js
Ext.direct.* w/ Zend Framework Ext.direct.Manager.addProvider({ id : 'eu.sourcedevcon.provider', enableUrlEncode : 'extDirectData', url : './groupware', format : 'json', type : 'zend', actions : { account : [{ name : 'getFeedAccounts' }, { name : 'getEmailAccounts' }] }, namespace : 'eu.sourcedevcon.provider' method }); client.js eu.sourcedevcon.provider.account.getEmailAccounts(); client.js
Ext.direct.* w/ Zend Framework Ext.direct.Manager.addProvider({ id : 'eu.sourcedevcon.provider', enableUrlEncode : 'extDirectData', url : './groupware', format : 'json', type : 'zend', actions : { account : [{ name : 'getFeedAccounts' }, { name : 'getEmailAccounts' }] }, namespace : 'eu.sourcedevcon.provider' }); client.js eu.sourcedevcon.provider.account.getEmailAccounts(); client.js POST http://sourcedevcon/groupware Firebug
Ext.direct.* w/ Zend Framework Ext.direct.Manager.addProvider({ id : 'eu.sourcedevcon.provider', enableUrlEncode : 'extDirectData', url : './groupware', format : 'json', type : 'zend', actions : { account : [{ name : 'getFeedAccounts' }, { name : 'getEmailAccounts' }] }, namespace : 'eu.sourcedevcon.provider' }); client.js eu.sourcedevcon.provider.account.getEmailAccounts(); client.js POST http://sourcedevcon/groupware extDirectData {"action":"account","method":"getEmailAccounts","data":null,"type":"rpc","tid":1} Firebug
Ext.direct.* w/ Zend Framework Ext.direct.Manager.addProvider({ id : 'eu.sourcedevcon.provider', enableUrlEncode : 'extDirectData', url : './groupware', format : 'json', type : 'zend', actions : { account : [{ name : 'getFeedAccounts' }, { name : 'getEmailAccounts' }] }, namespace : 'eu.sourcedevcon.provider' }); client.js eu.sourcedevcon.provider.account.getEmailAccounts(); client.js POST http://sourcedevcon/groupware extDirectData {"action":"account","method":"getEmailAccounts","data":null,"type":"rpc","tid":1} Firebug
Ext.direct.* w/ Zend Framework Ext.direct.Manager.addProvider({ id : 'eu.sourcedevcon.provider', enableUrlEncode : 'extDirectData', url : './groupware', format : 'json', type : 'zend', actions : { account : [{ name : 'getFeedAccounts' }, { name : 'getEmailAccounts' }] }, namespace : 'eu.sourcedevcon.provider' }); client.js eu.sourcedevcon.provider.account.getEmailAccounts(); client.js POST http://sourcedevcon/groupware extDirectData {"action":"account","method":"getEmailAccounts","data":null,"type":"rpc","tid":1} Firebug
Ext.direct.* w/ Zend Framework Ext.direct.Manager.addProvider({ id : 'eu.sourcedevcon.provider', enableUrlEncode : 'extDirectData', url : './groupware', format : 'json', type : 'zend', actions : { account : [{ name : 'getFeedAccounts' }, { name : 'getEmailAccounts' }] }, namespace : 'eu.sourcedevcon.provider' }); client.js eu.sourcedevcon.provider.account.getEmailAccounts(); client.js POST http://sourcedevcon/groupware extDirectData {"action":"account","method":"getEmailAccounts","data":null,"type":"rpc","tid":1} Firebug
Ext.direct.* w/ Zend Framework Ext.direct.Manager.addProvider({ id : 'eu.sourcedevcon.provider', enableUrlEncode : 'extDirectData', url : './groupware', format : 'json', type : 'zend', actions : { account : [{ name : 'getFeedAccounts' }, { name : 'getEmailAccounts' }] }, namespace : 'eu.sourcedevcon.provider' }); client.js eu.sourcedevcon.provider.account.getEmailAccounts(); client.js POST http://sourcedevcon/groupware extDirectData {"action":"account","method":"getEmailAccounts","data":null,"type":"rpc","tid":1} Firebug
Ext.direct.* w/ Zend Framework Ext.direct.Manager.addProvider({ id : 'eu.sourcedevcon.provider', enableUrlEncode : 'extDirectData', url : './groupware', format : 'json', type : 'zend', actions : { account : [{ name : 'getFeedAccounts' }, { name : 'getEmailAccounts' }] }, namespace : 'eu.sourcedevcon.provider' }); client.js eu.sourcedevcon.provider.account.getEmailAccounts(); client.js POST http://sourcedevcon/groupware extDirectData {"action":"account","method":"getEmailAccounts","data":null,"type":"rpc","tid":1} Firebug if (isset($_POST['extDirectData'])) { ... } /groupware Firebug
Ext.direct.* w/ Zend Framework Ext.direct.Manager.addProvider({ id : 'eu.sourcedevcon.provider', enableUrlEncode : 'extDirectData', url : './groupware', format : 'json', type : 'zend', actions : { account : [{ name : 'getFeedAccounts' }, { name : 'getEmailAccounts' }] }, namespace : 'eu.sourcedevcon.provider' }); client.js eu.sourcedevcon.provider.account.getEmailAccounts(); client.js POST http://sourcedevcon/groupware extDirectData {"action":"account","method":"getEmailAccounts","data":null,"type":"rpc","tid":1} Firebug if (isset($_POST['extDirectData'])) { ... } /groupware Firebug
Ext.direct.* w/ Zend Framework Ext.direct.Manager.addProvider({ id : 'eu.sourcedevcon.provider', enableUrlEncode : 'extDirectData', url : './groupware', format : 'json', type : 'zend', actions : { account : [{ name : 'getFeedAccounts' }, { name : 'getEmailAccounts' }] }, namespace : 'eu.sourcedevcon.provider' }); client.js eu.sourcedevcon.provider.account.getEmailAccounts(); client.js POST http://sourcedevcon/groupware extDirectData {"action":"account","method":"getEmailAccounts","data":null,"type":"rpc","tid":1} Firebug if (isset($_POST['extDirectData'])) { http://sourcedevcon/groupware/account/get.email.accounts ... } /groupware Firebug
Ext.direct.* w/ Zend Framework Ext.direct.Manager.addProvider({ id : 'eu.sourcedevcon.provider', enableUrlEncode : 'extDirectData', url : './groupware', format : 'json', type : 'zend', actions : { account : [{ name : 'getFeedAccounts' }, { name : 'getEmailAccounts' }] }, namespace : 'eu.sourcedevcon.provider' }); client.js eu.sourcedevcon.provider.account.getEmailAccounts(); URL = module client.js POST http://sourcedevcon/groupware extDirectData {"action":"account","method":"getEmailAccounts","data":null,"type":"rpc","tid":1} Firebug if (isset($_POST['extDirectData'])) { Http://sourcedevcon/groupware/account/get.email.accounts ... } /groupware Firebug
Ext.direct.* w/ Zend Framework Ext.direct.Manager.addProvider({ id : 'eu.sourcedevcon.provider', enableUrlEncode : 'extDirectData', url : './groupware', format : 'json', type : 'zend', actions : { account : [{ name : 'getFeedAccounts' }, { name : 'getEmailAccounts' }] }, namespace : 'eu.sourcedevcon.provider' }); client.js eu.sourcedevcon.provider.account.getEmailAccounts(); URL = module client.js action = controller POST http://sourcedevcon/groupware extDirectData {"action":"account","method":"getEmailAccounts","data":null,"type":"rpc","tid":1} Firebug if (isset($_POST['extDirectData'])) { http://sourcedevcon/groupware/account/get.email.accounts ... } /groupware Firebug
Ext.direct.* w/ Zend Framework Ext.direct.Manager.addProvider({ id : 'eu.sourcedevcon.provider', enableUrlEncode : 'extDirectData', url : './groupware', format : 'json', type : 'zend', actions : { account : [{ name : 'getFeedAccounts' }, { name : 'getEmailAccounts' }] }, namespace : 'eu.sourcedevcon.provider' }); client.js eu.sourcedevcon.provider.account.getEmailAccounts(); URL = module client.js action = controller POST http://sourcedevcon/groupware extDirectData method = action {"action":"account","method":"getEmailAccounts","data":null,"type":"rpc","tid":1} Firebug if (isset($_POST['extDirectData'])) { http://sourcedevcon/groupware/account/get.email.accounts ... } /groupware Firebug
Ext.direct.* w/ Zend Framework Ext.direct.Manager.addProvider({ id : 'eu.sourcedevcon.provider', enableUrlEncode : 'extDirectData', url : './groupware', URL format : 'json', type : 'zend', actions : { account : [{ „action“ name : 'getFeedAccounts' }, { name : 'getEmailAccounts' method }] }, namespace : 'eu.sourcedevcon.provider' }); client.js eu.sourcedevcon.provider.account.getEmailAccounts(); URL = module client.js action = controller POST http://sourcedevcon/groupware extDirectData method = action {"action":"account","method":"getEmailAccounts","data":null,"type":"rpc","tid":1} Firebug if (isset($_POST['extDirectData'])) { http://sourcedevcon/groupware/account/get.email.accounts ... } /groupware Firebug
Ext.direct.* w/ Zend Framework Ext.direct.Manager.addProvider({ id : 'eu.sourcedevcon.provider', enableUrlEncode : 'extDirectData', url : './groupware', format : 'json', type : 'zend', actions : { account : [{ name : 'getFeedAccounts' }, { name : 'getEmailAccounts' }] }, namespace : 'eu.sourcedevcon.provider' }); client.js
Ext.direct.* w/ Zend Framework Ext.direct.Manager.addProvider({ id : 'eu.sourcedevcon.provider', enableUrlEncode : 'extDirectData', url : './groupware', format : 'json', type : 'zend', actions : { account : [{ name : 'getFeedAccounts' }, { name : 'getEmailAccounts' }] }, namespace : 'eu.sourcedevcon.provider' }); client.js
Ext.direct.* w/ Zend Framework Ext.direct.Manager.addProvider({ id : 'eu.sourcedevcon.provider', enableUrlEncode : 'extDirectData', url : './groupware', format : 'json', type : 'zend', actions : { account : [{ name : 'getFeedAccounts' }, { name : 'getEmailAccounts' }] }, namespace : 'eu.sourcedevcon.provider' }); client.js Ext.define('eu.sourcedevcon.direct.ZendProvider', { alias : 'direct.zendprovider', extend : 'Ext.direct.RemotingProvider' }); ZendProvider.js
Ext.direct.* w/ Zend Framework URL = module action = controller method = action client.js
Ext.direct.* w/ Zend Framework URL = module action = controller method = action Ext.Ajax.on('beforerequest', function(conn, options) { var trans = options.transaction; if (trans && trans.provider && trans.provider.type == 'zend') { var controller = options.transaction.action; controller = controller.replace(/([a-z])([A-Z])/g, "$1.$2").toLowerCase(); var action = options.transaction.method; action = action.replace(/([a-z])([A-Z])/g, "$1.$2").toLowerCase(); var module = options.url; var url = module + (module.lastIndexOf('/') == module.length-1 ? '' : '/') + controller + '/' + action; if (options.transaction.provider.format) { url += '/format/' + options.transaction.provider.format; } options.url = url; options.disableCaching = true; } }); client.js
Ext.direct.* w/ Zend Framework URL = module action = controller method = action Ext.Ajax.on('beforerequest', function(conn, options) { var trans = options.transaction; if (trans && trans.provider && trans.provider.type == 'zend') { var controller = options.transaction.action; controller = controller.replace(/([a-z])([A-Z])/g, "$1.$2").toLowerCase(); var action = options.transaction.method; action = action.replace(/([a-z])([A-Z])/g, "$1.$2").toLowerCase(); var module = options.url; var url = module + (module.lastIndexOf('/') == module.length-1 ? '' : '/') + controller + '/' + action; if (options.transaction.provider.format) { url += '/format/' + options.transaction.provider.format; } options.url = url; options.disableCaching = true; } }); client.js
Ext.direct.* w/ Zend Framework URL = module action = controller method = action Ext.Ajax.on('beforerequest', function(conn, options) { var trans = options.transaction; if (trans && trans.provider && trans.provider.type == 'zend') { var controller = options.transaction.action; controller = controller.replace(/([a-z])([A-Z])/g, "$1.$2").toLowerCase(); var action = options.transaction.method; action = action.replace(/([a-z])([A-Z])/g, "$1.$2").toLowerCase(); var module = options.url; var url = module + (module.lastIndexOf('/') == module.length-1 ? '' : '/') + controller + '/' + action; if (options.transaction.provider.format) { url += '/format/' + options.transaction.provider.format; } options.url = url; options.disableCaching = true; } }); client.js
Ext.direct.* w/ Zend Framework URL = module action = controller method = action Ext.Ajax.on('beforerequest', function(conn, options) { var trans = options.transaction; if (trans && trans.provider && trans.provider.type == 'zend') { var controller = options.transaction.action; controller = controller.replace(/([a-z])([A-Z])/g, "$1.$2").toLowerCase(); var action = options.transaction.method; action = action.replace(/([a-z])([A-Z])/g, "$1.$2").toLowerCase(); var module = options.url; var url = module + (module.lastIndexOf('/') == module.length-1 ? '' : '/') + controller + '/' + action; if (options.transaction.provider.format) { url += '/format/' + options.transaction.provider.format; } options.url = url; options.disableCaching = true; } }); client.js
Ext.direct.* w/ Zend Framework URL = module action = controller method = action Ext.Ajax.on('beforerequest', function(conn, options) { var trans = options.transaction; if (trans && trans.provider && trans.provider.type == 'zend') { var controller = options.transaction.action; controller = controller.replace(/([a-z])([A-Z])/g, "$1.$2").toLowerCase(); var action = options.transaction.method; action = action.replace(/([a-z])([A-Z])/g, "$1.$2").toLowerCase(); var module = options.url; var url = module + (module.lastIndexOf('/') == module.length-1 ? '' : '/') + controller + '/' + action; if (options.transaction.provider.format) { url += '/format/' + options.transaction.provider.format; } options.url = url; options.disableCaching = true; } }); client.js
Ext.direct.* w/ Zend Framework URL = module action = controller method = action Ext.Ajax.on('beforerequest', function(conn, options) { var trans = options.transaction; if (trans && trans.provider && trans.provider.type == 'zend') { var controller = options.transaction.action; controller = controller.replace(/([a-z])([A-Z])/g, "$1.$2").toLowerCase(); var action = options.transaction.method; action = action.replace(/([a-z])([A-Z])/g, "$1.$2").toLowerCase(); var module = options.url; var url = module + (module.lastIndexOf('/') == module.length-1 ? '' : '/') + controller + '/' + action; if (options.transaction.provider.format) { url += '/format/' + options.transaction.provider.format; } options.url = url; options.disableCaching = true; } }); client.js
Ext.direct.* w/ Zend Framework URL = module action = controller method = action Ext.Ajax.on('beforerequest', function(conn, options) { var trans = options.transaction; if (trans && trans.provider && trans.provider.type == 'zend') { var controller = options.transaction.action; controller = controller.replace(/([a-z])([A-Z])/g, "$1.$2").toLowerCase(); var action = options.transaction.method; action = action.replace(/([a-z])([A-Z])/g, "$1.$2").toLowerCase(); var module = options.url; var url = module + (module.lastIndexOf('/') == module.length-1 ? '' : '/') + controller + '/' + action; if (options.transaction.provider.format) { ContextSwitch url += '/format/' + options.transaction.provider.format; } options.url = url; options.disableCaching = true; } }); client.js
Ext.direct.* w/ Zend Framework URL = module action = controller method = action Ext.Ajax.on('beforerequest', function(conn, options) { var trans = options.transaction; if (trans && trans.provider && trans.provider.type == 'zend') { var controller = options.transaction.action; controller = controller.replace(/([a-z])([A-Z])/g, "$1.$2").toLowerCase(); var action = options.transaction.method; action = action.replace(/([a-z])([A-Z])/g, "$1.$2").toLowerCase(); var module = options.url; var url = module + (module.lastIndexOf('/') == module.length-1 ? '' : '/') + controller + '/' + action; if (options.transaction.provider.format) { url += '/format/' + options.transaction.provider.format; } options.url = url; options.disableCaching = true; } }); client.js
Ext.direct.* w/ Zend Framework Ext.direct.Manager.addProvider({ id : 'eu.sourcedevcon.provider', enableUrlEncode : 'extDirectData', url : './groupware', format : 'json', type : 'zend', actions : { account : [{ name : 'getFeedAccounts' }, { name : 'getEmailAccounts' }] }, namespace : 'eu.sourcedevcon.provider' }); client.js eu.sourcedevcon.provider.account.getEmailAccounts(); client.js
Ext.direct.* w/ Zend Framework Ext.direct.Manager.addProvider({ id : 'eu.sourcedevcon.provider', enableUrlEncode : 'extDirectData', url : './groupware', format : 'json', type : 'zend', actions : { account : [{ name : 'getFeedAccounts' }, { name : 'getEmailAccounts' }] }, namespace : 'eu.sourcedevcon.provider' }); client.js eu.sourcedevcon.provider.account.getEmailAccounts(); client.js POST http://sourcedevcon/groupware
Ext.direct.* w/ Zend Framework Ext.direct.Manager.addProvider({ id : 'eu.sourcedevcon.provider', enableUrlEncode : 'extDirectData', url : './groupware', format : 'json', type : 'zend', actions : { account : [{ name : 'getFeedAccounts' }, { name : 'getEmailAccounts' }] }, namespace : 'eu.sourcedevcon.provider' }); client.js eu.sourcedevcon.provider.account.getEmailAccounts(); client.js POST http://sourcedevcon/groupware POST http://sourcedevcon/groupware/account/get.email.accounts/format/json extDirectData {"action":"account","method":"getEmailAccounts","data":null,"type":"rpc","tid":1} Firebug
Ext.direct.* w/ Zend Framework POST http://sourcedevcon/groupware/account/get.email.accounts/format/json extDirectData {"action":"account","method":"getEmailAccounts","data":null,"type":"rpc","tid":1} Firebug Note: ● Teach your actions the „extDirectData“-POST parameter
Ext.direct.* w/ Zend Framework POST http://sourcedevcon/groupware/account/get.email.accounts/format/json extDirectData {"action":"account","method":"getEmailAccounts","data":null,"type":"rpc","tid":1} Firebug Note: ● Teach your actions the „extDirectData“-POST parameter ● Your responses must return the „tid“ as received by the request
Ext.direct.* w/ Zend Framework Will do! Let me send a batched request now!
Ext.direct.* w/ Zend Framework Ext.direct.Manager.addProvider({ id : 'eu.sourcedevcon.provider', enableUrlEncode : 'extDirectData', url : './groupware', format : 'json', type : 'zend', actions : { account : [{ name : 'getFeedAccounts' }, { name : 'getEmailAccounts' }] }, namespace : 'eu.sourcedevcon.provider' }); client.js eu.sourcedevcon.provider.account.getEmailAccounts(); eu.sourcedevcon.provider.account.getFeedAccounts(); client.js
Ext.direct.* w/ Zend Framework Ext.direct.Manager.addProvider({ id : 'eu.sourcedevcon.provider', enableUrlEncode : 'extDirectData', url : './groupware', format : 'json', type : 'zend', actions : { account : [{ name : 'getFeedAccounts' }, { name : 'getEmailAccounts' }] }, namespace : 'eu.sourcedevcon.provider' }); client.js eu.sourcedevcon.provider.account.getEmailAccounts(); eu.sourcedevcon.provider.account.getFeedAccounts(); client.js POST http://sourcedevcon/groupware extDirectData [{"action":"account","method":"getEmailAccounts","data":null,"type":"rpc","tid":1}, {"action":"account","method":"getFeedAccounts","data":null,"type":"rpc","tid":2}] Firebug
Ext.direct.* w/ Zend Framework ...?
Ext.direct.* w/ Zend Framework queueTransaction: function(transaction){ var me = this, enableBuffer = me.enableBuffer; if (transaction.form) { me.sendFormRequest(transaction); return; } me.callBuffer.push(transaction); if (enableBuffer) { if (!me.callTask) { me.callTask = Ext.create('Ext.util.DelayedTask', me.combineAndSend, me); } me.callTask.delay(Ext.isNumber(enableBuffer) ? enableBuffer : 10); } else { me.combineAndSend(); } } RemotingProvider.js
Ext.direct.* w/ Zend Framework queueTransaction: function(transaction){ add transaction to callBuffer Array var me = this, enableBuffer = me.enableBuffer; if (transaction.form) { me.sendFormRequest(transaction); return; } me.callBuffer.push(transaction); if (enableBuffer) { if (!me.callTask) { me.callTask = Ext.create('Ext.util.DelayedTask', me.combineAndSend, me); } me.callTask.delay(Ext.isNumber(enableBuffer) ? enableBuffer : 10); } else { me.combineAndSend(); } } RemotingProvider.js
Ext.direct.* w/ Zend Framework queueTransaction: function(transaction){ add transaction to callBuffer Array var me = this, enableBuffer = me.enableBuffer; if batching is enabled, create a task if (transaction.form) { me.sendFormRequest(transaction); return; } me.callBuffer.push(transaction); if (enableBuffer) { if (!me.callTask) { me.callTask = Ext.create('Ext.util.DelayedTask', me.combineAndSend, me); } me.callTask.delay(Ext.isNumber(enableBuffer) ? enableBuffer : 10); } else { me.combineAndSend(); } } RemotingProvider.js
Ext.direct.* w/ Zend Framework queueTransaction: function(transaction){ add transaction to callBuffer Array var me = this, enableBuffer = me.enableBuffer; if batching is enabled, create a task wait for 10ms for another if (transaction.form) { transaction to be added me.sendFormRequest(transaction); return; } me.callBuffer.push(transaction); if (enableBuffer) { if (!me.callTask) { me.callTask = Ext.create('Ext.util.DelayedTask', me.combineAndSend, me); } me.callTask.delay(Ext.isNumber(enableBuffer) ? enableBuffer : 10); } else { me.combineAndSend(); } } RemotingProvider.js
Ext.direct.* w/ Zend Framework queueTransaction: function(transaction){ add transaction to callBuffer Array var me = this, enableBuffer = me.enableBuffer; if batching is enabled, create a task wait for 10ms for another if (transaction.form) { transaction to be added me.sendFormRequest(transaction); no more transactions coming in? Call return; combineAndSend } me.callBuffer.push(transaction); if (enableBuffer) { if (!me.callTask) { me.callTask = Ext.create('Ext.util.DelayedTask', me.combineAndSend, me); } me.callTask.delay(Ext.isNumber(enableBuffer) ? enableBuffer : 10); } else { me.combineAndSend(); } } RemotingProvider.js
Ext.direct.* w/ Zend Framework eu.sourcedevcon.provider.account.getEmailAccounts(); eu.sourcedevcon.provider.account.getFeedAccounts(); client.js Ext.Ajax.on('beforerequest', function(conn, options) { var trans = options.transaction; if (trans && trans.provider && trans.provider.type == 'zend') { var controller = options.transaction.action; controller = controller.replace(/([a-z])([A-Z])/g, "$1.$2").toLowerCase(); var action = options.transaction.method; action = action.replace(/([a-z])([A-Z])/g, "$1.$2").toLowerCase(); var module = options.url; var url = module + (module.lastIndexOf('/') == module.length-1 ? '' : '/') + controller + '/' + action; if (options.transaction.provider.format) { url += '/format/' + options.transaction.provider.format; } options.url = url; options.disableCaching = true; } }); client.js
Ext.direct.* w/ Zend Framework eu.sourcedevcon.provider.account.getEmailAccounts(); eu.sourcedevcon.provider.account.getFeedAccounts(); client.js Ext.Ajax.on('beforerequest', function(conn, options) { false var trans = options.transaction; if (trans && trans.provider && trans.provider.type == 'zend') { var controller = options.transaction.action; controller = controller.replace(/([a-z])([A-Z])/g, "$1.$2").toLowerCase(); var action = options.transaction.method; action = action.replace(/([a-z])([A-Z])/g, "$1.$2").toLowerCase(); var module = options.url; var url = module + (module.lastIndexOf('/') == module.length-1 ? '' : '/') + controller + '/' + action; if (options.transaction.provider.format) { url += '/format/' + options.transaction.provider.format; } options.url = url; options.disableCaching = true; } }); client.js
Ext.direct.* w/ Zend Framework eu.sourcedevcon.provider.account.getEmailAccounts(); eu.sourcedevcon.provider.account.getFeedAccounts(); client.js Ext.Ajax.on('beforerequest', function(conn, options) { false var trans = options.transaction; if (trans && trans.provider && trans.provider.type == 'zend') { var controller = options.transaction.action; controller = controller.replace(/([a-z])([A-Z])/g, "$1.$2").toLowerCase(); var action = options.transaction.method; ... client.js POST http://sourcedevcon/groupware extDirectData [{"action":"account","method":"getEmailAccounts","data":null,"type":"rpc","tid":1}, {"action":"account","method":"getFeedAccounts","data":null,"type":"rpc","tid":2}] Firebug
Ext.direct.* w/ Zend Framework ...and now for something completely different.
Ext.direct.* w/ Zend Framework Introducing Sourcedevcon_Controller_Plugin_ExtRequest[5]
Ext.direct.* w/ Zend Framework What is this Plugin? ● „Plugin“ for Zend Framework's Front Controller ● Actually not a real plugin, but rather a mediator for preDispatch/postDispatch Events ● Capable of detecting batched requests ● Capable of disassembling a batched request into individual requests ● Capable of creating a sandbox for each request, and processing it
Ext.direct.* w/ Zend Framework
Ext.direct.* w/ Zend Framework dispatchLoopStartup
Ext.direct.* w/ Zend Framework dispatchLoopStartup dispatchLoopShutdown
Ext.direct.* w/ Zend Framework pre Dispatch Action Dispatch Controller dispatchLoopStartup post dispatchLoopShutdown Dispatch Response Object
Ext.direct.* w/ Zend Framework $extDirect = new Sourcedevcon_Controller_Plugin_ExtRequest(array( 'extParameter' => 'extDirectData', 'additionalHeaders' => array('Content-Type' => 'application/json'), 'additionalParams' => array('format' => 'json'), 'action' => 'multi.request', 'controller' => 'ext', 'module' => 'default' )); $extDirect->registerPlugins(); index.php
Ext.direct.* w/ Zend Framework $extDirect = new Sourcedevcon_Controller_Plugin_ExtRequest(array( 'extParameter' => 'extDirectData', The POST parameter that holds a batched request 'additionalHeaders' => array('Content-Type' => 'application/json'), 'additionalParams' => array('format' => 'json'), 'action' => 'multi.request', 'controller' => 'ext', 'module' => 'default' )); $extDirect->registerPlugins(); index.php
Ext.direct.* w/ Zend Framework $extDirect = new Sourcedevcon_Controller_Plugin_ExtRequest(array( 'extParameter' => 'extDirectData', Additional headers for the 'additionalHeaders' => array('Content-Type' => 'application/json'), 'additionalParams' => array('format' => 'json'), response object 'action' => 'multi.request', 'controller' => 'ext', 'module' => 'default' )); $extDirect->registerPlugins(); index.php
Ext.direct.* w/ Zend Framework $extDirect = new Sourcedevcon_Controller_Plugin_ExtRequest(array( 'extParameter' => 'extDirectData', 'additionalHeaders' => array('Content-Type' => 'application/json'), 'additionalParams' => array('format' => Additional parameters we add 'json'), 'action' => 'multi.request', to each sandboxed request 'controller' => 'ext', 'module' => 'default' )); $extDirect->registerPlugins(); index.php
Ext.direct.* w/ Zend Framework $extDirect = new Sourcedevcon_Controller_Plugin_ExtRequest(array( 'extParameter' => 'extDirectData', 'additionalHeaders' => array('Content-Type' => 'application/json'), 'additionalParams' => array('format' => 'json'), 'action' => 'multi.request', 'controller' => 'ext', The default module/controller/action 'module' => 'default' before disassembling )); $extDirect->registerPlugins(); index.php
Ext.direct.* w/ Zend Framework $extDirect = new Sourcedevcon_Controller_Plugin_ExtRequest(array( 'extParameter' => 'extDirectData', 'additionalHeaders' => array('Content-Type' => 'application/json'), 'additionalParams' => array('format' => 'json'), 'action' => 'multi.request', 'controller' => 'ext', 'module' => 'default' )); $extDirect->registerPlugins(); Register the plugins at the Front Controller index.php
Ext.direct.* w/ Zend Framework $extDirect = new Sourcedevcon_Controller_Plugin_ExtRequest(array( 'extParameter' => 'extDirectData', 'additionalHeaders' => array('Content-Type' => 'application/json'), 'additionalParams' => array('format' => 'json'), 'action' => 'multi.request', 'controller' => 'ext', 'module' => 'default' )); $extDirect->registerPlugins(); index.php public function dispatchLoopStartup() { $this->_parent->notifyDispatchLoopStartup(); } PreDispatcher.php
Ext.direct.* w/ Zend Framework $extDirect = new Sourcedevcon_Controller_Plugin_ExtRequest(array( 'extParameter' => 'extDirectData', 'additionalHeaders' => array('Content-Type' => 'application/json'), 'additionalParams' => array('format' => 'json'), 'action' => 'multi.request', 'controller' => 'ext', 'module' => 'default' )); $extDirect->registerPlugins(); index.php public function dispatchLoopStartup() public function dispatchLoopShutdown() { { $this->_parent->notifyDispatchLoopStartup(); $this->_parent->notifyDispatchLoopShutdown(); } } PreDispatcher.php PostDispatcher.php
Ext.direct.* w/ Zend Framework $extDirect = new Sourcedevcon_Controller_Plugin_ExtRequest(array( 'extParameter' => 'extDirectData', 'additionalHeaders' => array('Content-Type' => 'application/json'), 'additionalParams' => array('format' => 'json'), 'action' => 'multi.request', 'controller' => 'ext', 'module' => 'default' )); $extDirect->registerPlugins(); index.php public function dispatchLoopStartup() public function dispatchLoopShutdown() { { $this->_parent->notifyDispatchLoopStartup(); $this->_parent->notifyDispatchLoopShutdown(); } } PreDispatcher.php PostDispatcher.php preDispatch dispatch postDispatch Request_Http Response_Http
Thank you Thank you! ... questions?
Resources [1] http://en.wikipedia.org/wiki/Continuous_integration [2] http://svnbook.red-bean.com/en/1.5/svn.advanced.vendorbr.html [3] http://zendframework.com/manual/en/zend.controller.actionhelpers.html [4] http://www.sencha.com/products/extjs/extdirect [5] http://thorsten.suckow-homberg.de/sourcedevcon [6] http://framework.zend.com/manual/en/zend.controller.basics.html

Zend Framework MVC driven ExtJS

  • 1.
    Zend Framework MVCdriven ExtJS Thorsten Suckow-Homberg, K&K GmbH Sourc{ - SenchaDev Conference May 5-7, 2011 Croatia, Split
  • 2.
    Who am I? ● Thorsten Suckow-Homberg ● Born 1976, Aachen, Germany ● Webdeveloper since 1999 ● Senior Software Developer for K&K GmbH, Aachen ● Focus on planning, architecture and deployment of web based software
  • 3.
    Who am I? …oh, and UI programming, of course: Author of: ● conjoon – http://www.conjoon.org ● Ext.ux.Livegrid – http://www.ext-livegrid.com ● various other open source ExtJS extensions/ components ● Numerous bug reports, rants and proposals over at the sencha forums :) (MindPatterns)
  • 4.
    This talk willshow you... ● … how you should plan your directory layout in larger projects
  • 5.
    This talk willshow you... ● … how you should plan your directory layout in larger projects ● … what is application context - and how to use it to your advantage
  • 6.
    This talk willshow you... ● … how you should plan your directory layout in larger projects ● … what is application context - and how to use it to your advantage ● … how to use Ext.Direct with existing ZF backend code
  • 7.
    This talk willshow you... ● … how you should plan your directory layout in larger projects ● … what is application context - and how to use it to your advantage ● … how to use Ext.Direct with existing ZF backend code ● In short: ...why Zend Framework could become the framework of your choice when combining Ext and PHP
  • 8.
  • 9.
    The Basics –Directory Layout ● Top level should be a directory named after your project (obviously) ● ...containing three child directories:
  • 10.
    The Basics –Directory Layout build-tools ● Real tools, like: ● yuicompressor ● phing ● ant
  • 11.
    The Basics –Directory Layout build-tools ● Real tools, like: ● yuicompressor ● phing ● ant ● (XML-)build scripts ● code sanity ● tests ● deployment ● etc... in short: Continuous Integration[1]!
  • 12.
    The Basics –Directory Layout vendor ● All the third party libs you're using in your code ● ExtJS ● ZendFramework ● etc.
  • 13.
    The Basics –Directory Layout vendor ● All the third party libs you're using in your code ● ExtJS ● ZendFramework ● etc. Note: ● files from separate repository vendor branch will be merged into this directory ● Best case: developers will not touch the vendor directory ● Read [2] for more infos on how to use vendor branches
  • 14.
    The Basics –Directory Layout src
  • 15.
    The Basics –Directory Layout src ● Everything you're actually coding
  • 16.
    The Basics –Directory Layout src ● Everything you're actually coding: ● overrides ● extensions ● backend code ● code that might refer to vendor code ● … in short: all application-specific code your team writes
  • 17.
    The Basics –Directory Layout corelib
  • 18.
    The Basics –Directory Layout corelib ● js – your client libraries (ExtJS, own implementations)
  • 19.
    The Basics –Directory Layout corelib ● js – your client libraries (ExtJS, own implementations) ● php – your backend code (including tests)
  • 20.
    The Basics –Directory Layout datastore ● data storage definition/structure goes here
  • 21.
    The Basics –Directory Layout datastore ● data storage definition/structure goes here Note: ● build scripts can refer to the structure file when deploying an application
  • 22.
    The Basics –Directory Layout www ● one step more towards a „callable“ application
  • 23.
    The Basics –Directory Layout application
  • 24.
    The Basics –Directory Layout application ● ZF specific „frontend“ code (controllers, templates)
  • 25.
    The Basics –Directory Layout application ● ZF specific „frontend“ code (controllers, templates) ● and: – Meta-Information files for your application – caching directories – etc.
  • 26.
    The Basics –Directory Layout htdocs
  • 27.
    The Basics –Directory Layout htdocs ● Finally! The document root for your application ● the only „public“ folder
  • 28.
    The Basics –Directory Layout htdocs ● Finally! The document root for your application ● the only „public“ folder Note: ● Build-process focuses on this folder
  • 29.
    The Basics –Directory Layout „This layout gets way too complex!“ Don't go berserk! There's a solution to all of it!
  • 30.
    The Basics –Directory Layout „Do I need to run a build process every time a line of code changed?“ „How do I get the resources from vendor into my src folder where – obviously - running code will resist?“
  • 31.
    The Basics –Directory Layout working copy (development): /var/www/your_project/vendor/ext-ux-util-messagebus /var/www/your_project/src/www/htdocs/index.php
  • 32.
    The Basics –Directory Layout working copy (development): /var/www/your_project/vendor/ext-ux-util-messagebus /var/www/your_project/src/www/htdocs/index.php <?php if ($isDeployment) { ?> <script type="text/javascript" src="/js/ext-ux-util-messagebus/src/messagebus.js"></script> <?php } else { ?> <script type="text/javascript" src="/vendor/ext-ux-util-messagebus/src/messagebus.js"></script> <?php } ?> index.phtml
  • 33.
    The Basics –Directory Layout working copy (development): /var/www/your_project/vendor/ext-ux-util-messagebus /var/www/your_project/src/www/htdocs/index.php <?php if ($isDeployment) { ?> <script type="text/javascript" src="/js/ext-ux-util-messagebus/src/messagebus.js"></script> <?php } else { ?> <script type="text/javascript" src="/vendor/ext-ux-util-messagebus/src/messagebus.js"></script> <?php } ?> index.phtml # Alias for Ext.ux.util.MessageBus Alias /js/ext-ux-util-messagebus "/htdocs/your_project/vendor/ext-ux-util-messagebus/src" <Directory "/htdocs/your_project/vendor/ext-ux-util-messagebus/src"> Order allow,deny Allow from all </Directory> apache.conf
  • 34.
    The Basics –Directory Layout working copy (development): /var/www/your_project/vendor/ext-ux-util-messagebus /var/www/your_project/src/www/htdocs/index.php <?php if ($isDeployment) { ?> <script type="text/javascript" src="/js/ext-ux-util-messagebus/src/messagebus.js"></script> <?php } else { ?> <script type="text/javascript" src="/vendor/ext-ux-util-messagebus/src/messagebus.js"></script> <?php } ?> index.phtml # Alias for Ext.ux.util.MessageBus Alias /js/ext-ux-util-messagebus "/htdocs/your_project/vendor/ext-ux-util-messagebus/src" <Directory "/htdocs/your_project/vendor/ext-ux-util-messagebus/src"> Order allow,deny Allow from all </Directory> apache.conf <script type="text/javascript" src="/js/ext-ux-util-messagebus/src/messagebus.js"></script> index.phtml
  • 35.
    The Basics –Directory Layout working copy (development): /var/www/your_project/vendor/ext-ux-util-messagebus /var/www/your_project/src/www/htdocs/index.php # Alias for Ext.ux.util.MessageBus Development: Alias /js/ext-ux-util-messagebus Virtual
  • 36.
    The Basics –Directory Layout working copy (development): /var/www/your_project/vendor/ext-ux-util-messagebus /var/www/your_project/src/www/htdocs/index.php # Alias for Ext.ux.util.MessageBus Development: Alias /js/ext-ux-util-messagebus Virtual Build: ... <target name="build_js"> <delete dir="./build/js/ext-ux-util-messagebus" /> <copy todir="./build/js/ext-ux-util-messagebus" includeemptydirs="true"> <fileset dir="./vendor/ext-ux-util-messagebus"> <exclude name="**/.svn" /> </fileset> </copy> </target> ... Build process
  • 37.
    The Basics –Directory Layout working copy (development): /var/www/your_project/vendor/ext-ux-util-messagebus /var/www/your_project/src/www/htdocs/index.php # Alias for Ext.ux.util.MessageBus Development: Alias /js/ext-ux-util-messagebus Virtual Build: ... <target name="build_js"> <delete dir="./build/js/ext-ux-util-messagebus" /> <copy todir="./build/js/ext-ux-util-messagebus" includeemptydirs="true"> <fileset dir="./vendor/ext-ux-util-messagebus"> <exclude name="**/.svn" /> </fileset> </copy> </target> ... Build process <script type="text/javascript" src="/js/ext-ux-util-messagebus/src/messagebus.js"></script> index.phtml
  • 38.
    The Basics –Directory Layout Cons ● will need some webserver configuration to get things working ● build-files depend on initial layout, changes mean adjustment at several locations at once ● needs documentation for your dev team ● users need to know how to set up their dev environment when they check out „including sources“
  • 39.
    The Basics –Directory Layout Pros ● No symlinks –> not f***ing up the repository ● Code structured after purpose ● No need to run builds after you've changed one line of code (except for tests, of course) ● Clean approach towards build- and development-code ● Makes build-definitions easy due to strictly defined layout Advice: ● Use svn.ignore and template files! - it will help you and you're coworkers to set up things on different machines
  • 40.
    Context based data What is a „context“?
  • 41.
    Context based data Application Server
  • 42.
    Context based data Application Server send/receive Desktop PC
  • 43.
    Context based data Application Server send/receive send/receive Desktop PC Mobile
  • 44.
    Context based data Application Server send/receive send/receive Desktop PC Mobile Context „default“
  • 45.
    Context based data Application Server send/receive send/receive Desktop PC Mobile Context „default“ Context „mobile“
  • 46.
    Context based data Why do we need it?
  • 47.
    Context based data ● Different devices need different views ● Content delivery optimizations ● One domain serves all (www.senchadevcon.eu vs. m.senchadevcon.eu) ● Specific data format (send/receive) might not be available on devices used by our users
  • 48.
    Context based data What do we need?
  • 49.
    Context based data classZend_Controller_Action_Helper_ContextSwitch ContextSwitch.php
  • 50.
    Context based data classZend_Controller_Action_Helper_ContextSwitch ContextSwitch.php ● Action helper that will detect context based requests ● Capable of sending specially formatted responses based on detected context and configuration ● Available as a default action helper provided by Zend Framework ● For more informations on how to use action helper, see [3]
  • 51.
    Context based data How does it work?
  • 52.
    Context based data- detection We can define a context based on (virtually) all data that's available during runtime!
  • 53.
    Context based data- detection We can define a context based on (virtually) all data that's available during runtime! For example: http://myproject.com/user/reception/get.user/format/json url
  • 54.
    Context based data- detection We can define a context based on (virtually) all data that's available during runtime! For example: http://myproject.com/user/reception/get.user/format/json url module
  • 55.
    Context based data- detection We can define a context based on (virtually) all data that's available during runtime! For example: http://myproject.com/user/reception/get.user/format/json url controller
  • 56.
    Context based data- detection We can define a context based on (virtually) all data that's available during runtime! For example: http://myproject.com/user/reception/get.user/format/json url action
  • 57.
    Context based data- detection We can define a context based on (virtually) all data that's available during runtime! For example: http://myproject.com/user/reception/get.user/format/json url parameter/value pair
  • 58.
    Context based data- detection We can define a context based on (virtually) all data that's available during runtime! For example: http://myproject.com/user/reception/get.user/format/json url $_GET['format'] == 'json'
  • 59.
    Context based data- detection We can define a context based on (virtually) all data that's available during runtime! For example: http://myproject.com/user/reception/get.user/format/json url $_GET['format'] == 'json' ● Application is now in „json“ context – i.e., return all responses json formatted, since the client told us so!
  • 60.
    Context based data- detection We can define a context based on (virtually) all data that's available during runtime! For example: http://myproject.com/user/reception/get.user/format/json url $_GET['format'] == 'json' ● Application is now in „json“ context – i.e., return all responses json formatted, since the client told us so! ● In fact, this is a default option coming with the ContextSwitch action helper
  • 61.
    Context based data- detection We can define a context based on (virtually) all data that's available during runtime! Another example: ... $userAgent = strtolower($_SERVER['HTTP_USER_AGENT']); // request coming from ipad? if (strpos($userAgent, 'ipad') !== false) { $this->currentContext = 'ipad'; // request coming from android? } else if (strpos($userAgent), 'android') !== false) { $this->currentContext = 'android'; } ... User Agent ● Context set by detection, not manually enforced by parameters
  • 62.
    Context based data Sowhat can it do for me?
  • 63.
    Context based data ● Detect devices/users (webbrowser, mobile, bots)
  • 64.
    Context based data ● Detect devices/users (webbrowser, mobile, bots) ● One codebase for all devices: „Write once, run anywhere“
  • 65.
    Context based data ● Detect devices/users (webbrowser, mobile, bots) ● One codebase for all devices: „Write once, run anywhere“
  • 66.
    Context based data ● Detect devices/users (webbrowser, mobile, bots) ● One codebase for all devices: „Write once, run anywhere“
  • 67.
    Context based data ● Detect devices/users (webbrowser, mobile, bots) ● One codebase for all devices:„Write once, run anywhere“ ● View variables will either be assigned to templates (plain html mixed with PHP for example) or transformed to json (xml etc.) – automatically – no need to implement special logic as long as your client can handle the response ● Requesting different formats is often just a thing of switching a parameter at client site ● Makes even delivering views from the backend very easy! ● Test a context by switching a parameter ● Only views? No, different business logic depending on the context, too!
  • 68.
    Context based data- example class User_ReceptionController extends Zend_Controller_Action() { public function init() { // define the actions that should consider different contexts $this->_helper->contextSwitch() ->addActionContext('get.user', 'json') ->initContext(); } /** * This method will return user data to the client as requested. */ public function getUserAction() { ... $this->view->userName = 'Peter Griffin'; $this->view->userEmail = 'peter@birdistheword.com'; } } ReceptionController.php
  • 69.
    Context based data- example class User_ReceptionController extends Zend_Controller_Action() { public function init() { // define the actions that should consider different contexts $this->_helper->contextSwitch() ->addActionContext('get.user', 'json') implement parent's init() method ->initContext(); } to add action contexts... /** * This method will return user data to the client as requested. */ public function getUserAction() { ... $this->view->userName = 'Peter Griffin'; $this->view->userEmail = 'peter@birdistheword.com'; } } ReceptionController.php
  • 70.
    Context based data- example class User_ReceptionController extends Zend_Controller_Action() { public function init() { // define the actions that should consider different contexts $this->_helper->contextSwitch() ->addActionContext('get.user', 'json') ->initContext(); } /** * This method will return user data to the client as requested. ...which happens here: */ public function getUserAction() { ... $this->view->userName = 'Peter Griffin'; $this->view->userEmail = 'peter@birdistheword.com'; } } ReceptionController.php
  • 71.
    Context based data- example class User_ReceptionController extends Zend_Controller_Action() { public function init() { // define the actions that should consider different contexts $this->_helper->contextSwitch() ->addActionContext('get.user', 'json') ->initContext(); } /** * This method will return user data to the client as requested. getUserAction() will now... */ public function getUserAction() { ... $this->view->userName = 'Peter Griffin'; $this->view->userEmail = 'peter@birdistheword.com'; } } ReceptionController.php
  • 72.
    Context based data- example class User_ReceptionController extends Zend_Controller_Action() { public function init() { // define the actions that should consider different contexts $this->_helper->contextSwitch() ->addActionContext('get.user', 'json') ->initContext(); } /** * This method will return user data response json-formatted if … return the to the client as requested. */ the get-Parameter „format“ was set to „json“ public function getUserAction() { ... $this->view->userName = 'Peter Griffin'; $this->view->userEmail = 'peter@birdistheword.com'; } } ReceptionController.php
  • 73.
    Context based data- example class User_ReceptionController extends Zend_Controller_Action() { public function init() { // define the actions that should consider different contexts $this->_helper->contextSwitch() ->addActionContext('get.user', 'json') ->initContext(); } /** * This method will return user data to the client as requested. */ public function getUserAction() { ... $this->view->userName = 'Peter Griffin'; $this->view->userEmail = 'peter@birdistheword.com'; } We have just added an action context to this action } ReceptionController.php
  • 74.
    Context based data- example class User_ReceptionController extends Zend_Controller_Action() { public function init() { // define the actions that should consider different contexts $this->_helper->contextSwitch() ->addActionContext('get.user', 'json') ->initContext(); } /** * This method will return user data to the client as requested. */ public function getUserAction() { ... $this->view->userName = 'Peter Griffin'; $this->view->userEmail = 'peter@birdistheword.com'; } We have just added an action context to this action } ReceptionController.php
  • 75.
    Context based data- example class User_ReceptionController extends Zend_Controller_Action() { public function init() { // define the actions that should consider different contexts $this->_helper->contextSwitch() ->addActionContext('get.user', 'json') ->initContext(); } /** * This method will return user data to the client as requested. */ Call business logic... public function getUserAction() { ... $this->view->userName = 'Peter Griffin'; $this->view->userEmail = 'peter@birdistheword.com'; } } ReceptionController.php
  • 76.
    Context based data- example class User_ReceptionController extends Zend_Controller_Action() { public function init() { // define the actions that should consider different contexts $this->_helper->contextSwitch() ->addActionContext('get.user', 'json') ->initContext(); } /** * This method will return user data to the client as requested. */ And finally: Assign values to the public function getUserAction() controller's view { ... $this->view->userName = 'Peter Griffin'; $this->view->userEmail = 'peter@birdistheword.com'; } } ReceptionController.php
  • 77.
    Context based data- example class User_ReceptionController extends Zend_Controller_Action() { ... public function getUserAction() { ... $this->view->userName = 'Peter Griffin'; $this->view->userEmail = 'peter@birdistheword.com'; } } ReceptionController.php Ext.Ajax.request({ url : „/user/reception/get.user/format/json“, success : function(response) { console.log(response.responseText); } }); client.js
  • 78.
    Context based data- example class User_ReceptionController extends Zend_Controller_Action() { ... public function getUserAction() { ... $this->view->userName = 'Peter Griffin'; $this->view->userEmail = 'peter@birdistheword.com'; } Module } ReceptionController.php Ext.Ajax.request({ url : „/user/reception/get.user/format/json“, success : function(response) { console.log(response.responseText); } }); client.js
  • 79.
    Context based data- example class User_ReceptionController extends Zend_Controller_Action() { ... public function getUserAction() { ... $this->view->userName = 'Peter Griffin'; $this->view->userEmail = 'peter@birdistheword.com'; } Controller } ReceptionController.php Ext.Ajax.request({ url : „/user/reception/get.user/format/json“, success : function(response) { console.log(response.responseText); } }); client.js
  • 80.
    Context based data- example class User_ReceptionController extends Zend_Controller_Action() { ... public function getUserAction() { ... $this->view->userName = 'Peter Griffin'; $this->view->userEmail = 'peter@birdistheword.com'; } Action } ReceptionController.php Ext.Ajax.request({ url : „/user/reception/get.user/format/json“, success : function(response) { console.log(response.responseText); } }); client.js
  • 81.
    Context based data- example Note ● This illustration explains how a request gets processed by Zend Framework [6] ● We'll use it to show how ContextSwitch changes the ResponseObject based on the application's context
  • 82.
    Context based data- example Request Client invokes request
  • 83.
    Context based data- example Request Router Zend Framework routes to User_ReceptionController::getUserAction()
  • 84.
    Context based data- example Request Router pre Any plugins defined? Dispatch Signal a preDispatch to them!
  • 85.
    Context based data- example Request Router pre Any plugins defined? Dispatch Signal a preDispatch to them! public function preDispatch() { } Zend_Controller_Action_Helper_Abstract
  • 86.
    Context based data- example Request Router pre Dispatch the action – Dispatch getUserAction() gets processed! Dispatch
  • 87.
    Context based data- example Request Router pre Dispatch Dispatch Action Controller public function getUserAction() { ... $this->view->userName = 'Peter Griffin'; $this->view->userEmail = 'peter@birdistheword.com'; } ReceptionController.php
  • 88.
    Context based data- example Request Router pre Dispatch Action Dispatch Controller Any plugins defined? post Signal a postDispatch to them! Dispatch
  • 89.
    Context based data- example Request Router pre Dispatch Action Dispatch Controller Any plugins defined? post Signal a postDispatch to them! Dispatch public function postDispatch() { $context = $this->getCurrentContext(); ... //$this->postJsonContext(); } ContextSwitch.php
  • 90.
    Context based data- example Request Router pre Dispatch Action Dispatch Controller post Dispatch $response->setHeader('Content-Type', 'application/json'); ContextSwitch.php actions left? Response Object
  • 91.
    Context based data- example Request Router pre Dispatch Action Dispatch Controller post Dispatch $response->setHeader('Content-Type', 'application/json'); ContextSwitch.php actions left? $response->setBody(Zend_Json::encode($view->getVars())); ContextSwitch.php Response Object
  • 92.
    Context based data- example Request Router pre Dispatch Action Dispatch Controller post Dispatch actions left? send response Response Object
  • 93.
    Context based data- example Request Router pre Dispatch Action Dispatch Controller post Dispatch actions left? {'userName' : „Peter Griffin“, 'userEmail' : „peter@birdistheword.com“} console send response Response Object
  • 94.
    Context based data AWESOME! This makes coding a real blast!
  • 96.
    Skeptikal Hippo It makessense when working with Ext 1.* and 2.*, but what about Ext.direct.*?
  • 97.
  • 98.
    Ext.direct.* „Ext Directis a platform and language agnostic technology to remote server-side methods to the client-side.“[4]
  • 99.
    Ext.direct.* ● That new kid in class no one wants to play with
  • 100.
    Ext.direct.* ● That new kid in class no one wants to play with ● Bugs
  • 101.
    Ext.direct.* ● That new kid in class no one wants to play with ● Bugs ● Clumsy
  • 102.
    Ext.direct.* ● That new kid in class no one wants to play with ● Bugs ● Clumsy ● Implementation too complex
  • 103.
    Ext.direct.* ● That new kid in class no one wants to play with ● Bugs ● Clumsy ● Implementation too complex ● The bad thing: – Sencha started to focus remote functionality in their library on Ext.direct.* with 3.*
  • 104.
    Ext.direct.* ● That new kid in class no one wants to play with ● Bugs ● Clumsy ● Implementation too complex ● The bad thing: – Sencha started to focus remote functionality in their library on Ext.direct.* with 3.* ● The good thing: – Sencha eliminated almost all implementation issues
  • 105.
    Ext.direct.* - advantages ● Let's you call remote procedures directly from client code class User_ReceptionController extends Zend_Controller_Action { public function getUserAction() { ... } } ReceptionController.php
  • 106.
    Ext.direct.* - advantages ● Let's you call remote procedures directly from client code class User_ReceptionController extends Zend_Controller_Action { public function getUserAction() { ... } } ReceptionController.php user.reception.getUser(); client.js
  • 107.
    Ext.direct.* - advantages ● Let's you call remote procedures directly from client code class User_ReceptionController extends Zend_Controller_Action { public function getUserAction() { ... } } ReceptionController.php module user.reception.getUser(); client.js
  • 108.
    Ext.direct.* - advantages ● Let's you call remote procedures directly from client code class User_ReceptionController extends Zend_Controller_Action { public function getUserAction() { ... } } ReceptionController.php controller user.reception.getUser(); client.js
  • 109.
    Ext.direct.* - advantages ● Let's you call remote procedures directly from client code class User_ReceptionController extends Zend_Controller_Action { public function getUserAction() { ... } } ReceptionController.php action user.reception.getUser(); client.js
  • 110.
    Ext.direct.* - advantages ● Can stack remote procedure calls and send them as one „batched request“ class Groupware_AccountController extends Zend_Controller_Action { public function getEmailAccountsAction(){ ... } public function getFeedAccountsAction(){ ... } } GroupwareController.php
  • 111.
    Ext.direct.* - advantages ● Can stack remote procedure calls and send them as one „batched request“ class Groupware_AccountController extends Zend_Controller_Action { public function getEmailAccountsAction(){ ... } public function getFeedAccountsAction(){ ... } } GroupwareController.php groupware.account.getEmailAccounts(); client.js
  • 112.
    Ext.direct.* - advantages ● Can stack remote procedure calls and send them as one „batched request“ class Groupware_AccountController extends Zend_Controller_Action { public function getEmailAccountsAction(){ ... } public function getFeedAccountsAction(){ ... } } GroupwareController.php groupware.account.getEmailAccounts(); client.js
  • 113.
    Ext.direct.* - advantages ● Can stack remote procedure calls and send them as one „batched request“ class Groupware_AccountController extends Zend_Controller_Action { public function getEmailAccountsAction(){ ... } public function getFeedAccountsAction(){ ... } } GroupwareController.php groupware.account.getEmailAccounts(); groupware.account.getFeedAccounts(); client.js Firebug
  • 114.
    Ext.direct.* - advantages ● Can stack remote procedure calls and send them as one „batched request“ class Groupware_AccountController extends Zend_Controller_Action { public function getEmailAccountsAction(){ ... } public function getFeedAccountsAction(){ ... } } GroupwareController.php groupware.account.getEmailAccounts(); groupware.account.getFeedAccounts(); client.js POST http://sourcedevcon/groupware/account/get.email.accounts POST http://sourcedevcon/groupware/account/get.feed.accounts Firebug
  • 115.
    Ext.direct.* - advantages ● Can stack remote procedure calls and send them as one „batched request“ class Groupware_AccountController extends Zend_Controller_Action { public function getEmailAccountsAction(){ ... } public function getFeedAccountsAction(){ ... } } GroupwareController.php groupware.account.getEmailAccounts(); groupware.account.getFeedAccounts(); client.js POST http://sourcedevcon/groupware/account/get.email.accounts POST http://sourcedevcon/groupware/account/get.feed.accounts Firebug POST http://sourcedevcon/groupware Firebug
  • 116.
    Ext.direct.* - advantages ● Can stack remote procedure calls and send them as one „batched request“ class Groupware_AccountController extends Zend_Controller_Action { public function getEmailAccountsAction(){ ... } public function getFeedAccountsAction(){ ... } } GroupwareController.php groupware.account.getEmailAccounts(); groupware.account.getFeedAccounts(); client.js POST http://sourcedevcon/groupware/account/get.email.accounts POST http://sourcedevcon/groupware/account/get.feed.accounts Firebug POST http://sourcedevcon/groupware extDirectData [{"action":"account","method":"getEmailAccounts","data":null,"type":"rpc","tid":1}, {"action":"account","method":"getFeedAccounts","data":null,"type":"rpc","tid":2}] Firebug
  • 117.
  • 118.
    Ext.direct.* ● Can reduce serverload (1 request with n calls to specific actions instead of n calls)
  • 119.
    Ext.direct.* ● Can reduce serverload (1 request with n calls to specific actions instead of n calls) ● Reduces possibility of losing connections (http allows for n concurrent connections to one domain at a time)
  • 120.
    Ext.direct.* ● Can reduce serverload (1 request with n calls to specific actions instead of n calls) ● Reduces possibility of losing connections (http allows for n concurrent connections to one domain at a time) This alone are tremendously important reasons for you to go ahead and make friendship with that new kid in class!
  • 121.
    Ext.direct.* w/ ZendFramework Unfortunately, not supported out of the box :(
  • 122.
    Ext.direct.* w/ ZendFramework … so let's find a solution. Let's sum up our goals:
  • 123.
    Ext.direct.* w/ ZendFramework ● Switch client code to Ext.direct.* without having to change backend source code ● Add another tier of complexity and automatically create backend methods/client code. Tests, changing naming conventions etc. have to be added by hand then. ● Keep our backend testable, reusable and make sure it runs with any other client library/ implementation
  • 124.
    Ext.direct.* w/ ZendFramework ● Switch client code to Ext.direct.* without having to change backend source code ● Add another tier of complexity and automatically create backend methods/client code. Tests, changing naming conventions etc. have to be added by hand then. ● Keep our backend testable, reusable and make sure it runs with any other client library/ implementation
  • 125.
    Ext.direct.* w/ ZendFramework ● Switch client code to Ext.direct.* without having to change backend source code ● Add another tier of complexity and automatically create backend methods/client code. Tests, changing naming conventions etc. have to be added by hand then. ● Keep our backend testable, reusable and make sure it runs with any other client library/ implementation
  • 126.
    Ext.direct.* w/ ZendFramework ● Switch client code to Ext.direct.* without having to change backend source code ● Add another tier of complexity and automatically create backend methods/client code. Tests, changing naming conventions etc. have to be added by hand then. ● Keep our backend testable, reusable and make sure it runs with any other client library/ implementation
  • 127.
    Ext.direct.* w/ ZendFramework What we need to do:
  • 128.
    Ext.direct.* w/ ZendFramework – Show requests sent by Ext.direct.* their way through Zend Framework to their module/controller/action – Teach Zend Framework how to distinguish between old fashioned and merged requests – Show Zend Framework how to disassemble 1 request into n requests – Give Zend Framework a sandbox where it can work/play/you name it with a disamssembled request – Collect sandboxed requests and merge them back into one response
  • 129.
    Ext.direct.* w/ ZendFramework Change Ext.direct.RemotingProvider URLs on the fly
  • 130.
    Ext.direct.* w/ ZendFramework Remember? The Ext.direct.RemotingProvider exposes access to server side methods on the client. By mapping those remote methods to the client, there is almost no need anymore to spray URLs around the source like crazy. It establishes a connection between the client and the backend by providing a programmer friendly interface.
  • 131.
    Ext.direct.* w/ ZendFramework Ext.direct.Manager.addProvider({ id : 'eu.sourcedevcon.provider', enableUrlEncode : 'extDirectData', url : './groupware', format : 'json', type : 'zend', actions : { account : [{ name : 'getFeedAccounts' }, { name : 'getEmailAccounts' }] }, namespace : 'eu.sourcedevcon.provider' }); client.js
  • 132.
    Ext.direct.* w/ ZendFramework Ext.direct.Manager.addProvider({ id : 'eu.sourcedevcon.provider', enableUrlEncode : 'extDirectData', url : './groupware', URL format : 'json', type : 'zend', actions : { account : [{ name : 'getFeedAccounts' }, { name : 'getEmailAccounts' }] }, namespace : 'eu.sourcedevcon.provider' }); client.js
  • 133.
    Ext.direct.* w/ ZendFramework Ext.direct.Manager.addProvider({ id : 'eu.sourcedevcon.provider', enableUrlEncode : 'extDirectData', url : './groupware', URL format : 'json', type : 'zend', actions : { account : [{ „action“ name : 'getFeedAccounts' }, { name : 'getEmailAccounts' }] }, namespace : 'eu.sourcedevcon.provider' }); client.js
  • 134.
    Ext.direct.* w/ ZendFramework Ext.direct.Manager.addProvider({ id : 'eu.sourcedevcon.provider', enableUrlEncode : 'extDirectData', url : './groupware', URL format : 'json', type : 'zend', actions : { account : [{ „action“ name : 'getFeedAccounts' }, { name : 'getEmailAccounts' method }] }, namespace : 'eu.sourcedevcon.provider' }); client.js
  • 135.
    Ext.direct.* w/ ZendFramework Ext.direct.Manager.addProvider({ id : 'eu.sourcedevcon.provider', enableUrlEncode : 'extDirectData', url : './groupware', format : 'json', type : 'zend', actions : { account : [{ name : 'getFeedAccounts' }, { name : 'getEmailAccounts' }] }, namespace : 'eu.sourcedevcon.provider' }); client.js eu.sourcedevcon.provider.account.getEmailAccounts(); client.js
  • 136.
    Ext.direct.* w/ ZendFramework Ext.direct.Manager.addProvider({ id : 'eu.sourcedevcon.provider', enableUrlEncode : 'extDirectData', url : './groupware', format : 'json', type : 'zend', actions : { account : [{ name : 'getFeedAccounts' }, { name : 'getEmailAccounts' }] }, namespace : 'eu.sourcedevcon.provider' }); namespace client.js eu.sourcedevcon.provider.account.getEmailAccounts(); client.js
  • 137.
    Ext.direct.* w/ ZendFramework Ext.direct.Manager.addProvider({ id : 'eu.sourcedevcon.provider', enableUrlEncode : 'extDirectData', url : './groupware', format : 'json', type : 'zend', actions : { account : [{ name : 'getFeedAccounts' }, { name : 'getEmailAccounts' }] action }, namespace : 'eu.sourcedevcon.provider' }); client.js eu.sourcedevcon.provider.account.getEmailAccounts(); client.js
  • 138.
    Ext.direct.* w/ ZendFramework Ext.direct.Manager.addProvider({ id : 'eu.sourcedevcon.provider', enableUrlEncode : 'extDirectData', url : './groupware', format : 'json', type : 'zend', actions : { account : [{ name : 'getFeedAccounts' }, { name : 'getEmailAccounts' }] }, namespace : 'eu.sourcedevcon.provider' method }); client.js eu.sourcedevcon.provider.account.getEmailAccounts(); client.js
  • 139.
    Ext.direct.* w/ ZendFramework Ext.direct.Manager.addProvider({ id : 'eu.sourcedevcon.provider', enableUrlEncode : 'extDirectData', url : './groupware', format : 'json', type : 'zend', actions : { account : [{ name : 'getFeedAccounts' }, { name : 'getEmailAccounts' }] }, namespace : 'eu.sourcedevcon.provider' }); client.js eu.sourcedevcon.provider.account.getEmailAccounts(); client.js POST http://sourcedevcon/groupware Firebug
  • 140.
    Ext.direct.* w/ ZendFramework Ext.direct.Manager.addProvider({ id : 'eu.sourcedevcon.provider', enableUrlEncode : 'extDirectData', url : './groupware', format : 'json', type : 'zend', actions : { account : [{ name : 'getFeedAccounts' }, { name : 'getEmailAccounts' }] }, namespace : 'eu.sourcedevcon.provider' }); client.js eu.sourcedevcon.provider.account.getEmailAccounts(); client.js POST http://sourcedevcon/groupware extDirectData {"action":"account","method":"getEmailAccounts","data":null,"type":"rpc","tid":1} Firebug
  • 141.
    Ext.direct.* w/ ZendFramework Ext.direct.Manager.addProvider({ id : 'eu.sourcedevcon.provider', enableUrlEncode : 'extDirectData', url : './groupware', format : 'json', type : 'zend', actions : { account : [{ name : 'getFeedAccounts' }, { name : 'getEmailAccounts' }] }, namespace : 'eu.sourcedevcon.provider' }); client.js eu.sourcedevcon.provider.account.getEmailAccounts(); client.js POST http://sourcedevcon/groupware extDirectData {"action":"account","method":"getEmailAccounts","data":null,"type":"rpc","tid":1} Firebug
  • 142.
    Ext.direct.* w/ ZendFramework Ext.direct.Manager.addProvider({ id : 'eu.sourcedevcon.provider', enableUrlEncode : 'extDirectData', url : './groupware', format : 'json', type : 'zend', actions : { account : [{ name : 'getFeedAccounts' }, { name : 'getEmailAccounts' }] }, namespace : 'eu.sourcedevcon.provider' }); client.js eu.sourcedevcon.provider.account.getEmailAccounts(); client.js POST http://sourcedevcon/groupware extDirectData {"action":"account","method":"getEmailAccounts","data":null,"type":"rpc","tid":1} Firebug
  • 143.
    Ext.direct.* w/ ZendFramework Ext.direct.Manager.addProvider({ id : 'eu.sourcedevcon.provider', enableUrlEncode : 'extDirectData', url : './groupware', format : 'json', type : 'zend', actions : { account : [{ name : 'getFeedAccounts' }, { name : 'getEmailAccounts' }] }, namespace : 'eu.sourcedevcon.provider' }); client.js eu.sourcedevcon.provider.account.getEmailAccounts(); client.js POST http://sourcedevcon/groupware extDirectData {"action":"account","method":"getEmailAccounts","data":null,"type":"rpc","tid":1} Firebug
  • 144.
    Ext.direct.* w/ ZendFramework Ext.direct.Manager.addProvider({ id : 'eu.sourcedevcon.provider', enableUrlEncode : 'extDirectData', url : './groupware', format : 'json', type : 'zend', actions : { account : [{ name : 'getFeedAccounts' }, { name : 'getEmailAccounts' }] }, namespace : 'eu.sourcedevcon.provider' }); client.js eu.sourcedevcon.provider.account.getEmailAccounts(); client.js POST http://sourcedevcon/groupware extDirectData {"action":"account","method":"getEmailAccounts","data":null,"type":"rpc","tid":1} Firebug
  • 145.
    Ext.direct.* w/ ZendFramework Ext.direct.Manager.addProvider({ id : 'eu.sourcedevcon.provider', enableUrlEncode : 'extDirectData', url : './groupware', format : 'json', type : 'zend', actions : { account : [{ name : 'getFeedAccounts' }, { name : 'getEmailAccounts' }] }, namespace : 'eu.sourcedevcon.provider' }); client.js eu.sourcedevcon.provider.account.getEmailAccounts(); client.js POST http://sourcedevcon/groupware extDirectData {"action":"account","method":"getEmailAccounts","data":null,"type":"rpc","tid":1} Firebug if (isset($_POST['extDirectData'])) { ... } /groupware Firebug
  • 146.
    Ext.direct.* w/ ZendFramework Ext.direct.Manager.addProvider({ id : 'eu.sourcedevcon.provider', enableUrlEncode : 'extDirectData', url : './groupware', format : 'json', type : 'zend', actions : { account : [{ name : 'getFeedAccounts' }, { name : 'getEmailAccounts' }] }, namespace : 'eu.sourcedevcon.provider' }); client.js eu.sourcedevcon.provider.account.getEmailAccounts(); client.js POST http://sourcedevcon/groupware extDirectData {"action":"account","method":"getEmailAccounts","data":null,"type":"rpc","tid":1} Firebug if (isset($_POST['extDirectData'])) { ... } /groupware Firebug
  • 147.
    Ext.direct.* w/ ZendFramework Ext.direct.Manager.addProvider({ id : 'eu.sourcedevcon.provider', enableUrlEncode : 'extDirectData', url : './groupware', format : 'json', type : 'zend', actions : { account : [{ name : 'getFeedAccounts' }, { name : 'getEmailAccounts' }] }, namespace : 'eu.sourcedevcon.provider' }); client.js eu.sourcedevcon.provider.account.getEmailAccounts(); client.js POST http://sourcedevcon/groupware extDirectData {"action":"account","method":"getEmailAccounts","data":null,"type":"rpc","tid":1} Firebug if (isset($_POST['extDirectData'])) { http://sourcedevcon/groupware/account/get.email.accounts ... } /groupware Firebug
  • 148.
    Ext.direct.* w/ ZendFramework Ext.direct.Manager.addProvider({ id : 'eu.sourcedevcon.provider', enableUrlEncode : 'extDirectData', url : './groupware', format : 'json', type : 'zend', actions : { account : [{ name : 'getFeedAccounts' }, { name : 'getEmailAccounts' }] }, namespace : 'eu.sourcedevcon.provider' }); client.js eu.sourcedevcon.provider.account.getEmailAccounts(); URL = module client.js POST http://sourcedevcon/groupware extDirectData {"action":"account","method":"getEmailAccounts","data":null,"type":"rpc","tid":1} Firebug if (isset($_POST['extDirectData'])) { Http://sourcedevcon/groupware/account/get.email.accounts ... } /groupware Firebug
  • 149.
    Ext.direct.* w/ ZendFramework Ext.direct.Manager.addProvider({ id : 'eu.sourcedevcon.provider', enableUrlEncode : 'extDirectData', url : './groupware', format : 'json', type : 'zend', actions : { account : [{ name : 'getFeedAccounts' }, { name : 'getEmailAccounts' }] }, namespace : 'eu.sourcedevcon.provider' }); client.js eu.sourcedevcon.provider.account.getEmailAccounts(); URL = module client.js action = controller POST http://sourcedevcon/groupware extDirectData {"action":"account","method":"getEmailAccounts","data":null,"type":"rpc","tid":1} Firebug if (isset($_POST['extDirectData'])) { http://sourcedevcon/groupware/account/get.email.accounts ... } /groupware Firebug
  • 150.
    Ext.direct.* w/ ZendFramework Ext.direct.Manager.addProvider({ id : 'eu.sourcedevcon.provider', enableUrlEncode : 'extDirectData', url : './groupware', format : 'json', type : 'zend', actions : { account : [{ name : 'getFeedAccounts' }, { name : 'getEmailAccounts' }] }, namespace : 'eu.sourcedevcon.provider' }); client.js eu.sourcedevcon.provider.account.getEmailAccounts(); URL = module client.js action = controller POST http://sourcedevcon/groupware extDirectData method = action {"action":"account","method":"getEmailAccounts","data":null,"type":"rpc","tid":1} Firebug if (isset($_POST['extDirectData'])) { http://sourcedevcon/groupware/account/get.email.accounts ... } /groupware Firebug
  • 151.
    Ext.direct.* w/ ZendFramework Ext.direct.Manager.addProvider({ id : 'eu.sourcedevcon.provider', enableUrlEncode : 'extDirectData', url : './groupware', URL format : 'json', type : 'zend', actions : { account : [{ „action“ name : 'getFeedAccounts' }, { name : 'getEmailAccounts' method }] }, namespace : 'eu.sourcedevcon.provider' }); client.js eu.sourcedevcon.provider.account.getEmailAccounts(); URL = module client.js action = controller POST http://sourcedevcon/groupware extDirectData method = action {"action":"account","method":"getEmailAccounts","data":null,"type":"rpc","tid":1} Firebug if (isset($_POST['extDirectData'])) { http://sourcedevcon/groupware/account/get.email.accounts ... } /groupware Firebug
  • 152.
    Ext.direct.* w/ ZendFramework Ext.direct.Manager.addProvider({ id : 'eu.sourcedevcon.provider', enableUrlEncode : 'extDirectData', url : './groupware', format : 'json', type : 'zend', actions : { account : [{ name : 'getFeedAccounts' }, { name : 'getEmailAccounts' }] }, namespace : 'eu.sourcedevcon.provider' }); client.js
  • 153.
    Ext.direct.* w/ ZendFramework Ext.direct.Manager.addProvider({ id : 'eu.sourcedevcon.provider', enableUrlEncode : 'extDirectData', url : './groupware', format : 'json', type : 'zend', actions : { account : [{ name : 'getFeedAccounts' }, { name : 'getEmailAccounts' }] }, namespace : 'eu.sourcedevcon.provider' }); client.js
  • 154.
    Ext.direct.* w/ ZendFramework Ext.direct.Manager.addProvider({ id : 'eu.sourcedevcon.provider', enableUrlEncode : 'extDirectData', url : './groupware', format : 'json', type : 'zend', actions : { account : [{ name : 'getFeedAccounts' }, { name : 'getEmailAccounts' }] }, namespace : 'eu.sourcedevcon.provider' }); client.js Ext.define('eu.sourcedevcon.direct.ZendProvider', { alias : 'direct.zendprovider', extend : 'Ext.direct.RemotingProvider' }); ZendProvider.js
  • 155.
    Ext.direct.* w/ ZendFramework URL = module action = controller method = action client.js
  • 156.
    Ext.direct.* w/ ZendFramework URL = module action = controller method = action Ext.Ajax.on('beforerequest', function(conn, options) { var trans = options.transaction; if (trans && trans.provider && trans.provider.type == 'zend') { var controller = options.transaction.action; controller = controller.replace(/([a-z])([A-Z])/g, "$1.$2").toLowerCase(); var action = options.transaction.method; action = action.replace(/([a-z])([A-Z])/g, "$1.$2").toLowerCase(); var module = options.url; var url = module + (module.lastIndexOf('/') == module.length-1 ? '' : '/') + controller + '/' + action; if (options.transaction.provider.format) { url += '/format/' + options.transaction.provider.format; } options.url = url; options.disableCaching = true; } }); client.js
  • 157.
    Ext.direct.* w/ ZendFramework URL = module action = controller method = action Ext.Ajax.on('beforerequest', function(conn, options) { var trans = options.transaction; if (trans && trans.provider && trans.provider.type == 'zend') { var controller = options.transaction.action; controller = controller.replace(/([a-z])([A-Z])/g, "$1.$2").toLowerCase(); var action = options.transaction.method; action = action.replace(/([a-z])([A-Z])/g, "$1.$2").toLowerCase(); var module = options.url; var url = module + (module.lastIndexOf('/') == module.length-1 ? '' : '/') + controller + '/' + action; if (options.transaction.provider.format) { url += '/format/' + options.transaction.provider.format; } options.url = url; options.disableCaching = true; } }); client.js
  • 158.
    Ext.direct.* w/ ZendFramework URL = module action = controller method = action Ext.Ajax.on('beforerequest', function(conn, options) { var trans = options.transaction; if (trans && trans.provider && trans.provider.type == 'zend') { var controller = options.transaction.action; controller = controller.replace(/([a-z])([A-Z])/g, "$1.$2").toLowerCase(); var action = options.transaction.method; action = action.replace(/([a-z])([A-Z])/g, "$1.$2").toLowerCase(); var module = options.url; var url = module + (module.lastIndexOf('/') == module.length-1 ? '' : '/') + controller + '/' + action; if (options.transaction.provider.format) { url += '/format/' + options.transaction.provider.format; } options.url = url; options.disableCaching = true; } }); client.js
  • 159.
    Ext.direct.* w/ ZendFramework URL = module action = controller method = action Ext.Ajax.on('beforerequest', function(conn, options) { var trans = options.transaction; if (trans && trans.provider && trans.provider.type == 'zend') { var controller = options.transaction.action; controller = controller.replace(/([a-z])([A-Z])/g, "$1.$2").toLowerCase(); var action = options.transaction.method; action = action.replace(/([a-z])([A-Z])/g, "$1.$2").toLowerCase(); var module = options.url; var url = module + (module.lastIndexOf('/') == module.length-1 ? '' : '/') + controller + '/' + action; if (options.transaction.provider.format) { url += '/format/' + options.transaction.provider.format; } options.url = url; options.disableCaching = true; } }); client.js
  • 160.
    Ext.direct.* w/ ZendFramework URL = module action = controller method = action Ext.Ajax.on('beforerequest', function(conn, options) { var trans = options.transaction; if (trans && trans.provider && trans.provider.type == 'zend') { var controller = options.transaction.action; controller = controller.replace(/([a-z])([A-Z])/g, "$1.$2").toLowerCase(); var action = options.transaction.method; action = action.replace(/([a-z])([A-Z])/g, "$1.$2").toLowerCase(); var module = options.url; var url = module + (module.lastIndexOf('/') == module.length-1 ? '' : '/') + controller + '/' + action; if (options.transaction.provider.format) { url += '/format/' + options.transaction.provider.format; } options.url = url; options.disableCaching = true; } }); client.js
  • 161.
    Ext.direct.* w/ ZendFramework URL = module action = controller method = action Ext.Ajax.on('beforerequest', function(conn, options) { var trans = options.transaction; if (trans && trans.provider && trans.provider.type == 'zend') { var controller = options.transaction.action; controller = controller.replace(/([a-z])([A-Z])/g, "$1.$2").toLowerCase(); var action = options.transaction.method; action = action.replace(/([a-z])([A-Z])/g, "$1.$2").toLowerCase(); var module = options.url; var url = module + (module.lastIndexOf('/') == module.length-1 ? '' : '/') + controller + '/' + action; if (options.transaction.provider.format) { url += '/format/' + options.transaction.provider.format; } options.url = url; options.disableCaching = true; } }); client.js
  • 162.
    Ext.direct.* w/ ZendFramework URL = module action = controller method = action Ext.Ajax.on('beforerequest', function(conn, options) { var trans = options.transaction; if (trans && trans.provider && trans.provider.type == 'zend') { var controller = options.transaction.action; controller = controller.replace(/([a-z])([A-Z])/g, "$1.$2").toLowerCase(); var action = options.transaction.method; action = action.replace(/([a-z])([A-Z])/g, "$1.$2").toLowerCase(); var module = options.url; var url = module + (module.lastIndexOf('/') == module.length-1 ? '' : '/') + controller + '/' + action; if (options.transaction.provider.format) { ContextSwitch url += '/format/' + options.transaction.provider.format; } options.url = url; options.disableCaching = true; } }); client.js
  • 163.
    Ext.direct.* w/ ZendFramework URL = module action = controller method = action Ext.Ajax.on('beforerequest', function(conn, options) { var trans = options.transaction; if (trans && trans.provider && trans.provider.type == 'zend') { var controller = options.transaction.action; controller = controller.replace(/([a-z])([A-Z])/g, "$1.$2").toLowerCase(); var action = options.transaction.method; action = action.replace(/([a-z])([A-Z])/g, "$1.$2").toLowerCase(); var module = options.url; var url = module + (module.lastIndexOf('/') == module.length-1 ? '' : '/') + controller + '/' + action; if (options.transaction.provider.format) { url += '/format/' + options.transaction.provider.format; } options.url = url; options.disableCaching = true; } }); client.js
  • 164.
    Ext.direct.* w/ ZendFramework Ext.direct.Manager.addProvider({ id : 'eu.sourcedevcon.provider', enableUrlEncode : 'extDirectData', url : './groupware', format : 'json', type : 'zend', actions : { account : [{ name : 'getFeedAccounts' }, { name : 'getEmailAccounts' }] }, namespace : 'eu.sourcedevcon.provider' }); client.js eu.sourcedevcon.provider.account.getEmailAccounts(); client.js
  • 165.
    Ext.direct.* w/ ZendFramework Ext.direct.Manager.addProvider({ id : 'eu.sourcedevcon.provider', enableUrlEncode : 'extDirectData', url : './groupware', format : 'json', type : 'zend', actions : { account : [{ name : 'getFeedAccounts' }, { name : 'getEmailAccounts' }] }, namespace : 'eu.sourcedevcon.provider' }); client.js eu.sourcedevcon.provider.account.getEmailAccounts(); client.js POST http://sourcedevcon/groupware
  • 166.
    Ext.direct.* w/ ZendFramework Ext.direct.Manager.addProvider({ id : 'eu.sourcedevcon.provider', enableUrlEncode : 'extDirectData', url : './groupware', format : 'json', type : 'zend', actions : { account : [{ name : 'getFeedAccounts' }, { name : 'getEmailAccounts' }] }, namespace : 'eu.sourcedevcon.provider' }); client.js eu.sourcedevcon.provider.account.getEmailAccounts(); client.js POST http://sourcedevcon/groupware POST http://sourcedevcon/groupware/account/get.email.accounts/format/json extDirectData {"action":"account","method":"getEmailAccounts","data":null,"type":"rpc","tid":1} Firebug
  • 167.
    Ext.direct.* w/ ZendFramework POST http://sourcedevcon/groupware/account/get.email.accounts/format/json extDirectData {"action":"account","method":"getEmailAccounts","data":null,"type":"rpc","tid":1} Firebug Note: ● Teach your actions the „extDirectData“-POST parameter
  • 168.
    Ext.direct.* w/ ZendFramework POST http://sourcedevcon/groupware/account/get.email.accounts/format/json extDirectData {"action":"account","method":"getEmailAccounts","data":null,"type":"rpc","tid":1} Firebug Note: ● Teach your actions the „extDirectData“-POST parameter ● Your responses must return the „tid“ as received by the request
  • 169.
    Ext.direct.* w/ ZendFramework Will do! Let me send a batched request now!
  • 170.
    Ext.direct.* w/ ZendFramework Ext.direct.Manager.addProvider({ id : 'eu.sourcedevcon.provider', enableUrlEncode : 'extDirectData', url : './groupware', format : 'json', type : 'zend', actions : { account : [{ name : 'getFeedAccounts' }, { name : 'getEmailAccounts' }] }, namespace : 'eu.sourcedevcon.provider' }); client.js eu.sourcedevcon.provider.account.getEmailAccounts(); eu.sourcedevcon.provider.account.getFeedAccounts(); client.js
  • 171.
    Ext.direct.* w/ ZendFramework Ext.direct.Manager.addProvider({ id : 'eu.sourcedevcon.provider', enableUrlEncode : 'extDirectData', url : './groupware', format : 'json', type : 'zend', actions : { account : [{ name : 'getFeedAccounts' }, { name : 'getEmailAccounts' }] }, namespace : 'eu.sourcedevcon.provider' }); client.js eu.sourcedevcon.provider.account.getEmailAccounts(); eu.sourcedevcon.provider.account.getFeedAccounts(); client.js POST http://sourcedevcon/groupware extDirectData [{"action":"account","method":"getEmailAccounts","data":null,"type":"rpc","tid":1}, {"action":"account","method":"getFeedAccounts","data":null,"type":"rpc","tid":2}] Firebug
  • 172.
    Ext.direct.* w/ ZendFramework ...?
  • 173.
    Ext.direct.* w/ ZendFramework queueTransaction: function(transaction){ var me = this, enableBuffer = me.enableBuffer; if (transaction.form) { me.sendFormRequest(transaction); return; } me.callBuffer.push(transaction); if (enableBuffer) { if (!me.callTask) { me.callTask = Ext.create('Ext.util.DelayedTask', me.combineAndSend, me); } me.callTask.delay(Ext.isNumber(enableBuffer) ? enableBuffer : 10); } else { me.combineAndSend(); } } RemotingProvider.js
  • 174.
    Ext.direct.* w/ ZendFramework queueTransaction: function(transaction){ add transaction to callBuffer Array var me = this, enableBuffer = me.enableBuffer; if (transaction.form) { me.sendFormRequest(transaction); return; } me.callBuffer.push(transaction); if (enableBuffer) { if (!me.callTask) { me.callTask = Ext.create('Ext.util.DelayedTask', me.combineAndSend, me); } me.callTask.delay(Ext.isNumber(enableBuffer) ? enableBuffer : 10); } else { me.combineAndSend(); } } RemotingProvider.js
  • 175.
    Ext.direct.* w/ ZendFramework queueTransaction: function(transaction){ add transaction to callBuffer Array var me = this, enableBuffer = me.enableBuffer; if batching is enabled, create a task if (transaction.form) { me.sendFormRequest(transaction); return; } me.callBuffer.push(transaction); if (enableBuffer) { if (!me.callTask) { me.callTask = Ext.create('Ext.util.DelayedTask', me.combineAndSend, me); } me.callTask.delay(Ext.isNumber(enableBuffer) ? enableBuffer : 10); } else { me.combineAndSend(); } } RemotingProvider.js
  • 176.
    Ext.direct.* w/ ZendFramework queueTransaction: function(transaction){ add transaction to callBuffer Array var me = this, enableBuffer = me.enableBuffer; if batching is enabled, create a task wait for 10ms for another if (transaction.form) { transaction to be added me.sendFormRequest(transaction); return; } me.callBuffer.push(transaction); if (enableBuffer) { if (!me.callTask) { me.callTask = Ext.create('Ext.util.DelayedTask', me.combineAndSend, me); } me.callTask.delay(Ext.isNumber(enableBuffer) ? enableBuffer : 10); } else { me.combineAndSend(); } } RemotingProvider.js
  • 177.
    Ext.direct.* w/ ZendFramework queueTransaction: function(transaction){ add transaction to callBuffer Array var me = this, enableBuffer = me.enableBuffer; if batching is enabled, create a task wait for 10ms for another if (transaction.form) { transaction to be added me.sendFormRequest(transaction); no more transactions coming in? Call return; combineAndSend } me.callBuffer.push(transaction); if (enableBuffer) { if (!me.callTask) { me.callTask = Ext.create('Ext.util.DelayedTask', me.combineAndSend, me); } me.callTask.delay(Ext.isNumber(enableBuffer) ? enableBuffer : 10); } else { me.combineAndSend(); } } RemotingProvider.js
  • 178.
    Ext.direct.* w/ ZendFramework eu.sourcedevcon.provider.account.getEmailAccounts(); eu.sourcedevcon.provider.account.getFeedAccounts(); client.js Ext.Ajax.on('beforerequest', function(conn, options) { var trans = options.transaction; if (trans && trans.provider && trans.provider.type == 'zend') { var controller = options.transaction.action; controller = controller.replace(/([a-z])([A-Z])/g, "$1.$2").toLowerCase(); var action = options.transaction.method; action = action.replace(/([a-z])([A-Z])/g, "$1.$2").toLowerCase(); var module = options.url; var url = module + (module.lastIndexOf('/') == module.length-1 ? '' : '/') + controller + '/' + action; if (options.transaction.provider.format) { url += '/format/' + options.transaction.provider.format; } options.url = url; options.disableCaching = true; } }); client.js
  • 179.
    Ext.direct.* w/ ZendFramework eu.sourcedevcon.provider.account.getEmailAccounts(); eu.sourcedevcon.provider.account.getFeedAccounts(); client.js Ext.Ajax.on('beforerequest', function(conn, options) { false var trans = options.transaction; if (trans && trans.provider && trans.provider.type == 'zend') { var controller = options.transaction.action; controller = controller.replace(/([a-z])([A-Z])/g, "$1.$2").toLowerCase(); var action = options.transaction.method; action = action.replace(/([a-z])([A-Z])/g, "$1.$2").toLowerCase(); var module = options.url; var url = module + (module.lastIndexOf('/') == module.length-1 ? '' : '/') + controller + '/' + action; if (options.transaction.provider.format) { url += '/format/' + options.transaction.provider.format; } options.url = url; options.disableCaching = true; } }); client.js
  • 180.
    Ext.direct.* w/ ZendFramework eu.sourcedevcon.provider.account.getEmailAccounts(); eu.sourcedevcon.provider.account.getFeedAccounts(); client.js Ext.Ajax.on('beforerequest', function(conn, options) { false var trans = options.transaction; if (trans && trans.provider && trans.provider.type == 'zend') { var controller = options.transaction.action; controller = controller.replace(/([a-z])([A-Z])/g, "$1.$2").toLowerCase(); var action = options.transaction.method; ... client.js POST http://sourcedevcon/groupware extDirectData [{"action":"account","method":"getEmailAccounts","data":null,"type":"rpc","tid":1}, {"action":"account","method":"getFeedAccounts","data":null,"type":"rpc","tid":2}] Firebug
  • 181.
    Ext.direct.* w/ ZendFramework ...and now for something completely different.
  • 182.
    Ext.direct.* w/ ZendFramework Introducing Sourcedevcon_Controller_Plugin_ExtRequest[5]
  • 183.
    Ext.direct.* w/ ZendFramework What is this Plugin? ● „Plugin“ for Zend Framework's Front Controller ● Actually not a real plugin, but rather a mediator for preDispatch/postDispatch Events ● Capable of detecting batched requests ● Capable of disassembling a batched request into individual requests ● Capable of creating a sandbox for each request, and processing it
  • 184.
  • 185.
    Ext.direct.* w/ ZendFramework dispatchLoopStartup
  • 186.
    Ext.direct.* w/ ZendFramework dispatchLoopStartup dispatchLoopShutdown
  • 187.
    Ext.direct.* w/ ZendFramework pre Dispatch Action Dispatch Controller dispatchLoopStartup post dispatchLoopShutdown Dispatch Response Object
  • 188.
    Ext.direct.* w/ ZendFramework $extDirect = new Sourcedevcon_Controller_Plugin_ExtRequest(array( 'extParameter' => 'extDirectData', 'additionalHeaders' => array('Content-Type' => 'application/json'), 'additionalParams' => array('format' => 'json'), 'action' => 'multi.request', 'controller' => 'ext', 'module' => 'default' )); $extDirect->registerPlugins(); index.php
  • 189.
    Ext.direct.* w/ ZendFramework $extDirect = new Sourcedevcon_Controller_Plugin_ExtRequest(array( 'extParameter' => 'extDirectData', The POST parameter that holds a batched request 'additionalHeaders' => array('Content-Type' => 'application/json'), 'additionalParams' => array('format' => 'json'), 'action' => 'multi.request', 'controller' => 'ext', 'module' => 'default' )); $extDirect->registerPlugins(); index.php
  • 190.
    Ext.direct.* w/ ZendFramework $extDirect = new Sourcedevcon_Controller_Plugin_ExtRequest(array( 'extParameter' => 'extDirectData', Additional headers for the 'additionalHeaders' => array('Content-Type' => 'application/json'), 'additionalParams' => array('format' => 'json'), response object 'action' => 'multi.request', 'controller' => 'ext', 'module' => 'default' )); $extDirect->registerPlugins(); index.php
  • 191.
    Ext.direct.* w/ ZendFramework $extDirect = new Sourcedevcon_Controller_Plugin_ExtRequest(array( 'extParameter' => 'extDirectData', 'additionalHeaders' => array('Content-Type' => 'application/json'), 'additionalParams' => array('format' => Additional parameters we add 'json'), 'action' => 'multi.request', to each sandboxed request 'controller' => 'ext', 'module' => 'default' )); $extDirect->registerPlugins(); index.php
  • 192.
    Ext.direct.* w/ ZendFramework $extDirect = new Sourcedevcon_Controller_Plugin_ExtRequest(array( 'extParameter' => 'extDirectData', 'additionalHeaders' => array('Content-Type' => 'application/json'), 'additionalParams' => array('format' => 'json'), 'action' => 'multi.request', 'controller' => 'ext', The default module/controller/action 'module' => 'default' before disassembling )); $extDirect->registerPlugins(); index.php
  • 193.
    Ext.direct.* w/ ZendFramework $extDirect = new Sourcedevcon_Controller_Plugin_ExtRequest(array( 'extParameter' => 'extDirectData', 'additionalHeaders' => array('Content-Type' => 'application/json'), 'additionalParams' => array('format' => 'json'), 'action' => 'multi.request', 'controller' => 'ext', 'module' => 'default' )); $extDirect->registerPlugins(); Register the plugins at the Front Controller index.php
  • 194.
    Ext.direct.* w/ ZendFramework $extDirect = new Sourcedevcon_Controller_Plugin_ExtRequest(array( 'extParameter' => 'extDirectData', 'additionalHeaders' => array('Content-Type' => 'application/json'), 'additionalParams' => array('format' => 'json'), 'action' => 'multi.request', 'controller' => 'ext', 'module' => 'default' )); $extDirect->registerPlugins(); index.php public function dispatchLoopStartup() { $this->_parent->notifyDispatchLoopStartup(); } PreDispatcher.php
  • 195.
    Ext.direct.* w/ ZendFramework $extDirect = new Sourcedevcon_Controller_Plugin_ExtRequest(array( 'extParameter' => 'extDirectData', 'additionalHeaders' => array('Content-Type' => 'application/json'), 'additionalParams' => array('format' => 'json'), 'action' => 'multi.request', 'controller' => 'ext', 'module' => 'default' )); $extDirect->registerPlugins(); index.php public function dispatchLoopStartup() public function dispatchLoopShutdown() { { $this->_parent->notifyDispatchLoopStartup(); $this->_parent->notifyDispatchLoopShutdown(); } } PreDispatcher.php PostDispatcher.php
  • 196.
    Ext.direct.* w/ ZendFramework $extDirect = new Sourcedevcon_Controller_Plugin_ExtRequest(array( 'extParameter' => 'extDirectData', 'additionalHeaders' => array('Content-Type' => 'application/json'), 'additionalParams' => array('format' => 'json'), 'action' => 'multi.request', 'controller' => 'ext', 'module' => 'default' )); $extDirect->registerPlugins(); index.php public function dispatchLoopStartup() public function dispatchLoopShutdown() { { $this->_parent->notifyDispatchLoopStartup(); $this->_parent->notifyDispatchLoopShutdown(); } } PreDispatcher.php PostDispatcher.php preDispatch dispatch postDispatch Request_Http Response_Http
  • 197.
    Thank you Thankyou! ... questions?
  • 198.
    Resources [1] http://en.wikipedia.org/wiki/Continuous_integration [2] http://svnbook.red-bean.com/en/1.5/svn.advanced.vendorbr.html [3] http://zendframework.com/manual/en/zend.controller.actionhelpers.html [4] http://www.sencha.com/products/extjs/extdirect [5] http://thorsten.suckow-homberg.de/sourcedevcon [6] http://framework.zend.com/manual/en/zend.controller.basics.html