AJAX in CakePHP
Cake 中的 AJAX 實(shí)質(zhì)
CakePHP(以下簡稱“Cake”,本文使用的版本是 0.10.7.1856 RC3)對 AJAX 的支持是建立在 prototype 與 script.aculo.us 之上的,其自身并沒有包含服務(wù)器端、客戶端的 AJAX 實(shí)現(xiàn),所以想要在 Cake 中熟練地使用 AJAX,必須首先熟悉 prototype 和 script.aculo.us,而本文的重點(diǎn)并不是這兩個出眾的 JavaScript 庫。說白了,Cake 實(shí)際上只是簡化了繁瑣的 AJAX JavaScript 代碼。
Cake 中的 AJAX 相關(guān)文件
在 Cake 中,與 AJAX 相關(guān)的文件只有兩個:
- cake/cake/libs/view/templates/layouts/ajax.thtml
- cake/cake/libs/view/helpers/ajax.php
ajax.thtml 文件是執(zhí)行 AJAX 動作之后用于輸出的布局,和一般的布局文件不同的是它是一個空的布局視圖文件,沒有 header/footer 等等內(nèi)容。ajax.php 文件則是用于 Cake 視圖文件的 AJAX 輔助類 AjaxHelper,此類中包含了很多 AJAX 動作的相關(guān)方法,詳細(xì) API 請參考 http://api.cakephp.org/class_ajax_helper.html。
除了上述的兩個文件外,我們還需要 Prototype 的 prototype.js 和 script.aculo.us 中的 *.js 文件,這些文件可在官方網(wǎng)站上下載到,將這些 *.js 文件放置 cake/app/webroot/js/ 目錄下即可。對于這兩個庫,本文用的版本分別是 1.4.0 和 1.5.1。
Hello, AJAX world!
現(xiàn)在我們使用一個簡單的示例簡單演示一下如何在 Cake 中使用 AJAX。這個示例將實(shí)現(xiàn)點(diǎn)擊鏈接之后,在頁面上加載服務(wù)器端輸出的“Hello, AJAX world!”信息。為了簡單起見,示例中不使用任何數(shù)據(jù)庫,也就是不用 Cake 的模型(Model),而只用控制器(Controller)和視圖(View)。首先創(chuàng)建一個布局視圖文件 cake/app/views/layouts/demo.thtml,用于自定義布局以及加載需要用到的 JavaScript 文件,此文件內(nèi)容如下:
<!DOCTYPE html PUBLIC ”-//W3C//DTD XHTML 1.0 Transitional//EN”
”http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>
<html xmlns=”http://www.w3.org/1999/xhtml”>
<head>
<title>CakePHP AJAX Demo :: <?php echo $title_for_layout?></title>
<?php echo $html->charsetTag(‘UTF-8′)?>
<?php echo $javascript->link(‘prototype’)?>
<?php echo $javascript->link(’scriptaculous’)?>
</head>
<body>
<div id=”container”>
<center><h2>CakePHP AJAX Demo</h2></center>
<div id=”content”><?php echo $content_for_layout?></div>
</div>
</body>
</html>
重點(diǎn)在于 $javascript->link() 的那兩行,包含了需要用到的 JavaScript 文件 prototype.js 和 scriptaculous.js,而 script.aculo.us 庫中的其他 JavaScript 文件都由 scriptaculous.js 統(tǒng)一進(jìn)行包含,不用我們書寫額外的代碼,如果并不想使用 script.aculo.us 的所有 js 文件,可以使用 load 參數(shù)進(jìn)行指定包含,比如我們只用到 effects.js,可以這樣(多個則用英文逗號“,”分割開):
<?php echo $javascript->link(’scriptaculous.js?load=effects’)?>
接下來是創(chuàng)建一個控制器文件 cake/app/controllers/demos_controller.php:
<?php
class DemosController extends AppController
{
var $name = ‘Demos’; //兼容 PHP4
var $layout = ‘demo’; //指定視圖使用的布局為 demo.thtml
var $helpers = array(‘Html’, ‘Javascript’, ‘Ajax’); //需要用到的視圖輔助類
function index()
{
}
function hello()
{
sleep(1); //本地測試時,為了更好地看到效果,模擬延遲狀態(tài)
$this->layout = ‘ajax’; //此方法為 AJAX 動作,所以布局需要使用 ajax.thtml
}
} ///:~
?>
此控制器的重點(diǎn)有三個:
- 設(shè)置默認(rèn)布局為 demo.thtml,這樣才能在 index 視圖中使用 prototype.js 等
- 在視圖輔助類數(shù)組 $helpers 中加上“Ajax”輔助類
- 對于 AJAX 動作方法 hello() 需要將其當(dāng)前布局設(shè)置為“ajax”
對應(yīng)于控制器中的兩個方法(Action),我們需要在 cake/app/views/demos/ 目錄下創(chuàng)建兩個視圖文件:index.thtml 和 hello.thtml。
index.thtml:
<div id=”loading” style=”display:none;padding:4px;color:black;
background-color:#FAD163;width:100px”><strong>Loading…</strong></div>
<div id=”view” style=”display:none;background-color:#E8EEF7;
padding:4px;border:1px solid silver;width:300px”></div>
<p>
<?php
//設(shè)置 AJAX 選項(xiàng)
$options = array(
//設(shè)置加載成功之后需要進(jìn)行更新的元素為 view
‘update’ => ‘view’,
//加載過程中隱藏 view 元素,顯示“Loading…”字樣
‘loading’ => “Element.hide(’view’);Element.show(’loading’)”,
//加載成功之后隱藏 loading,同時顯示 view 元素
‘complete’ => “Element.hide(’loading’);Effect.Appear(’view’)”
);
//使用 AjaxHelper 創(chuàng)建 AJAX 動作鏈接
echo $ajax->link(‘Click here!’, ‘/demos/hello’, $options);
?>
</p>
這個視圖中有三個元素:loading、view 與 AJAX 鏈接。初始狀態(tài)下 loading 與 view 是隱藏的(display:none),只有點(diǎn)擊了 AJAX 鏈接之后,在加載狀態(tài)中顯示 loading,加載完成之后將其隱藏,然后顯示 view,“Hello, AJAX world!”即顯示在 view 中。此視圖的重點(diǎn)在于 $ajax->link(),$ajax 是 AjaxHelper 的對象實(shí)例,link() 方法的第一個參數(shù)是鏈接顯示的文本,第二個參數(shù)是 Cake 的 URL,這里的 URL 為 /demos/hello,指向了 AJAX 動作方法 hello(),此動作最終輸出視圖 hello.thtml,第三個參數(shù)為 AJAX 選項(xiàng),Cake 會自動根據(jù)選項(xiàng)生成鏈接中使用到的 JavaScript 代碼。對于一個簡單的 AJAX 動作,主要就是設(shè)置三個選項(xiàng):update、loading 與 complete,這幾個選項(xiàng)的意義在視圖代碼的注釋中都有了詳細(xì)說明。
最后就是 hello.thtml 視圖文件了,只是一行簡單的文本:
<center>Hello, AJAX world!</center>
OK,通過盡量少的編碼,我們完成了這個示例,可以通過 http://www.somesite.com/cake/demos/ 瀏覽最終效果。
Live Search
Live Search 指的是即時查詢,通常是用戶在文本框中輸入想要查詢的關(guān)鍵字,由客戶端 JavaScript 對文本框進(jìn)行觀察,監(jiān)測到用戶輸入之后即時提交到服務(wù)器,并顯示服務(wù)器返回的結(jié)果。接下來我們將使用 Cake 的 AJAX 實(shí)現(xiàn) Live Search,從一個數(shù)組中獲取符合查詢關(guān)鍵字的數(shù)據(jù),然后即時更新到頁面中。
首先修改我們的控制器 demos_controller.php,增加一個方法 search():
<?php
function search()
{
$langs = array(
‘C’, ‘C++’, ‘C#’,
‘Java’, ‘JavaScript’,
‘PHP’, ‘Perl’, ‘Python’,
‘Ruby’, ‘Delphi’);
$this->layout = ‘ajax’;
if (empty($this->params[‘form’][‘livesearch’])) { //未提交任何數(shù)據(jù)
$result = $langs;
} else { //根據(jù)提交的關(guān)鍵字進(jìn)行查詢
$word = $this->params[‘form’][‘livesearch’];
$result = array();
foreach ($langs as $lang)
if (stristr($lang, $word) !== false)
$result[] = $lang;
}
$this->set(‘result’, $result);
}
?>
在 index.thtml 視圖文件中,加入 Live Search 的表單代碼:
<form onsubmit=”return false”>
<p>
<b>Live Search:</b>
<input type=”text” name=”livesearch” id=”livesearch” />
</p>
</form>
<?php
//設(shè)置 AJAX 選項(xiàng)
$options = array(
‘update’ => ‘view’,
//處理查詢的 URL,對應(yīng)于控制器中的 search() 方法
‘url’ => ‘/demos/search’,
//觀察頻率,單位為“秒”
‘frequency’ => 1,
‘loading’ => “Element.hide(’view’);Element.show(’loading’)”,
‘complete’ => “Element.hide(’loading’);Effect.Appear(’view’)”
);
echo $ajax->observeField(‘livesearch’, $options);
?>
這里用到了 AjaxHelper 中的另一個方法 observeField(),此方法用于觀察某個元素的數(shù)據(jù)是否發(fā)生變化,發(fā)生變化時則調(diào)用相應(yīng)的 AJAX 操作。方法的第一個參數(shù)為需要觀察的元素 id,這里是“livesearch”文本框,第二個參數(shù)為 AJAX 選項(xiàng),和 link() 方法中的相似,只不過這里的選項(xiàng)設(shè)置多了 url 與 frequency,url 即 Cake 的 URL,對應(yīng)于控制器中的方法,frequency 則是觀察的間隔時間,單位為“秒”,即每隔多少秒查看一下對應(yīng)的元素是否發(fā)生了改變。
對應(yīng)于 search() 方法,創(chuàng)建視圖文件 cake/app/views/demos/search.thtml:
<?php if (count($result) > 0): ?>
<ul>
<?php foreach ($result as $lang): ?>
<li><?php echo $lang ?></li>
<?php endforeach ?>
</ul>
<?php else: ?>
<font color=”gray”>Found nothing!</font>
<?php endif ?>
好了,這個 Live Search 已經(jīng)完成了,是不是很簡單 現(xiàn)在只要在文本框中輸入字符,就會在 $langs 數(shù)組中查找,只要包含了查詢關(guān)鍵字的結(jié)果都會被返回并更新到 view 元素中。
其他應(yīng)用
Cake 的 AjaxHelper 中還提供了很多方法,如用于拖曳的 drag()、drop() 及 dropRemote();用于排序的 sortable();用于自動完成的 autoComplete() 等等,由于涉及到的講解篇幅比較大,暫時就不深入了,有時間的話我再一一道來。drag/drop 可以在我寫的 Cake Framework AJAX Demo 中看到在線演示,并可下載到源代碼。
一些問題
Cake 當(dāng)前版本(0.10.7.1856 RC3)的一些方法還不是非常完善,對于最新版本的 script.aculo.us 庫中的一些參數(shù)還不支持,相信很快會得到更新。實(shí)際應(yīng)用中可能會碰到中文亂碼的問題,那是因?yàn)?XMLHttpRequest 獲取到的數(shù)據(jù)都是以 UTF-8 編碼的,所以解決的辦法有兩個,一個是所有頁面、數(shù)據(jù)庫數(shù)據(jù)均使用 UTF-8 編碼,這樣可以省去很多麻煩,另外一個就是通過 PHP 的 iconv() 函數(shù)進(jìn)行轉(zhuǎn)碼,但這需要用到 iconv 擴(kuò)展,所以比較麻煩一些,而且無形中為服務(wù)器增加了額外的負(fù)擔(dān)。
相關(guān)資源
- Prototype
- script.aculo.us
- Cake AjaxHelper Api
- Cake Framework AJAX Demo
- CakePHP Ajax Helper
- Using Ajax (and company) with Cake
- AJAX in CakePHP 示例源代碼