Yii Framework 整合Extjs Direct实现RPC的方法

本文介绍如何使用ExtJS前端框架与Yii后端框架整合,实现类似CRM系统的会员管理功能。通过ExtJS的Direct组件,可以轻松地将服务器端方法暴露给客户端调用,实现RPC批量调用和正常MVC页面流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这些天由于公司需要开发一个类似CRM的会员管理系统,我的技术选型为 Extjs+Yii+Mysql实现,发现Extjs3.x后推出了Direct组件,可以很方便的将服务器端的方法暴露给客户端调用,非常方便,于是就有了整合Yii框架的想法。

说干就干,花了一天时间进行研究、实现代码,Yii的组件架构和OOP的特性,让我比较方便的就实现了这个目标:兼容RPC批量调用和正常的MVC页面流程。

具体实现了几个类:
[list]
[*]ExtApplication,是CApplication的继承类,覆盖了ProcessRequest方法,区分普通页面action和RPC调用action等。
[*]Controller,是CController的继承类,覆盖若干方法以调用ExtAction
[*]ExtAction,是CAction的继承类,用于执行RPC方法
[*]ApiAction,也是CAction的继承类,用于暴露服务器端方法
[*]ExtDirect_API和ExtDirect_CacheProvider这两个类是direct自带的类,稍作修改。
[/list]

将以上类文件放入项目的protected/components下即可,然后修改入口文件index.php为:


<?php

// change the following paths if necessary
$yii=dirname(__FILE__).'/../yii-trunk/framework/yii.php';
$config=dirname(__FILE__).'/protected/config/main.php';

// remove the following lines when in production mode
defined('YII_DEBUG') or define('YII_DEBUG',true);
// specify how many levels of call stack should be shown in each log message
defined('YII_TRACE_LEVEL') or define('YII_TRACE_LEVEL',3);

require_once($yii);
require(dirname(__FILE__).'/protected/components/ExtApplication.php');
Yii::createApplication('ExtApplication',$config)->run();



然后修改 protected/views/layouts/main.php,加入extjs引用和代码:

<link rel="stylesheet" type="text/css" href="/ext3/resources/css/ext-all.css" />
<link rel="stylesheet" type="text/css" href="/css/main.css" />

<script type="text/javascript" src="/ext3/adapter/ext/ext-base-debug.js"></script>
<script type="text/javascript" src="/ext3/ext-all-debug.js"></script>
<script type="text/javascript" src="/ext3/src/locale/ext-lang-zh_CN.js"></script>
<script src="<?php echo $this->createUrl('/site/api') ?>"></script>
<script type="text/javascript">
Ext.BLANK_IMAGE_URL = '/ext3/resources/images/default/s.gif';
var mk;
Ext.onReady(function(){
// Notice that Direct requests will batch together if they occur
// within the enableBuffer delay period (in milliseconds).
// Slow the buffering down from the default of 10ms to 100ms
Ext.app.REMOTING_API.enableBuffer = 100;
Ext.Direct.addProvider(Ext.app.REMOTING_API);
// provide feedback for any errors
Ext.QuickTips.init();
// turn on validation errors beside the field globally
Ext.form.Field.prototype.msgTarget = 'side';
});
</script>


这段代码“<script src="<?php echo $this->createUrl('/site/api') ?>"></script>”就是调用了接口页面,得到了服务器端暴露的方法列表,如:

Ext.ns('Ext.app'); Ext.app.REMOTING_API = {"url":"\/index.php","type":"remoting","actions":{"Site":[{"name":"DoLogin","len":1,"formHandler":true},{"name":"Logout","len":0},{"name":"updateBasicInfo","len":1,"formHandler":true},{"name":"getBasicInfo","len":2},{"name":"GetPhoneInfo","len":0},{"name":"getLocationInfo","len":1}],"Test":[{"name":"Index","len":0}]}};


然后这句:“Ext.Direct.addProvider(Ext.app.REMOTING_API);“就是向extjs注册了rpc的api。

这样准备工作就完成了。

接着添加apiAction的定义到 SiteController.php中:

/**
* Declares class-based actions.
*/
public function actions()
{
return array(
'api'=>array(
'class'=>'ApiAction',
),
);
}

然后在Controllers中定义普通action和rpc方法:


public function actionLogin()
{
$this->render('login');
}

/**
* @remotable
* @formHandler
*/
public function actionDoLogin($formPacket)
{
$model=new LoginForm;

// collect user input data
if(isset($formPacket['username']))
{
$model->username = $formPacket['username'];
$model->password = $formPacket['password'];

// validate user input and redirect to the previous page if valid
if($model->validate() && $model->login())
$output['success'] = true;
else
{
$output['success'] = false;
$output['errors'] = $model->errors;
}
return $output;
}
}


注意上面的 “
/**
* @remotable
* @formHandler
*/”
注释,表示DoLogin这个方法是可以远程调用的,且是表单处理方法,这里的备注可以在反射的时候用到。而且可以发现普通页面action和RPC的action的写法上的区别:一个是没有参数的,render页面或者直接输出内容的;一个是有参数的,然后有返回值的。

actionLogin对应的视图文件中就可以调用DoLogin方法来登录用户了:

var indexUrl = '<?php echo $this->createUrl('index') ?>';

Ext.onReady(function(){
function login(){
if(form.getForm().isValid()){
form.getForm().submit({
waitMsg: '正在登录,请稍候...',
waitTitle:'登录中',
success: function(form,action){
loginwin.hide();
window.location = indexUrl;
}
});
}
}
var form = new Ext.form.FormPanel({
baseCls: 'x-plain',
labelWidth: 55,
defaultType: 'textfield',
title: '<div style="padding:10px;text-align:center;font-size:22px; border-bottom:silver solid 1px; margin-bottom:5px;">会员管理系统</div>',
api: {submit: Site.DoLogin },
items: [{
fieldLabel: '用户名',
name: 'username',
allowBlank: false,
blankText: "请输入您的用户名",
anchor:'92%',
listeners: {
specialkey : function(field, e) {
if (e.getKey() == Ext.EventObject.ENTER) {
login();
}
}
}
},{
inputType: "password",
fieldLabel: '密 码',
allowBlank: false,
blankText: "请输入您的密码",
name: 'password',
anchor: '92%' ,
listeners: {
specialkey : function(field, e) {
if (e.getKey() == Ext.EventObject.ENTER) {
login();
}
}
}
}],
buttons: [{
text: '登录',
handler: login
},
{text: '重设', handler: function(){form.getForm().reset();}}
],
buttonAlign:'center'
});

var loginwin = new Ext.Window({
title: "管理登录",
width: 300,
height: 180,
modal: true,
draggable: false,
closable: false,
tools: [{id: "help", handler: function(){Ext.Msg.alert('帮助','输入您的用户名和密码登录系统。')}}],
layout: 'fit',
plain:true,
bodyStyle:'padding:5px;',
items: form,
resizable: false
});
loginwin.show();
});


注意FormPanel中的 “api: {submit: Site.DoLogin },”,这样表单的处理请求就自动被发送到了SiteController的actionDoLogin方法,其处理结果也自动返回给客户端了,是不是很方便?

以上涉及到的完整代码已附上,需要的朋友拿去吧,有问题别忘了反馈,一起改进完善。

效果截图:

[img]http://dl.iteye.com/upload/attachment/370035/b7d35a9f-ba73-364b-8f69-21d7bba5ec1c.jpg[/img]

[img]http://dl.iteye.com/upload/attachment/370037/c4a46730-38c2-3653-a385-4368564adc1f.jpg[/img]

以上截图表现的是如何处理表单,下面说说如何调用服务器端其他方法,如我们在SiteController中定义:

/**
* @remotable
* put your comment there...
* This method configured with len=2, so 2 arguments will be sent
* in the order according to the client side specified paramOrder
* @param Number $userId
* @param String $foo
* @return Array response packet
*/
function actiongetBasicInfo($userId, $foo){
return array(
'success'=>true,
'data'=>array(
'foo'=>$foo,
'name'=>'Aaron Conran',
'company'=>'Ext JS, LLC',
'email'=>'aaron@extjs.com'
)
);
}

/**
* Handler for client side form sumbit
* @formHandler
* @remotable
*
* @param Array $formPacket Collection of form items along with direct data
* @return Array response packet
*
*/
function actionUpdateBasicInfo($formPacket){
$response = array();
$email = $formPacket['email'];
if ($email == 'aaron@extjs.com') {
$success = false;
$response['errors'] = array(
'email'=>'already taken'
);
} else {
$success = true;
}
$response['success'] = $success;
// return form packet for demonstration/testing purposes
$response['debug_formPacket'] = $formPacket;
return $response;
}



接下来就可以在客户端js中这么调用:

var basicInfo = new Ext.form.FormPanel({
// configs for FormPanel
title: 'Basic Information',
border: false,
padding: 10,
buttons:[{
text: 'Submit',
handler: function(){
basicInfo.getForm().submit({
params: {
foo: 'bar',
uid: 34
}
});
}
}],

// configs apply to child items
defaults: {anchor: '-20'}, // provide some room on right for validation errors
defaultType: 'textfield',
items: [{
fieldLabel: 'Name',
name: 'name'
},{
fieldLabel: 'Email',
msgTarget: 'side',
name: 'email'
},{
fieldLabel: 'Company',
name: 'company'
}],

// configs for BasicForm
api: {
// The server-side method to call for load() requests
load: Site.getBasicInfo,
// The server-side must mark the submit handler as a 'formHandler'
submit: Site.UpdateBasicInfo
},
// specify the order for the passed params
paramOrder: ['uid', 'foo']
});


这样一来,这个表单的初始数据加载和提交,就都是RPC自动调用了,是不是很赞?呵呵
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值