Skip to content

【转帖】Zend Framework中Ajax的一个使用技巧

转载本站文章请注明:
转载来至:[记录与PHP的PK经历]
本文链接: 【转帖】Zend Framework中Ajax的一个使用技巧

前言:其实这个技巧,不限于Zend Framework。我对于这个技巧,是来自Ruby On Rails的学习过程中,在Thomas的书中有提到这种ajax场景;而本文章主要讲述这个技巧在Zend Framework中如何实现。

几点说明:
1、本文章的Js部分,采用jQuery;我比较中意这个。但是本文章侧重讲的是一个思路;而不是js的写法技巧。
2、阅读本文章前,最好对Zend Framework了解;并了解Zend_Layout的概念。

一个场景的处理思考

先从一个场景来入手。假设你有一个请求时http://project-name/product/list,来显示商品列表。而,普通情况下,你的这个
请求来自左侧菜单栏;请求方式是一个ajax请求;即不更新菜单栏以及header
footer,只更新在<divid=’mainbody’></div>的内容。

这是一个常见的ajax应用场景。

一般情况下,是没什么问题。但是有一天一个聪明的用户知道这个url(知道方法很多),而直接在浏览器上输入http://project-name/product/list,那么结果呢?
你显示了一块不带有header footer 的html代码,你的界面里没有定义css,因此显示很难看;没有js,可能很多点击无效无法工作。

用户不懂这些,他只知道出现error。

这个情况,你遇到过吗?我的应用中,其实就遇到过;之前的解决办法没有,只是希望用户别这么聪明。

好了,问题明白了;解决之前,我们先一起看看Zend_Layout的使用方法。
更多的Zend_Layout的学习,可以先阅读 http://akrabat.com/2007/12/11/simple-zend_layout-example/的内容 以及官方文档 http://framework.zend.com/manual/en/zend.layout.html

首先建立一个普通的Zend_Layout使用例。

复制PHP内容到剪贴板

PHP代码:

private function _buildMVCLayout(){
//增加layout
//$this->_config 是一个Zend_Config_Xml类;内容来自一个配置文件。
if(!empty($this->_config->view->layout->enable)){
$_viewPath = trim($this->_config->view->layout->path);
$_viewPath = empty($_viewPath)?‘layout’:$_viewPath;
Zend_Layout::startMvc(array(‘layoutPath’=>APP_DIR.DIRECTORY_SEPARATOR.$_viewPath));
}
}

那么,在layout的目录下,建立一个lauout.phtml

复制PHP内容到剪贴板

PHP代码:

<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Strict//EN” ”http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd”>
<
html xmlns=“http://www.w3.org/1999/xhtml” lang=“en”>
<
head>
<
meta http-equiv=“content-type” c />
<
meta name=“description” c />
<
meta name=“keywords” c />
<
link rel=“icon” type=“image/x-icon” href=“<?php echo $this->baseUrl; ?>/public/images/layout/favicon.ico” />
<
title><?php echo empty ( $this->title ) ? $this->translate ( ‘meta.title.default’ ) : $this->escape ( $this->title ); ?></title>
<?php
echo $this->loadCss ( array (‘layout’ ), $this->baseUrl );
echo
$this->loadJs ( array (‘jquery-1.2.1.pack’, ‘loading’, ‘jquery/jquery.extend’, ‘jquery/jquery.action’), $this->baseUrl );
?>
</head>
<!– Global IE fix to avoid layout crash when single word size wider than column width –>
<!–[if IE]><style type=”text/css”> body {word-wrap: break-word;}</style><![endif]–>
<body>
<div class=”page-container”>
<div class=”header”>
<div class=”header-top”>
<img class=”loading_position” style=”display:none” id=”loading” src=”<?php echo $this->baseUrl; ?>/public/images/icons/loading-large.gif” />
<?php echo $this->layout()->header_top; ?>
<?php echo $this->layout()->header_nav; ?>
</div>
</div>
<div class=”main”>
<div class=”main-navigation”>
<?php echo $this->layout()->sidebar_menu; ?>
</div>
<div class=”main-content” id=”mainbody”>
<?php echo $this->layout()->content ?>
</div>
</div>
<div class=”footer”>
<p>Copyright &#169; 2006 qqinxl | All Rights Reserved</p><p class=”credits”>Design by <a href=”http://www.1-2-3-4.info/” title=”Designer Homepage”>Wolfgang</a> | Modified by <a href=”#” title=”Adaptor Homepage”>qqinxl</a> </p>
</div>
</div>
</body>
</html>

这是一个典型的layout布局;其实该模板来自http://www.1-2-3-4.info/ 。利用$this->translate()
可以做到Zend_View内国际化显示;主要内容是在$this->layout()->content内显示;即在<div
id=”mainbody”></div>内显示(这个id名字很关键)。

用户如果请求一个url为http://project-name/product/list,那么Zend_Framework首先执行
ProductController->listAction()
,渲染product/list.phtml,然后填充到layout.phtml内。

这里开始思考一个Ajax场景。

假设存在两个连接(比如在左侧的菜单栏内),一个发出的是Ajax请求,一个是普通的link连接

复制PHP内容到剪贴板

PHP代码:

//Ajax请求
<a id=“link1″ href=“#”><?php echo $this->translate ( ‘menu.product.list’ ) ;?></a>
//普通的link连接
<a id=”link2″ href=”<?php echo $this->baseUrl;?>/product/list”><?php echo $this->translate ( ‘menu.product.list’ ) ;?></a>
<script type=”text/javascript”>
//onclick 事件在这里
//尽量html和js分离
$(document).ready(function() {
$(”#link1″).click(function () {
var url = ’<?php echo $this->baseUrl;?>/product/list’;
$.ajaxRequest(’mainbody’,url);
return false;
});
});
</script>

$.ajaxRequest就是发出一个url请求(参数2),执行后更新div名为mainbody(参数1)的内容。这个js代码后面给出。

一个希望的结果是,如果发出的是link连接(id=link2),当然是整个页面刷新,当然希望除了显示product/list.phtml 还是渲染layout.phtml的内容。
而如果发出的是ajax请求(id=link1),应该关闭layout.phtml;只显示list.phtml内容。

如果这个问题解决了,很明显,第一部分中,我提到的那个聪明的用户直接输入url请求的问题也随着解决了。其实,用户直接输入就是一个普通link请求。

利用Zend Framework 实现

我们只需要在继承Zend_Controller_Action的时候增加如下代码:

复制PHP内容到剪贴板

PHP代码:

public function preDispatch(){
//MVC
if ($this->getRequest ()->isXmlHttpRequest ()) {
$this->getHelper ( ‘layout’ )->disableLayout ();
}else{
$response = $this->getResponse ();
$response->insert ( ‘header_top’, $this->view->render ( ‘basic/header.top.phtml’ ) );
if (empty (
$this->_loginUser ) or ! $this->_loginUser instanceof Object_User) {
$response->insert ( ’sidebar_menu’, $this->view->render ( ‘basic/sidebar.menu.no.login.phtml’ ) );
} else {
$response->insert ( ‘header_nav’, $this->view->render ( ‘basic/header.nav.phtml’ ) );
$response->insert ( ’sidebar_menu’, $this->view->render ( ‘basic/sidebar.menu.phtml’ ) );
}
}
}

看到了没有 ,$this->getRequest ()->isXmlHttpRequest () 这是一个判断ajax请求的函数。

复制PHP内容到剪贴板

PHP代码:

//Zend_Controller_Request_Http类内
/**
* Is the request a Javascript XMLHttpRequest?
*
* Should work with Prototype/Script.aculo.us, possibly others.
*
* @return boolean
*/
public function isXmlHttpRequest()   {
return (
$this->getHeader(‘X_REQUESTED_WITH’) == ‘XMLHttpRequest’);
}

官方解释在这里 http://framework.zend.com/manual … r.request.http.ajax
当然 jQuery 支持这一函数的判断。

如果是ajax请求 通过 $this->getHelper ( ‘layout’ )->disableLayout (); 关闭layout
否则 渲染lauout的内容;在例子中,我还通过是否存在 当前登录用户loginUser,来渲染不同左侧菜单栏sidebar_menu。

这是一个非常有用的技巧。

另外一个应用

$this->getRequest ()->isXmlHttpRequest () 经常还应用在另外一个ajax场景内。

比如,我们提交一个表单;我们希望通过ajax提交,并将结果显示在一个叫<div id=fm-intro></div>内;而不是刷新整个页面。
一个常见的写法是:

复制PHP内容到剪贴板

PHP代码:

$this->view->title = $this->_ ( ‘product.new’);
//产生一个form
$url = $this->_baseUrl . ‘/’ . $this->_pageInfo->getModuleUrl() . ‘product/new’;
$form = new Form_Product_New( );
$form->setAttrib ( ‘action’, $url );
$this->_formData = null;
if (
$this->getRequest ()->isPost ()) {
$this->getActionController ()->getHelper ( ‘viewRenderer’ )->setNoRender ();
$this->_formData = $this->getRequest()->getPosts ();
if (
$this->_form->isValid ( $this->_formData )) {
//注册
$module = Module_Product::getInstance();
$check = $module->insert($this->_formData);
if (empty (
$check )) {
//失败
$this->view->errors = $this->_module->translateError ();
$this->view->message = $this->_ ( ‘product.fail’);
} else {
//成功
$this->view->message = $this->_ ( ‘product.success’, $options );
}
} else {
//注册前验证失败
$this->view->message = $this->_ ( ‘product.check’);
$this->view->errors = $this->_form->translateError ();
}
$this->render(‘message’);
return;
}
//如果没有post数据,显示原始form
$this->view->form = $this->_form;
$this->render ();

这种写法 是没问题的;缺点和上面一样,在浏览器js失效的情况下,无法正确显示页面。因此,一个好的写法是

复制PHP内容到剪贴板

PHP代码:

// $this->render ( ’message’ );的地方增加判断
if ($this->getRequest ()->isXmlHttpRequest ()) {
$this->render ( ‘message’ );
} else {
$this->render ();
}

更进一步

更进一步,我们在页面显示部分可以这么做;我们在制作sidebar_menu的时候,完全不考虑ajax;首先生成普通的link连接。

复制PHP内容到剪贴板

PHP代码:

<h1 class=“first”><?php echo $this->translate ( ’sidebar.menu.user.title’ );?></h1>
<!– Navigation with bullets –>
<dl class=”nav3-bullet” id=”sidebar_menu”>
<dt>
<a href=”<?php echo $this->baseUrl;?>/product/list”>
<?php echo $this->translate ( ’sidebar.menu.products.list’ );?>
</a>
</dt>
<dt>
<a href=”<?php echo $this->baseUrl;?>/product/register”>
<?php echo $this->translate ( ’sidebar.menu.products.new’ );?>
</a>
</dt>
<dt>
<a href=”<?php echo $this->baseUrl;?>/auth/logout”>
<?php echo $this->translate ( ’sidebar.menu.logout’ );?>
</a>
</dt>
</dl>

很明显,根据上面的设置,这些普通连接都可以正常工作;只是每次都需要刷新整个页面。

为了加速响应速度,采用ajax请求;一个笨方法是,去修改每一个连接,将href的值变成”#”,增加一个onclick请求,写一段js代码。
这样做,除了麻烦之外,一个明显的缺点是,如果用户浏览器不支持js,那么这些连接全部无效。

一个简单的办法如下:不修改原来的html任何东西,只是将普通连接转化为ajax连接

复制PHP内容到剪贴板

PHP代码:

<script type=“text/javascript”>
$(
document).ready(function() {
$(
“#sidebar_menu a[@href!='']“).toAjaxLink();
});
</script>

那么toAjaxlink()方法呢?如下代码:

复制PHP内容到剪贴板

PHP代码:

//jquery.action.js
//toAjaxLink()函数目前参数只有一个toId。其实可以扩展很多东西,比如点击后首先加载某些js css 然后在ajax请求。
(function ($) {
$.
fn.toAjaxLink = function (options) {
return
this.each(function () {
options = $.extend({
toId:“mainbody”
}, options || {});
$(
this).attr(“id”,$(this).attr(“href”)).attr(“href”,“#”);
$(
this).click(function () {
var
url = $(this).attr(“id”);
$.
ajaxRequest(options.toId,url);
return
false;
});
});
};
})(
jQuery);
//下面是ajaxRequest的代码
//jquery.extend.js
(function($) {
$.
extend({
ajaxRequest: function(toId,url,pars,type,evalScript) {
$.
ajax({
url: url,
type: (type === null || type === )?‘GET’:type,
dataType: ‘html’,
data: pars,
evalScripts: (evalScript === null || evalScript === || evalScript === false)?false:true,
error: function(){
//error Function
},
success: function(html){
$(
‘#’+toId).html(html);
}
});
return
false;
}
})(
jQuery);

如果用户浏览器js不执行,也就是$(”#sidebar_menu a[@href!='']“).toAjaxLink(); 不工作了,那么连接还是原来的普通连接,一切不变;
那么如果执行 $(”#sidebar_menu a[@href!='']“).toAjaxLink(); ,根据定义连接变成一个ajax连接,请求变成ajax请求。

其实这个技巧,和Zend  Framework没有任何关系;可以应用在很多地方。最重要的这种思路解决浏览器端js不支持的问题。而且还减轻服务器端
代码量;我们几乎不需要考虑ajax情况怎么办 非ajax怎么办。比如很多分页类
都有isAjaxLink之类的参数。生成一个ajax请求的分页连接;这种思考增加了服务器端代码工作量。

思路总结:
1、连接请求,是否是ajax请求,还是普通link请求,不是在服务器端设定;而是在客户端变换。

2、请求内容的返回,服务器端可以根据ajax请求,还是普通link请求,来做不同处理。

好了,现在这个聪明的用户直接在浏览器上输入http://project-name/product/list,Ok 没问题,让他自由输入去~~

原帖:http://bbs.phpchina.com/viewthread.php?tid=56702&pid=406076&page=1&extra=page%3D1#pid406076

zend framework ajax(2)php 判断是否Ajax请求(1)Zend_Layout 关闭mvc(1)php view 页 类(1)php framework排名(1)zend framework baseurl(1)zend framework mvc 应用(1)$this->getRequest()->isPost()(1)zend framework使用ajax(2)zend layout佈局(1)isXmlHttpRequest() head(1)zend ispost(2)ajax 点击无效(1)zendframework image problem(1)PHP MVC view(1)

中文关键字:php 服务器 jquery 代码 framework zend vi css 文件 ajax 技巧 简单 目录 速度 点击

Post a Comment

Your email is never published nor shared. Required fields are marked *
*
*
Note: Commenter is allowed to use '@User+blank' to automatically notify your reply to other commenter. e.g, if ABC is one of commenter of this post, then write '@ABC '(exclude ') will automatically send your comment to ABC. Using '@all ' to notify all previous commenters. Be sure that the value of User should exactly match with commenter's name (case sensitive).