Zend_Controller-ActionHelpers-ContextSwitch.xml 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602
  1. <sect3 id="zend.controller.actionhelpers.contextswitch">
  2. <title>ContextSwitch and AjaxContext</title>
  3. <para>
  4. <code>ContextSwitch</code> 动作助手用来使在请求后返回不同的响应格式变得容易。<code>AjaxContext</code> 助手是 <code>ContextSwitch</code> 的特别版本,用来返回响应到 XmlHttpRequests。
  5. </para>
  6. <para>
  7. 你必须在关于动作能够响应哪个上下文(context)的控制器中打开(enable)其中一个。如果一个进来的请求对给定的动作指出一个有效的上下文,助手将做:
  8. </para>
  9. <itemizedlist>
  10. <listitem><para>
  11. 如果布局被打开,则关闭它。
  12. </para></listitem>
  13. <listitem><para>
  14. 设置一个备用的视图后缀,为上下文有效地请求一个分离的视图脚本。
  15. </para></listitem>
  16. <listitem><para>
  17. 为期望的上下文发送适当的响应头。
  18. </para></listitem>
  19. <listitem><para>
  20. 可选地调用特别的回调(callback)来设置上下文和/或执行处理后的任务。
  21. </para></listitem>
  22. </itemizedlist>
  23. <para>
  24. 例子,考虑下列的控制器:
  25. </para>
  26. <programlisting role="php"><![CDATA[
  27. class NewsController extends Zend_Controller_Action
  28. {
  29. /**
  30. * Landing page; forwards to listAction()
  31. */
  32. public function indexAction()
  33. {
  34. $this->_forward('list');
  35. }
  36. /**
  37. * List news items
  38. */
  39. public function listAction()
  40. {
  41. }
  42. /**
  43. * View a news item
  44. */
  45. public function viewAction()
  46. {
  47. }
  48. }
  49. ]]>
  50. </programlisting>
  51. <para>
  52. 我们想让 <code>listAction()</code> 也支持 XML 格式,不用创建一个不同的动作,我们可以提示它可以返回 XML 响应:
  53. </para>
  54. <programlisting role="php"><![CDATA[
  55. class NewsController extends Zend_Controller_Action
  56. {
  57. public function init()
  58. {
  59. $contextSwitch = $this->_helper->getHelper('contextSwitch');
  60. $contextSwitch->addActionContext('list', 'xml')
  61. ->initContext();
  62. }
  63. // ...
  64. }
  65. ]]>
  66. </programlisting>
  67. <para>
  68. 这将完成:
  69. </para>
  70. <itemizedlist>
  71. <listitem><para>
  72. 设置 'Content-Type' 响应头为 'text/xml'.
  73. </para></listitem>
  74. <listitem><para>
  75. 修改视图后缀为 'xml.phtml' (或者,如果你使用另外的视图后缀,'xml.[你的后缀]')。
  76. </para></listitem>
  77. </itemizedlist>
  78. <para>
  79. 现在你需要创建一个新的视图脚本 'news/list.xml.phtml',它将创建和解析 XML。
  80. </para>
  81. <para>
  82. 为决定一个请求是否应该初始化一个上下文开关(context switch),这个助手检查在请求对象理的令牌。缺省地,它寻找一个 'format' 参数,尽管这是可配置的。这意味着,在大多数情况下,为触发一个上下文开关,你可以添加一个 'format' 参数给你的请求:
  83. </para>
  84. <itemizedlist>
  85. <listitem><para>
  86. 通过 URL 参数:<code>/news/list/format/xml</code> (回忆一下,缺省路由模式(schema)允许在动作后跟随任意的键/值对)
  87. </para></listitem>
  88. <listitem><para>
  89. 通过 GET 参数:<code>/news/list?format=xml</code>
  90. </para></listitem>
  91. </itemizedlist>
  92. <para>
  93. <code>ContextSwitch</code> 允许指定任意的上下文,包括需要修改后缀的、任何应该被发送的响应头和任意的用来初始化和善后的回调(callback)。
  94. </para>
  95. <sect4 id="zend.controller.actionhelpers.contextswitch.contexts">
  96. <title> 缺省可用的上下文 </title>
  97. <para>
  98. 缺省地,<code>ContextSwitch</code> 助手的两个上下文可用:json 和 xml。
  99. </para>
  100. <itemizedlist>
  101. <listitem>
  102. <para>
  103. <emphasis>JSON</emphasis>. JSON 上下文设置 'Content-Type' 响应头为 'application/json',设置视图脚本后缀为 'json.phtml'。
  104. </para>
  105. <para>
  106. 缺省地,不需要视图脚本,它将系列化所有的视图变量,立即发出 JSON 响应。
  107. </para>
  108. <para>
  109. 通过关闭 auto-JSON serialization 可禁止这个行为:
  110. </para>
  111. <programlisting role="php"><![CDATA[
  112. $this->_helper->contextSwitch()->setAutoJsonSerialization(false);
  113. ]]>
  114. </programlisting>
  115. </listitem>
  116. <listitem>
  117. <para>
  118. <emphasis>XML</emphasis>. XML 上下文设置 'Content-Type' 响应头为 'text/xml',设置视图脚本后缀为 'xml.phtml'。你需要为这个上下文创建新的视图脚本。
  119. </para>
  120. </listitem>
  121. </itemizedlist>
  122. </sect4>
  123. <sect4 id="zend.controller.actionhelpers.contextswitch.custom">
  124. <title> 创建定制的上下文 </title>
  125. <para>
  126. 有时候,缺省的上下文不够用,例如你需要返回 YAML,或系列化 PHP、RSS 或 ATOM feed 等等,<code>ContextSwitch</code> 正是你需要的东西。
  127. </para>
  128. <para>
  129. 最容易的添加新的上下文的办法是通过 <code>addContext()</code> 方法。这个方法带有两个参数:上下文的名称和一个规范(specification)数组,规范应当包含下列中的一个或多个:
  130. </para>
  131. <itemizedlist>
  132. <listitem>
  133. <para><emphasis>suffix</emphasis>: 当在视图解析器里注册时,预先准备的缺省的视图后缀。 </para>
  134. </listitem>
  135. <listitem>
  136. <para><emphasis>headers</emphasis>: 作为响应的一部分发送的头/值(header/value)对的数组。</para>
  137. </listitem>
  138. <listitem>
  139. <para><emphasis>callbacks</emphasis>: 数组,包含一个或更多键 'init' 或 'post',指向有效的可用于上下文和善后处理的 PHP 回调(callback)。</para>
  140. <para>
  141. 回调的初始化发生在当 <code>ContextSwitch</code> 检测到上下文时,你可以用它来执行任意的应该发生的逻辑。作为例子,当 auto-JSON serialization 是 on 的时候,JSON 上下文使用回调来关闭视图解析器(ViewRenderer)。
  142. </para>
  143. <para>
  144. 善后处理(post processing)发生在动作的 <code>postDispatch()</code> 程序期间,可用来执行任意的逻辑。作为例子,JSON 上下文使用回调(callback)来决定是否 auto-JSON serialization 是 on;如果是,它系列化视图变量到 JSON 和 发送响应,如果不是,它重新打开(re-enable)视图解析器(ViewRenderer)。
  145. </para>
  146. </listitem>
  147. </itemizedlist>
  148. <para>
  149. 和上下文交互作用的方法:
  150. </para>
  151. <itemizedlist>
  152. <listitem><para>
  153. <code>addContext($context, array $spec)</code>: 添加新的上下文,如果上下文存在,抛出一个异常。
  154. </para></listitem>
  155. <listitem><para>
  156. <code>setContext($context, array $spec)</code>: 添加新的上下文或重写一个已存在的上下文,和 <code>addContext()</code> 使用相同的规范(specification)。
  157. </para></listitem>
  158. <listitem><para>
  159. <code>addContexts(array $contexts)</code>: 一次添加多个上下文。<code>$contexts</code> 应当是上下文/规范(context/specification)对的数组。如果任何一个上下文存在,就抛出异常。
  160. </para></listitem>
  161. <listitem><para>
  162. <code>setContexts(array $contexts)</code>: 添加和重写存在的上下文(多于一个),和 <code>addContexts()</code> 使用相同的规范。
  163. </para></listitem>
  164. <listitem><para>
  165. <code>hasContext($context)</code>: 如果上下文存在,返回 true,否则返回 false。
  166. </para></listitem>
  167. <listitem><para>
  168. <code>getContext($context)</code>: 通过名称来获取一个单个的上下文,返回一个遵循用于 <code>addContext()</code> 的规范的数组。
  169. </para></listitem>
  170. <listitem><para>
  171. <code>getContexts()</code>: 获取所有的上下文,返回上下文/规范对的数组。
  172. </para></listitem>
  173. <listitem><para>
  174. <code>removeContext($context)</code>: 通过名称来清除一个单个的上下文,成功返回 true,如果没有发现上下文返回 false。
  175. </para></listitem>
  176. <listitem><para>
  177. <code>clearContexts()</code>: 清除所有上下文。
  178. </para></listitem>
  179. </itemizedlist>
  180. </sect4>
  181. <sect4 id="zend.controller.actionhelpers.contextswitch.actions">
  182. <title> 为每个动作设置上下文 </title>
  183. <para>
  184. 有两个设置可用的上下文的机制:或者在控制器里手工创建数组,或者使用在 <code>ContextSwitch</code> 里的方法来装配它们。
  185. </para>
  186. <para>
  187. 添加动作/上下文关系的基本方法是 <code>addActionContext()</code> 。它有两个参数:上下文被添加到的动作和上下文的名称或上下文数组的其中之一。作为例子,考虑下列的控制器类:
  188. </para>
  189. <programlisting role="php"><![CDATA[
  190. class FooController extends Zend_Controller_Action
  191. {
  192. public function listAction()
  193. {
  194. }
  195. public function viewAction()
  196. {
  197. }
  198. public function commentsAction()
  199. {
  200. }
  201. public function updateAction()
  202. {
  203. }
  204. }
  205. ]]>
  206. </programlisting>
  207. <para>
  208. 假如我们想添加 XML 上下文到 'list'动作、XML 和 JSON 上下文到 'comments' 动作,可以在 <code>init()</code> 方法中完成:
  209. </para>
  210. <programlisting role="php"><![CDATA[
  211. class FooController extends Zend_Controller_Action
  212. {
  213. public function init()
  214. {
  215. $this->_helper->contextSwitch()
  216. ->addActionContext('list', 'xml')
  217. ->addActionContext('comments', array('xml', 'json'))
  218. ->initContext();
  219. }
  220. }
  221. ]]>
  222. </programlisting>
  223. <para>
  224. 另外,还可以定义数组属性 <code>$contexts</code>:
  225. </para>
  226. <programlisting role="php"><![CDATA[
  227. class FooController extends Zend_Controller_Action
  228. {
  229. public $contexts = array(
  230. 'list' => array('xml'),
  231. 'comments' => array('xml', 'json')
  232. );
  233. public function init()
  234. {
  235. $this->_helper->contextSwitch()->initContext();
  236. }
  237. }
  238. ]]>
  239. </programlisting>
  240. <para>
  241. 上述不但缺少周密考虑,而且有潜在的错误。
  242. </para>
  243. <para>
  244. 下面的方法可用来构造上下文映射:
  245. </para>
  246. <itemizedlist>
  247. <listitem>
  248. <para>
  249. <code>addActionContext($action, $context)</code>: 标记一个或多个可用的上下文给一个动作,如果映射已经存在,就追加到那些映射中。<code>$context</code> 可以是一个单个的上下文,或者是一个上下文数组。
  250. </para>
  251. <para>
  252. 一个上下文的 <code>true</code> 值将标记所有可用的上下文给动作。
  253. </para>
  254. <para>
  255. 一个 $context 的空值将关闭(disable)所有给定动作的上下文。
  256. </para>
  257. </listitem>
  258. <listitem><para>
  259. <code>setActionContext($action, $context)</code>: 标记一个或多个上下文对动作可用,如果映射已经存在,它就替换它们。<code>$context</code> 可以是一个单个的上下文,或者是一个上下文数组。
  260. </para></listitem>
  261. <listitem><para>
  262. <code>addActionContexts(array $contexts)</code>: 一次性添加若干动作/上下文对,<code>$contexts</code> 是动作/上下文对的关联数组。它代理 <code>addActionContext()</code>,意味着如果那些动作/上下文对存在,就追加之。
  263. </para></listitem>
  264. <listitem><para>
  265. <code>setActionContexts(array $contexts)</code>: 和 <code>addActionContexts()</code> 一样,但重写已存在的动作/上下文对。
  266. </para></listitem>
  267. <listitem><para>
  268. <code>hasActionContext($action, $context)</code>: 决定一个特定的动作是否有一个给定的上下文。
  269. </para></listitem>
  270. <listitem><para>
  271. <code>getActionContexts($action = null)</code>: 返回或者给定动作的所有上下文,或者所有动作/上下文对。
  272. </para></listitem>
  273. <listitem><para>
  274. <code>removeActionContext($action, $context)</code>: 从给定动作中清除一个或多个上下文。<code>$context</code> 可以是一个单个的上下文,或者是一个上下文数组。
  275. </para></listitem>
  276. <listitem><para>
  277. <code>clearActionContexts($action = null)</code>: 从给定动作或者从有上下文的动作中清除所有上下文。
  278. </para></listitem>
  279. </itemizedlist>
  280. </sect4>
  281. <sect4 id="zend.controller.actionhelpers.contextswitch.initcontext">
  282. <title> 初始化上下文开关 </title>
  283. <para>
  284. 为初始化上下文开关,需要在动作控制器中调用 <code>initContext()</code>:
  285. </para>
  286. <programlisting role="php"><![CDATA[
  287. class NewsController extends Zend_Controller_Action
  288. {
  289. public function init()
  290. {
  291. $this->_helper->contextSwitch()->initContext();
  292. }
  293. }
  294. ]]>
  295. </programlisting>
  296. <para>
  297. 在某些情况下,你想强制使用上下文,例如,如果上下文开关是激活状态,你只想用 XML 上下文,可以通过传递上下文给 <code>initContext()</code> 来完成:
  298. </para>
  299. <programlisting role="php"><![CDATA[
  300. $contextSwitch->initContext('xml');
  301. ]]>
  302. </programlisting>
  303. </sect4>
  304. <sect4 id="zend.controller.actionhelpers.contextswitch.misc">
  305. <title> 另外的功能 </title>
  306. <para>
  307. 用来改变 <code>ContextSwitch</code> 助手行为的方法包括:
  308. </para>
  309. <itemizedlist>
  310. <listitem>
  311. <para>
  312. <code>setAutoJsonSerialization($flag)</code>: 缺省地,JSON 上下文将系列化任何视图变量给 JSON 符号并把它作为响应返回。如果想创建自己的响应,你需要关闭它,这需要在调用 <code>initContext()</code> 之前来完成。
  313. </para>
  314. <programlisting role="php"><![CDATA[
  315. $contextSwitch->setAutoJsonSerialization(false);
  316. $contextSwitch->initContext();
  317. ]]>
  318. </programlisting>
  319. <para>
  320. 用 <code>getAutoJsonSerialization()</code> 来获取 flag 的值。
  321. </para>
  322. </listitem>
  323. <listitem>
  324. <para>
  325. <code>setSuffix($context, $suffix, $prependViewRendererSuffix)</code>: 用这个方法,你可以为给定的上下文指定一个不同的后缀。第三个参数用来指示是否用新的后缀预先准备当前视图解析器,这个 flag 缺省为打开。
  326. </para>
  327. <para>
  328. 传递空值给后缀将导致只有视图解析器的后缀被使用。
  329. </para>
  330. </listitem>
  331. <listitem>
  332. <para>
  333. <code>addHeader($context, $header, $content)</code>: 为给定的上下文添加一个响应头,<code>$header</code> 是头的名称,<code>$content</code> 是为这个头传递的值。
  334. </para>
  335. <para>
  336. 每个上下文可以有多个头,<code>addHeader()</code> 把另外的头添加到头的堆栈。
  337. </para>
  338. <para>
  339. 如果为上下文指定的 <code>$header</code> 已经存在,就抛出一个异常。
  340. </para>
  341. </listitem>
  342. <listitem>
  343. <para>
  344. <code>setHeader($context, $header, $content)</code>: <code>setHeader()</code> 和 <code>addHeader()</code> 一样,但它允许重写已经存在的上下文的头。
  345. </para>
  346. </listitem>
  347. <listitem>
  348. <para>
  349. <code>addHeaders($context, array $headers)</code>: 一次性添加多个头到给定的上下文,代理 <code>addHeader()</code>,如果头已经存在,将抛出异常。<code>$headers</code> 是一个头/上下文对的数组。
  350. </para>
  351. </listitem>
  352. <listitem>
  353. <para>
  354. <code>setHeaders($context, array $headers.)</code>: 象 <code>addHeaders()</code> 一样,但代理 <code>setHeader()</code>,允许重写已存在的头。
  355. </para>
  356. </listitem>
  357. <listitem>
  358. <para>
  359. <code>getHeader($context, $header)</code>: 获取给定上下文的头的值,如果没有发现返回 null。
  360. </para>
  361. </listitem>
  362. <listitem>
  363. <para>
  364. <code>removeHeader($context, $header)</code>: 清除一个单个的给定上下文的头。
  365. </para>
  366. </listitem>
  367. <listitem>
  368. <para>
  369. <code>clearHeaders($context, $header)</code>: 清除所有给定上下文的头。
  370. </para>
  371. </listitem>
  372. <listitem>
  373. <para>
  374. <code>setCallback($context, $trigger, $callback)</code>: 为给定的上下文在给定的触发器设置回调(callback),触发器或者是 'init',或者是 'post'(指明回调将被在上下文初始化时或者派遣后(postDispatch)调用)。 <code>$callback</code> 应当是一个有效的 PHP 回调。
  375. </para>
  376. </listitem>
  377. <listitem>
  378. <para>
  379. <code>setCallbacks($context, array $callbacks)</code>: 为给定的上下文设置多个回调,<code>$callbacks</code> 应当是触发器/回调对。事实上,最常用的可注册的回调有两个:一个为初始化用的,一个是做善后处理。
  380. </para>
  381. </listitem>
  382. <listitem>
  383. <para>
  384. <code>getCallback($context, $trigger)</code>: 在给定的上下文中从给定的触发器中获取一个回调。
  385. </para>
  386. </listitem>
  387. <listitem>
  388. <para>
  389. <code>getCallbacks($context)</code>: 从给定的上下文获取所有的回调,返回一个触发器/回调对数组。
  390. </para>
  391. </listitem>
  392. <listitem>
  393. <para>
  394. <code>removeCallback($context, $trigger)</code>: 从给定的触发器和上下文清除一个回调。
  395. </para>
  396. </listitem>
  397. <listitem>
  398. <para>
  399. <code>clearCallbacks($context)</code>: 为给定的上下文清除所有的回调。
  400. </para>
  401. </listitem>
  402. <listitem>
  403. <para>
  404. <code>setContextParam($name)</code>: 设置请求参数来检查什么时候决定上下文开关是否已经请求,缺省值为 'format',但这个访问器可以用来设置一个备用的值。
  405. </para>
  406. <para>
  407. <code>getContextParam()</code> 用来获取当前的值。
  408. </para>
  409. </listitem>
  410. <listitem>
  411. <para>
  412. <code>setAutoDisableLayout($flag)</code>: 缺省地,当上下文开关出现,布局是关闭的;这是因为一般布局将只用来返回正常的相应,对备用的(alternate)上下文没有意义。然而,如果想使用布局(也许你对新的上下文有个布局),你可以通过传递一个 false 的值给 <code>setAutoDisableLayout()</code> 来改变它的行为。这应当 <emphasis> 在 </emphasis>调用 <code>initContext()</code> <emphasis> 之前 </emphasis> 来做。
  413. </para>
  414. <para>
  415. 用访问器 <code>getAutoDisableLayout()</code> 来获取这个 flag 的值。
  416. </para>
  417. </listitem>
  418. <listitem>
  419. <para>
  420. <code>getCurrentContext()</code> 可用来确定什么上下文被检测到,如果有的话。如果没有上下文开关发生,或者在调用 <code>initContext()</code> 之前调用它,则返回 null。
  421. </para>
  422. </listitem>
  423. </itemizedlist>
  424. </sect4>
  425. <sect4 id="zend.controller.actionhelpers.contextswitch.ajaxcontext">
  426. <title>AjaxContext 函数 </title>
  427. <para>
  428. <code>AjaxContext</code> 助手继承 <code>ContextSwitch</code>,所有 <code>ContextSwitch</code> 函数列表对它也有效,只是有些小小的不同。
  429. </para>
  430. <para>
  431. 首先,它为确定上下文使用不同的动作控制器属性 - <code>$ajaxable</code>。这样你就可以对 AJAX 和 普通的 HTTP 请求使用不同的上下文。<code>AjaxContext</code> 的各种各样 <code>*ActionContext*()</code> 方法将写到这个属性。
  432. </para>
  433. <para>
  434. 其次,由请求对象的 <code>isXmlHttpRequest()</code> 方法来确定,它只有在 XmlHttpRequest 出现后才触发。这样,如果上下文参数 ('format')在请求中传递,但请求没有做成 XmlHttpRequest,不会触发上下文开关。
  435. </para>
  436. <para>
  437. 第三,<code>AjaxContext</code> 添加另外的上下文 - HTML。在这个上下文中,为了区别这个上下文和普通的请求,它设置后缀为 'ajax.phtml'。没有另外的头返回。
  438. </para>
  439. <example id="zend.controller.actionhelpers.contextswitch.ajaxcontext.example">
  440. <title> 允许动作响应 Ajax 的请求 </title>
  441. <para>
  442. 在下面的例子中,我们允许对动作 'view'、 'form' 和 'process' 的请求响应 AJAX 的请求。在头两个例子 'view' 和 'form',返回不更新页面的 HTML 片段;在最后的例子,返回 JSON。
  443. </para>
  444. <programlisting role="php"><![CDATA[
  445. class CommentController extends Zend_Controller_Action
  446. {
  447. public function init()
  448. {
  449. $ajaxContext = $this->_helper->getHelper('AjaxContext');
  450. $ajaxContext->addActionContext('view', 'html')
  451. ->addActionContext('form', 'html')
  452. ->addActionContext('process', 'json')
  453. ->initContext();
  454. }
  455. public function viewAction()
  456. {
  457. // Pull a single comment to view.
  458. // When AjaxContext detected, uses the comment/view.ajax.phtml
  459. // view script.
  460. }
  461. public function formAction()
  462. {
  463. // Render the "add new comment" form.
  464. // When AjaxContext detected, uses the comment/form.ajax.phtml
  465. // view script.
  466. }
  467. public function processAction()
  468. {
  469. // Process a new comment
  470. // Return the results as JSON; simply assign the results as
  471. // view variables, and JSON will be returned.
  472. }
  473. }
  474. ]]>
  475. </programlisting>
  476. <para>
  477. 在客户端,AJAX 库将请求终点 '/comment/view'、 '/comment/form' 和 '/comment/process',并传递 'format' 参数:'/comment/view/format/html'、'/comment/form/format/html' 和 '/comment/process/format/json'。(或者你可以通过查询字符串( query string) 传递参数,如:"?format=json")
  478. </para>
  479. <para>
  480. 假定你的库传递 'X-Requested-With:XmlHttpRequest'头,这些动作将返回适当的响应格式。
  481. </para>
  482. </example>
  483. </sect4>
  484. </sect3>
  485. <!--
  486. vim:se ts=4 sw=4 et:
  487. -->