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.
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 will show you...
● … how you should plan your directory layout in
larger projects
5. 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
6. 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
7. 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
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
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
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
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
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
50. 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]
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
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
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
98. Ext.direct.*
„Ext Direct is 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
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!
122. Ext.direct.* w/ Zend Framework
… so let's find a solution.
Let's sum up our goals:
123. 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
124. 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
125. 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
126. 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
128. 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
129. Ext.direct.* w/ Zend Framework
Change Ext.direct.RemotingProvider
URLs on the fly
130. 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.
131. 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
132. 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
133. 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
134. 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
135. 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
136. 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
137. 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
138. 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
139. 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
140. 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
141. 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
142. 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
143. 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
144. 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
145. 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
146. 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
147. 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
148. 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
149. 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
150. 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
151. 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
152. 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
153. 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
154. 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
164. 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
165. 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
166. 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
167. 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
168. 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
173. 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
174. 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
175. 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
176. 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
177. 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
183. 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