zend framework mvc driven extjs
DESCRIPTION
This talk will give attendees details on how to set up their directory layout in larger PHP/ExtJS applications, along with hints to build processing and deployment. In the second part the talk will cover context based applications, how to detect context and how to utilize it with ExtJS. The third and last part will show how developers can use Ext.direct.* with an already existing Zend Framework backend, including merged requests and how to set up sandboxes for processing merged requests.TRANSCRIPT
Zend Framework MVC driven ExtJS
Thorsten Suckow-Homberg, K&K GmbH
Sourc{ - SenchaDev ConferenceMay 5-7, 2011Croatia, 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
„How do I get the resources from vendor into my src folder where – obviously - running code will resist?“
„Do I need to run a build process every time a line of code changed?“
The Basics – Directory Layoutworking copy (development): /var/www/your_project/vendor/ext-ux-util-messagebus/var/www/your_project/src/www/htdocs/index.php
The Basics – Directory Layout
<?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
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
<?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.MessageBusAlias /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,denyAllow from all</Directory>
apache.conf
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
<?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.MessageBusAlias /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,denyAllow from all</Directory>
apache.conf
<script type="text/javascript" src="/js/ext-ux-util-messagebus/src/messagebus.js"></script>
index.phtml
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
Virtual# Alias for Ext.ux.util.MessageBusAlias /js/ext-ux-util-messagebusDevelopment:
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
Virtual
Build process
# Alias for Ext.ux.util.MessageBusAlias /js/ext-ux-util-messagebusDevelopment:
...<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:
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
Virtual
Build process
# Alias for Ext.ux.util.MessageBusAlias /js/ext-ux-util-messagebusDevelopment:
...<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:
<script type="text/javascript" src="/js/ext-ux-util-messagebus/src/messagebus.js"></script>
index.phtml
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
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 LayoutPros
● 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
Desktop PC
send/receive
Context based data
Application Server
Desktop PC Mobile
send/receivesend/receive
Context based data
Application Server
Desktop PC Mobile
send/receivesend/receive
Context „default“
Context based data
Application Server
Desktop PC Mobile
send/receivesend/receive
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
● 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]
ContextSwitch.php
Context based data
How does it work?
Context based data - detectionWe can define a context based on (virtually) all data that's available during runtime!
Context based data - detectionWe 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 - detectionWe 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 - detectionWe 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 - detectionWe 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 - detectionWe 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 - detectionWe 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 - detectionWe 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 - detectionWe 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 - detectionWe can define a context based on (virtually) all data that's available during runtime!
Another example:
User Agent
...$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';
}...
● 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 = '[email protected]'; }
}
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 = '[email protected]'; }
}
implement parent's init() methodto add action contexts...
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 = '[email protected]'; }
}
...which happens here:
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 = '[email protected]'; }
}
getUserAction() will now...
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 = '[email protected]'; }
}
… return the response json-formatted ifthe get-Parameter „format“ was set to „json“
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 = '[email protected]'; }
}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 = '[email protected]'; }
}
ReceptionController.php
We have just added an action context to this action
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 = '[email protected]'; }
}
Call business logic...
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 = '[email protected]'; }
}
And finally: Assign values to thecontroller's view
ReceptionController.php
Context based data - exampleclass User_ReceptionController extends Zend_Controller_Action() { ... public function getUserAction() { ... $this->view->userName = 'Peter Griffin'; $this->view->userEmail = '[email protected]'; }
}ReceptionController.php
client.js
Ext.Ajax.request({ url : „/user/reception/get.user/format/json“, success : function(response) { console.log(response.responseText); }});
Context based data - exampleclass User_ReceptionController extends Zend_Controller_Action() { ... public function getUserAction() { ... $this->view->userName = 'Peter Griffin'; $this->view->userEmail = '[email protected]'; }
}ReceptionController.php
client.js
Ext.Ajax.request({ url : „/user/reception/get.user/format/json“, success : function(response) { console.log(response.responseText); }});
Module
Context based data - exampleclass User_ReceptionController extends Zend_Controller_Action() { ... public function getUserAction() { ... $this->view->userName = 'Peter Griffin'; $this->view->userEmail = '[email protected]'; }
}
ReceptionController.php
client.js
Ext.Ajax.request({ url : „/user/reception/get.user/format/json“, success : function(response) { console.log(response.responseText); }});
Controller
Context based data - exampleclass User_ReceptionController extends Zend_Controller_Action() { ... public function getUserAction() { ... $this->view->userName = 'Peter Griffin'; $this->view->userEmail = '[email protected]'; }
}
ReceptionController.php
client.js
Ext.Ajax.request({ url : „/user/reception/get.user/format/json“, success : function(response) { console.log(response.responseText); }});
Action
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
Zend Framework routes to User_ReceptionController::getUserAction()
Router
Context based data - example
Request
Any plugins defined? Signal a preDispatch to them!
Router preDispatch
Context based data - example
Request
Any plugins defined? Signal a preDispatch to them!
Router preDispatch
public function preDispatch() { }
Zend_Controller_Action_Helper_Abstract
Context based data - example
Request
Dispatch the action – getUserAction() gets processed!
Router preDispatch
Dispatch
Context based data - example
Request Router preDispatch
Dispatch ActionController
public function getUserAction() { ... $this->view->userName = 'Peter Griffin'; $this->view->userEmail = '[email protected]'; } ReceptionController.php
Context based data - example
Request Router preDispatch
Dispatch ActionController
postDispatch
Any plugins defined? Signal a postDispatch to them!
Context based data - example
Request Router preDispatch
Dispatch ActionController
postDispatch
public function postDispatch() { $context = $this->getCurrentContext(); ... //$this->postJsonContext(); } ContextSwitch.php
Any plugins defined? Signal a postDispatch to them!
Context based data - example
Request Router preDispatch
Dispatch ActionController
postDispatch
$response->setHeader('Content-Type', 'application/json');
actionsleft?
Response Object
ContextSwitch.php
Context based data - example
Request Router preDispatch
Dispatch ActionController
postDispatch
$response->setHeader('Content-Type', 'application/json');
actionsleft?
Response Object
$response->setBody(Zend_Json::encode($view->getVars()));
ContextSwitch.php
ContextSwitch.php
Context based data - example
Request Router preDispatch
Dispatch ActionController
postDispatch
actionsleft?
Response Object
send response
Context based data - example
Request Router preDispatch
Dispatch ActionController
postDispatch
actionsleft?
Response Object
{'userName' : „Peter Griffin“, 'userEmail' : „[email protected]“}
send response
console
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 codeclass User_ReceptionController extends Zend_Controller_Action {
public function getUserAction() { ... }} ReceptionController.php
Ext.direct.* - advantages● Let's you call remote procedures directly from
client codeclass 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 codeclass User_ReceptionController extends Zend_Controller_Action {
public function getUserAction() { ... }} ReceptionController.php
user.reception.getUser();client.js
module
Ext.direct.* - advantages● Let's you call remote procedures directly from
client codeclass User_ReceptionController extends Zend_Controller_Action {
public function getUserAction() { ... }} ReceptionController.php
user.reception.getUser();client.js
controller
Ext.direct.* - advantages● Let's you call remote procedures directly from
client codeclass User_ReceptionController extends Zend_Controller_Action {
public function getUserAction() { ... }} ReceptionController.php
user.reception.getUser();client.js
action
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.jsPOST http://sourcedevcon/groupware/account/get.email.accountsPOST 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
Firebug
POST http://sourcedevcon/groupware
POST http://sourcedevcon/groupware/account/get.email.accountsPOST 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
Firebug
POST http://sourcedevcon/groupwareextDirectData[{"action":"account","method":"getEmailAccounts","data":null,"type":"rpc","tid":1},{"action":"account","method":"getFeedAccounts","data":null,"type":"rpc","tid":2}]
POST http://sourcedevcon/groupware/account/get.email.accountsPOST http://sourcedevcon/groupware/account/get.feed.accounts
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 FrameworkExt.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 FrameworkExt.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
URL
Ext.direct.* w/ Zend FrameworkExt.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
URL
„action“
Ext.direct.* w/ Zend FrameworkExt.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
URL
„action“
method
Ext.direct.* w/ Zend FrameworkExt.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
client.jseu.sourcedevcon.provider.account.getEmailAccounts();
Ext.direct.* w/ Zend FrameworkExt.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
client.js
eu.sourcedevcon.provider.account.getEmailAccounts();
namespace
Ext.direct.* w/ Zend FrameworkExt.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
client.js
eu.sourcedevcon.provider.account.getEmailAccounts();
action
Ext.direct.* w/ Zend FrameworkExt.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
client.js
eu.sourcedevcon.provider.account.getEmailAccounts();
method
Ext.direct.* w/ Zend FrameworkExt.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
client.jseu.sourcedevcon.provider.account.getEmailAccounts();
POST http://sourcedevcon/groupware
Firebug
Ext.direct.* w/ Zend FrameworkExt.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
client.jseu.sourcedevcon.provider.account.getEmailAccounts();
POST http://sourcedevcon/groupware
FirebugextDirectData{"action":"account","method":"getEmailAccounts","data":null,"type":"rpc","tid":1}
Ext.direct.* w/ Zend FrameworkExt.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
client.jseu.sourcedevcon.provider.account.getEmailAccounts();
POST http://sourcedevcon/groupware
FirebugextDirectData{"action":"account","method":"getEmailAccounts","data":null,"type":"rpc","tid":1}
Ext.direct.* w/ Zend FrameworkExt.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
client.jseu.sourcedevcon.provider.account.getEmailAccounts();
POST http://sourcedevcon/groupware
Firebug
extDirectData{"action":"account","method":"getEmailAccounts","data":null,"type":"rpc","tid":1}
Ext.direct.* w/ Zend FrameworkExt.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
client.jseu.sourcedevcon.provider.account.getEmailAccounts();
POST http://sourcedevcon/groupware
Firebug
extDirectData{"action":"account","method":"getEmailAccounts","data":null,"type":"rpc","tid":1}
Ext.direct.* w/ Zend FrameworkExt.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
client.jseu.sourcedevcon.provider.account.getEmailAccounts();
POST http://sourcedevcon/groupware
Firebug
extDirectData{"action":"account","method":"getEmailAccounts","data":null,"type":"rpc","tid":1}
Ext.direct.* w/ Zend FrameworkExt.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
client.jseu.sourcedevcon.provider.account.getEmailAccounts();
POST http://sourcedevcon/groupware
FirebugextDirectData{"action":"account","method":"getEmailAccounts","data":null,"type":"rpc","tid":1}
/groupware
if (isset($_POST['extDirectData'])) { ... }
Firebug
Ext.direct.* w/ Zend FrameworkExt.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
client.jseu.sourcedevcon.provider.account.getEmailAccounts();
POST http://sourcedevcon/groupware
FirebugextDirectData{"action":"account","method":"getEmailAccounts","data":null,"type":"rpc","tid":1}
/groupware
if (isset($_POST['extDirectData'])) { ... }
Firebug
Ext.direct.* w/ Zend FrameworkExt.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
client.jseu.sourcedevcon.provider.account.getEmailAccounts();
POST http://sourcedevcon/groupware
FirebugextDirectData{"action":"account","method":"getEmailAccounts","data":null,"type":"rpc","tid":1}
/groupware
if (isset($_POST['extDirectData'])) { ... }
http://sourcedevcon/groupware/account/get.email.accounts
Firebug
Ext.direct.* w/ Zend FrameworkExt.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
client.jseu.sourcedevcon.provider.account.getEmailAccounts();
POST http://sourcedevcon/groupware
FirebugextDirectData{"action":"account","method":"getEmailAccounts","data":null,"type":"rpc","tid":1}
/groupware
if (isset($_POST['extDirectData'])) { ... }
Http://sourcedevcon/groupware/account/get.email.accounts
Firebug
URL = module
Ext.direct.* w/ Zend FrameworkExt.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
client.jseu.sourcedevcon.provider.account.getEmailAccounts();
POST http://sourcedevcon/groupware
FirebugextDirectData{"action":"account","method":"getEmailAccounts","data":null,"type":"rpc","tid":1}
/groupware
if (isset($_POST['extDirectData'])) { ... }
http://sourcedevcon/groupware/account/get.email.accounts
Firebug
action = controller
URL = module
Ext.direct.* w/ Zend FrameworkExt.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
client.jseu.sourcedevcon.provider.account.getEmailAccounts();
POST http://sourcedevcon/groupware
FirebugextDirectData{"action":"account","method":"getEmailAccounts","data":null,"type":"rpc","tid":1}
/groupware
if (isset($_POST['extDirectData'])) { ... }
http://sourcedevcon/groupware/account/get.email.accounts
Firebug
action = controller
URL = module
method = action
Ext.direct.* w/ Zend FrameworkExt.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
client.jseu.sourcedevcon.provider.account.getEmailAccounts();
POST http://sourcedevcon/groupware
FirebugextDirectData{"action":"account","method":"getEmailAccounts","data":null,"type":"rpc","tid":1}
/groupware
if (isset($_POST['extDirectData'])) { ... }
http://sourcedevcon/groupware/account/get.email.accounts
Firebug
action = controller
URL = module
method = action
URL
„action“
method
Ext.direct.* w/ Zend FrameworkExt.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 FrameworkExt.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 FrameworkExt.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
ZendProvider.js
Ext.define('eu.sourcedevcon.direct.ZendProvider', { alias : 'direct.zendprovider', extend : 'Ext.direct.RemotingProvider'
});
Ext.direct.* w/ Zend Framework
client.js
URL = module action = controller method = action
Ext.direct.* w/ Zend Framework
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;
}});
URL = module action = controller method = action
Ext.direct.* w/ Zend Framework
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;
}});
URL = module action = controller method = action
Ext.direct.* w/ Zend Framework
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;
}});
URL = module action = controller method = action
Ext.direct.* w/ Zend Framework
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;
}});
URL = module action = controller method = action
Ext.direct.* w/ Zend Framework
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;
}});
URL = module action = controller method = action
Ext.direct.* w/ Zend Framework
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;
}});
URL = module action = controller method = action
Ext.direct.* w/ Zend Framework
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;
}});
URL = module action = controller method = action
ContextSwitch
Ext.direct.* w/ Zend Framework
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;
}});
URL = module action = controller method = action
Ext.direct.* w/ Zend FrameworkExt.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
client.jseu.sourcedevcon.provider.account.getEmailAccounts();
Ext.direct.* w/ Zend FrameworkExt.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
client.jseu.sourcedevcon.provider.account.getEmailAccounts();
POST http://sourcedevcon/groupware
Ext.direct.* w/ Zend FrameworkExt.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
client.jseu.sourcedevcon.provider.account.getEmailAccounts();
POST http://sourcedevcon/groupware/account/get.email.accounts/format/json
FirebugextDirectData{"action":"account","method":"getEmailAccounts","data":null,"type":"rpc","tid":1}
POST http://sourcedevcon/groupware
Ext.direct.* w/ Zend FrameworkPOST http://sourcedevcon/groupware/account/get.email.accounts/format/json
FirebugextDirectData{"action":"account","method":"getEmailAccounts","data":null,"type":"rpc","tid":1}
Note:● Teach your actions the „extDirectData“-POST parameter
Ext.direct.* w/ Zend FrameworkPOST http://sourcedevcon/groupware/account/get.email.accounts/format/json
FirebugextDirectData{"action":"account","method":"getEmailAccounts","data":null,"type":"rpc","tid":1}
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 FrameworkExt.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
client.jseu.sourcedevcon.provider.account.getEmailAccounts();eu.sourcedevcon.provider.account.getFeedAccounts();
Ext.direct.* w/ Zend FrameworkExt.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
client.jseu.sourcedevcon.provider.account.getEmailAccounts();eu.sourcedevcon.provider.account.getFeedAccounts();
POST http://sourcedevcon/groupware
Firebug
extDirectData[{"action":"account","method":"getEmailAccounts","data":null,"type":"rpc","tid":1},{"action":"account","method":"getFeedAccounts","data":null,"type":"rpc","tid":2}]
Ext.direct.* w/ Zend Framework
. . . ?
Ext.direct.* w/ Zend FrameworkqueueTransaction: 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 FrameworkqueueTransaction: 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
add transaction to callBuffer Array
Ext.direct.* w/ Zend FrameworkqueueTransaction: 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
add transaction to callBuffer Array
if batching is enabled, create a task
Ext.direct.* w/ Zend FrameworkqueueTransaction: 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
add transaction to callBuffer Array
if batching is enabled, create a task
wait for 10ms for another transaction to be added
Ext.direct.* w/ Zend FrameworkqueueTransaction: 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
add transaction to callBuffer Array
if batching is enabled, create a task
wait for 10ms for another transaction to be added
no more transactions coming in? Call combineAndSend
Ext.direct.* w/ Zend Framework
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.jseu.sourcedevcon.provider.account.getEmailAccounts();eu.sourcedevcon.provider.account.getFeedAccounts();
Ext.direct.* w/ Zend Framework
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.jseu.sourcedevcon.provider.account.getEmailAccounts();eu.sourcedevcon.provider.account.getFeedAccounts();
false
Ext.direct.* w/ Zend Framework
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; ...
client.jseu.sourcedevcon.provider.account.getEmailAccounts();eu.sourcedevcon.provider.account.getFeedAccounts();
false
POST http://sourcedevcon/groupware
Firebug
extDirectData[{"action":"account","method":"getEmailAccounts","data":null,"type":"rpc","tid":1},{"action":"account","method":"getFeedAccounts","data":null,"type":"rpc","tid":2}]
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
dispatchLoopStartup
dispatchLoopShutdown
preDispatch
Dispatch ActionController
postDispatch
ResponseObject
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', 'additionalHeaders' => array('Content-Type' => 'application/json'), 'additionalParams' => array('format' => 'json'), 'action' => 'multi.request', 'controller' => 'ext', 'module' => 'default'));
$extDirect->registerPlugins();index.php
The POST parameter that holds a batched request
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
Additional headers for the 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
Additional parameters we add to each sandboxed request
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
The default module/controller/action before disassembling
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
Register the plugins at the Front Controller
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
PreDispatcher.php
public function dispatchLoopStartup() { $this->_parent->notifyDispatchLoopStartup();}
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
PreDispatcher.php
public function dispatchLoopStartup() { $this->_parent->notifyDispatchLoopStartup();}
PostDispatcher.php
public function dispatchLoopShutdown() { $this->_parent->notifyDispatchLoopShutdown();}
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
PreDispatcher.php
public function dispatchLoopStartup() { $this->_parent->notifyDispatchLoopStartup();}
PostDispatcher.php
public function dispatchLoopShutdown() { $this->_parent->notifyDispatchLoopShutdown();}
preDispatch postDispatchdispatch
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