CActiveForm讲解javascript
CActiveForm提供了一些方法,这些方法可以方便的去建立一个与数据模型相关联的Form表单。CActiveForm继承自CWidget,因此他须要实现CWidget的init() 和 run() 方法,同时它也实现了一些本身的封装方法。CActiveForm很重要的一个特性就是它支持Ajax校验。咱们能够设置CActiveForm的enableAjaxValidation属性为ture来启动Ajax校验。例如,当用户在input框中输入一些值后就会触发Ajax校验。CActiveForm会向服务器提交Ajax请求,用来校验用户当前输入的值。服务器的校验通常是调用模型类Model的validate()方法。若是校验失败,相对应的错误信息将会被返回并显示给用户。即便用户在浏览器禁用javascript,他也会经过整个页面的提交自动回滚到传统的页面验证。php
在客户端,Yii认为input框能够存在四个状态:初始化,校验,错误和成功。为了区分这些状态,CActiveForm自动指定了不一样的CSS样式给包含此input框的HTML
element。默认状况下,这些CSS样式类的名字为:validating,error,success。固然咱们可使用CActiveForm的options属性去自定义他们。html
CActiveForm的提交和校验是基于Ajax模式的。若是你的Form表单中有很大量的数据须要提交,那么这种Ajax模式的提交可能就不那么好了。这种状况下,你能够设计本身轻量级的Ajax校验。使用Yii对JQuery的支持?java
使用CActiveForm来作Ajax校验,咱们须要使用两个JS库:jquery.js和jquery.yiiactiveform.js。他们的位置在工程根目录下:assets\5ce53e17\文件夹中。不用担忧,这些JS库Yii会自动发布到你的工程中。虽然这些动做Yii会悄悄的作,当你必须知道。jquery
首先咱们来看咱们的模型类,这里咱们使用CFormModel:ios
1 class LoginForm extends CFormModel 2 { 3 public $username; 4 public $password; 5 public function rules() 6 { 7 return array( 8 array('username, password', 'required'), 9 array('username', 'authenticate'), 10 ); 11 } 12 public function authenticate($attribute,$params) 13 { 14 if($this->username=='admin') 15 $this->addError('username','can not login with admin!'); 16 } 17 }
咱们的模型类很是简单,只有username和passsword两个属性。username的校验规则是不为空,且不能是“admin”,而password的校验规则只是不为空。注意,当咱们检查到用户输入的username的值为“admin”时候,校验失败,咱们添加错误信息为“can
not login with admin”,这段信息会在页面中的Yii提供的error元素中输出。ajax
咱们能够在rules方法中指定任何一条验证规则的错误信息,咱们须要使用message属性。例如:array(‘username’, ‘required’,'message’=>’not
null’),当‘username’的内容为空的时候,咱们会使用message指定的内容在页面上显示错误信息。固然咱们可使用中文,若是出现乱码,能够这样设置:array(‘username’,
‘required’,'message’=>iconv(“gb2312″,”utf-8″,”不能为空“))数据库
你将main.php里的app配置加上language=>’zh_cn’,系统默认的提示就是中文的了!数组
其余验证规则案例:浏览器
array(‘title, content,
status’, ‘required’),
array(‘title’, ‘length’,
‘max’=>128),
array(‘status’, ‘in’,
‘range’=>array(0, 1, 2)),
array(‘tags’, ‘match’,
‘pattern’=>’/^[\w\s,]+$/’,'message’=>’Tags can only contain word
characters.’),
咱们的页面代码以下:
1 <div class="form"> 2 <?php $form=$this->beginWidget('CActiveForm', 3 array('id'=>'login-form','enableAjaxValidation'=>true,'action'=>array('site/login'))); ?> 4 <div class="row"> 5 <?php echo $form->labelEx($model,'username'); ?> 6 <?php echo $form->textField($model,'username'); ?> 7 <?php echo $form->error($model,'username'); ?> 8 </div> 9 <div class="row"> 10 <?php echo $form->labelEx($model,'password'); ?> 11 <?php echo $form->passwordField($model,'password'); ?> 12 <?php echo $form->error($model,'password'); ?> 13 </div> 14 <div class="row submit"> 15 <?php echo CHtml::submitButton('Login'); ?> 16 </div> 17 <?php $this->endWidget(); ?> 18 </div>
对于每个模型属性的页面表示来讲,咱们都会有一个Label来讲明其属性名,还有一个input框用来输入该属性值,最后有个error元素来显示校验失败时的错误信息。Yii对Form及其子元素封装的太多,咱们将以上php代码翻译成静态页面为:
1 <div class="form"> 2 <form id="login-form" action="/validator/index.php?r=site/login" method="post"> 3 <div class="row"> 4 <label for="LoginForm_username" class="required">Username 5 <span class="required">*</span></label> 6 <input name="LoginForm[username]" id="LoginForm_username" type="text" value="" /> 7 <div id="LoginForm_username_em_" class="errorMessage" style="display:none"></div> 8 </div> 9 <div class="row"> 10 <label for="LoginForm_password" class="required">Password 11 <span class="required">*</span></label> 12 <input name="LoginForm[password]" id="LoginForm_password" type="password" value="" /> 13 <div id="LoginForm_password_em_" class="errorMessage" style="display:none"></div> 14 </div> 15 <div class="row submit"> 16 <input type="submit" name="yt0" value="Login" /> 17 </div> 18 </form> 19 </div>
Yii的Ajax的校验效果是经过CSS来实现的,上文提到一个input框能够存在四个状态:初始化,校验,错误和成功。每一个状态对应不一样CSS样式类。初始化和校验这两个状态咱们不须要关心,咱们只注重用户校验失败和成功时,该input框的CSS样式。此外当校验失败的时候,咱们还要定义错误信息的样式,从翻译过来的静态文原本来,咱们须要定义errorMessage类。如下是个人CSS类:
/*标签是否换行*/
.form label {
font-size:12px; display:block; }
/*属性是否必填项,客户端根据model的rule方法而定*/
.form span.required {
color:red; }
/*校验出错时标签CSS样式*/
.form .error label {
color:#FFCC33; }
/*校验出错时输入框的CSS样式*/
.form .error input {
background:#FEE; border-color:#C00; }
/*校验出错时错误信息的CSS样式,咱们能够设置其display属性让其不换行。*/
.form .errorMessage {
display:inline; color:red; font-size:12px; }
/*校验成功时标签CSS样式*/
.form .success label {
color:#000000; }
/*校验成功时输入框的CSS样式*/
.form .success input {
background:#E6EFC2; border-color:#C6D880; }
以上的CSS注释已经很详细了,咱们来看看校验流程,首先咱们须要先渲染咱们的页面:
$model = new LoginForm;
$this->render(‘index’,array(‘model’=>$model));
注意,这里咱们必须使用render()方法对其进行渲染。
咱们的actionLogin以下:
public function actionLogin() { $model = new LoginForm; //ajax validate if(isset($_POST['ajax']) && $_POST['ajax']==='login-form') { echo CActiveForm::validate($model); Yii::app()->end(); } //submit handle and validate if(isset($_POST['LoginForm'])) { $model->attributes = $_POST['LoginForm']; if($model->validate()){ //no business logic handle $this->renderPartial('success'); Yii::app()->end(); } } $this->render('index',array('model'=>$model)); }
当input框失去焦点的时候,页面会向服务器提交Ajax请求,服务器端就会根据模型类LoginForm中定义的校验规则就其就行验证。若是校验成功,则input框会按指定的success样式类去显示,若是校验失败,则input框会按指定的error样式类去显示,同时还会按照errorMessage指定的样式类显示错误信息。
须要注意的是,若是咱们对用户输入的信息进行了校验,并且有可能校验失败,可是用户在校验失败的状况下,仍然能够提交Form表单。因此当用户提交Form表单的处理代码中,咱们仍须要对用户的输入进行校验。
CActiveForm其余组件的使用
1. textArea
这个组件没有太多讲的,主要注意行列的属性配置,使用代码:
<?php echo
$form->textArea($model,’textArea’,array(‘rows’=>10,’cols’=>50)); ?>
2. fileField
虽然Yii封装了这个组件,可是它并无作更多的支持,它的上传须要更多的配置,它也不支持Ajax校验。使用代码:
<?php echo $form->fileField($model,’fileField’);
?>
3. radioButtonList
这是一个radio集合组件。使用代码:
<?php echo
$form->radioButtonList($model,’radioButtonList’,
array(’1′=>’Male’,’0′=>’Female’),
array(‘separator’=>’ ’,'labelOptions’=>array(‘class’=>’radiolabel’))
)?>
Yii框架封装的元素集合组件大体为4个参数:
$model,$property,$data,$htmlOptios
前面两个参数是咱们关联模型类和指定的属性。第三个参数是一个数组,他是元素集合组件的数据来源,上面的代码中配置了两个数组元素,则会对应生成两个radio,radio的值为1或者0,radio的标签为Male或者Female。Yii默认将两个radio之间使用<br/>间隔,即两个radio不在同一行上,咱们可使用separator属性更改其间隔方式,这里咱们使用空格符来间隔两个radio,这样他们显示在同一行上。另外在同一个radio中,其标签和实体也是换行的,缘由在于标签<label>会换行,咱们能够给label标签添加CSS样式,改变其display值为inline将其与radio实体排列在一行。
radioButtonList对应模型类中的属性值不是数组,只是一个单一数值或者字符而已,虽然他是一个集合组件,可是他是单选的,因此最终只有一个单一值提交服务器端。
另外值得注意的是,Yii封装的这些组件的初始值不可以在标签中设置,Yii自动会从模型类中读取属性值,而后在组件上显示出来。因此,若是你想在页面渲染前初始化一些组件的默认值,那么你能够直接初始化模型类就能够了。
4. checkBoxList
这是一个checkBox集合组件,使用代码:
<?php echo $form->checkBoxList($model,’checkBoxList’, array(’1′=>’Football’,’2′=>’Music’,’3′=>’Game’,’4′=>’basketball’), array(‘separator’=>’ ’,'labelOptions’=>array(‘class’=>’checkboxlabel’)) )?>
这个组件同上,惟一不一样的是这个组件是多选的,因此他对应的模型类的属性应该是一个数组。这个组件将你选中的每一个checkBox的值构形成一个数组提交服务器端。例如咱们选中了Football和Game,那个该组件构造的数组将是array(‘1’,’3’),没有选中的checkBox不会被构造进这个数组中。反之从服务器段读取数组,而后显示该组件也是一样的道理。
5. listBox
本质上它是一个select,可是它会显示全部的option。使用代码以下:
<?php echo $form->listBox($model,’listBox’, array(’1′=>’Football’,’2′=>’Music’,’3′=>’Game’,’4′=>’basketball’), array(‘size’=>8,’multiple’=>false,’class’=>’listbox’) )?>
须要说明的属性‘size’表示该select的大小,虽然我只定义了4个option,可是我仍然想让它占据8个option的高度。属性multiple为是否多选。属性class为select的CSS样式类,可是貌似不起做用。在须要说明的一点是,虽然它能够单选和多选,可是他对应的模型类的属性始终是一个数组。
6. dropDownList
本质上它是一个真正意义上select,由于他不会显示全部的option,使用代码以下:
1 <?php 2 3 $models = 4 person::model()->findAll(array(‘order’=>’age’)); 5 6 $list = CHtml::listData($models,’id’,'username’); 7 8 echo $form->dropDownList($model,’dropDownList’,$list,array(‘empty’=>’Select 9 a user’) 10 11 )?>
这个组件的使用跟select差很少,对应模型层的属性是一个单一数值或者字符,而不是数组。属性empty指定了select的第一个option,至关于初始化值。
这里咱们须要说明的是以上这些集合组件的数据来源。本质上其实咱们就是查询数据库,将结果集封装成一个数组,其实查询数据结果集原本就是一个数组。这里咱们使用AR类person按年龄查询全部的记录,而后使用CHtml的listData方法将查询记录中的id字段和username字段构形成一个简单的数组$list,而后咱们只须要在dropDownList的标签配置中应用便可。
最后咱们介绍一下服务器端获取页面数据,由于咱们已经将模型类和咱们的页面标签相关联,因此使用标签显示模型类属性或者从显示标签中获取模型类属性是十分简单的。在服务器端咱们获取页面数据的时候,可使用以下代码:
$model = new Form; $model->attributes = $_POST['Form'];
以上代码会将用户提交的数据自动填充到模型类的属性中,这个方式称为安全特性分配。首先咱们的页面中必须已经将标签和模型类Form关联。须要注意的是$_POST的参数就是模型类的名称Form,而不是标签名称。最重要的问题在于咱们模型类中的属性必须是安全的,不然咱们的安全特性分配将会失败。指定模型类中的属性为安全的是经过实现模型类中的
Rules方法,即校验规则方法。咱们能够直接将模型类的指定属性指定为”safe”或者指定其余校验规则,由于Yii由于若是一个属性经过某个校验后它就能够被认为是安全的了。
public function rules() { return array( array(‘property’,'safe’) ); }
CactiveDataProvider讲解
CactiveDataProvider是基于ActiveRecord的一个数据提供者,同时它也继承自CDataProvider。它提供了ActiveRecord对象的集合。咱们可使用“modelClass”属性指定CactiveDataProvider所要提供的ActiveRecord对象的类型。CactiveDataProvider可使用AR方法CActiveRecord::findAll从数据库中检索信息。它还可使用criteria来指定查询条件,排序以及分页等等。
CActiveDataProvider的时候方法以下:
$dataProvider=new CActiveDataProvider('Post', array( 'criteria'=>array( 'condition'=>'status=1 AND tags LIKE :tags', 'params'=>array(':tags'=>$_GET['tags']), 'with'=>array('author'), ), 'pagination'=>array( 'pageSize'=>20, 'currentPage'=>0, ), ));
在Yii的blog案例中,很不少地方用到了CactiveDataProvider,尤为是在读取不少数据且须要分页的状况下,CactiveDataProvider是很出色的。CactiveDataProvider只是一个数据的集合,并非一个显示组件,因此在blog案例中,它是和CGridView一块儿使用的,CGridView确切的说是一个使用表格显示数据的widget。其实包含了不少的封装,在这里咱们不是用CGridView,只使用CActiveDataProvider来进行分页查询。使用CActiveDataProvider进行分页查询最重要的是“currentPage”属性,默认它是0。
在以上的CActiveDataProvider使用代码中,咱们配置了它的两个属性:criteria和pagination。
Criteria属性值就是CDbCriteria类的实例,而pagination值并不是一个CPagination类的实例,而是一个数组而已。实例化CPagination类须要表中记录总数做为参数。
如下是我action的代码:
public function actionPage() { $currentPage = 0; $pageSize = 5; if(isset($_GET['id'])){ $currentPage = $_GET['id']; } $criteria = new CDbCriteria(array('order'=>'age desc',)); $pagination = array('currentPage'=>$currentPage,'pageSize'=>$pageSize,); $dataProvider = new CActiveDataProvider('person',array('pagination'=>$pagination,'criteria'=>$criteria,)); $totalItemCount = $dataProvider->getTotalItemCount(); $pageCount = ceil($totalItemCount/$pageSize); $itemCount = $dataProvider->getItemCount(); $page = array('totalItemCount'=>$totalItemCount, 'pageCount'=>$pageCount,'itemCount'=>$itemCount,'currentPage'=>$currentPage,); $data = $dataProvider->getData(); $this->render('all',array('data'=>$data,'page'=>$page,)); }
以上代码咱们将数据信息data和分页信息page分开存放,并传递到被渲染的页面。上文提到了“currentPage”,实际上是CPagination类的一个属性,它默认是从0开始的,即0表明第一页。在个人代码中,我将pageSize设置成5,即每页显示5条信息。CDbCriteria类能够帮助咱们进行复杂的数据库查询,并且代码书写很清晰。这里我只是查询个人person表,而后按照年龄排序。pagination值并不是一个CPagination类的实例,而是一个数组而已。因此我将pagination配置成一个数组,数组元素只有currentPage和pageSize两个。最后咱们即可以实例化CActiveDataProvider,而且按咱们的配置查询数据库了。
虽然咱们能够只向页面传递CActiveDataProvider对象,可是这样会使得咱们的页面很繁杂,因此我将数据信息data和页面信息page分离开来,而后传递到被渲染的页面。CActiveDataProvider类的getData()方法返回检索数据结果集,类型为数组。咱们能够是使用foreach循环读取data里面的信息:
<?php foreach($data as $person){ echo "<tr height='30'>"; echo "<td>".$person['id']."</td>"; echo "<td>".$person['username']."</td>"; echo "</tr>"; } ?>
页面信息page的构建其实很简单,咱们获取到总的记录数,而后每页显示5条,天然咱们会获得总的页码数。咱们也能够获取该页下的记录数,由于最后一页的记录数不必定是5。咱们将这些信息封装成page数组,传递到被渲染的页面上以供使用。固然咱们还能够封装更多的信息,固然这取决于你是怎么显示分页信息,以及显示什么信息。
CHtml::link()的使用
方法说明:
public static string
link(string $text, mixed $url=’#', array $htmlOptions=array ( ))
例如:
<?php echo
CHtml::link(‘Link Text’,array(‘controller/action’)); ?>
HTML输出为:
<a
href=”index.php?r=controller/action”>Link Text</a>
带参数的:
<?php echo
CHtml::link(‘Link Text’,array(‘controller/action’,'param1′=>’value1′));
?>
HTML输出为:
<a
href=”index.php?r=controller/action¶m1=value1″>Link
Text</a>
多参数的:
<?php echo
CHtml::link(‘Link Text’,array(‘controller/action’,
’param1′=>’value1′,’param2′=>’value2′));
?>
HTML输出为:
<a
href=”index.php?r=controller/action¶m1=value1¶m2=value2″>Link
Text</a>
额外参数的:
<?php echo
CHtml::link(‘Link Text’,array(‘controller/action’,'param1′=>’value1′),
array(‘target’=>’_blank’); ?>
HTML输出:
<a
target=”_blank”
href=”index.php?r=controller/action¶m1=value1″>Link
Text</a>
绝对路径:
<?php echo
CHtml::link(‘Link Text’,array(‘/controller/action’)); ?>
指定模块下的路径:
<?php echo
CHtml::link(‘Link Text’,array(‘/module-id/controller/action’)); ?>
<?php echo
CHtml::linkButton(‘LinkName’,
array(‘submit’=>array(‘controller/action’,'param’=>’value’),’confirm’=>”Are
you sure?”,)); ?>
无效连接:
echo CHtml::link(‘LinkName’,”,array(‘href’=>’javascript:void(0)’));
HTNL输出:
<a href=”javascript:void(0)”>LinkName</a>
URL生成:
$route =
“site/test”;
$params =
array(‘id’=>100);
$url =
$this->createUrl($route,$params);
Widget的使用
继承 CWidget 以及重载它的init() 和 run() 方法,能够定义一个新的 widget:
class MyWidget extends
CWidget
{
public function init()
{
// this method is called by
CController::beginWidget()
}
public function run()
{
// this method is called by
CController::endWidget()
}
}
Widget能够像一个控制器同样拥有它本身的视图。默认的, widget 的视图文件位于包含了widget文件的views 子目录之下。这些视图能够经过调用 CWidget::render() 渲染,这一点和控制器很类似。惟一不一样的是,widget的视图没有布局文件支持。同时,view 文件中的 $this 指的是 widget 实例而不是 controller 实例。
如今咱们建立一个 PageCode widget(分页码):
首先咱们须要在Web应用下的components包下建立自定义的widget:
components/ PageCode.php:
<?php
class PageCode extends
CWidget
{
public $page = array();
public function run()
{
$this->render(‘pagecode’,array(‘page’=>$this->page));
}
}
其次咱们还须要建立该widget所须要的视图文件pagecode.php:
components/views/pagecode.php:
<?php
echo
CHtml::link(‘ first ’,array(‘site/page’,'id’=>0));
if($page['currentPage']<=0){
echo
CHtml::link(‘ previous ’,”,array(‘href’=>’javascript:void(0)’));
}else{
echo
CHtml::link(‘ previous ’,array(‘site/page’,'id’=>$page['currentPage']-1));
}
echo “|”;
if($page['currentPage']>=$page['pageCount']-1){
echo
CHtml::link(‘ next ’,”,array(‘href’=>’javascript:void(0)’));
}else{
echo
CHtml::link(‘ next ’,array(‘site/page’,'id’=>$page['currentPage']+1));
}
echo “|”;
echo
CHtml::link(‘ last ’,array(‘site/page’,'id’=>$page['pageCount']-1));
echo
“ ”;
echo
“ ”;
echo “current page
:”.($page['currentPage']+1);
echo
“ ”;
echo “total page
:”.$page['pageCount'];
echo
“ ”;
echo “total record
:”.$page['totalItemCount'];
echo
“ ”;
echo
“ ”;
如今咱们就能够在咱们的php页面上使用该widget了:
<?php
$this->widget(‘application.components.PageCode’,array(‘page’=>$page));
?>
CCaptcha的使用
使用CCaptcha的话,你不得不使用CCaptchaAction,这两个都是Yii提供的,CCaptcha本质上是一个widget,而CCaptchaAction则是一个action类。
CCaptcha的做用只是显示校验码图片和注册Js脚本,而CCaptchaAction才是核心,它负责生成校验码和图片,而且支持Ajax校验。在CCaptcha生成的静态页面中,咱们能够看到生成的<img/>标签请求的地址正是该CCaptchaAction。
实际上,CCaptchaAction将生成的校验码是放在Session中的,这样有利于校验。咱们使用CCaptcha,通常是将其做为Model的一个属性的,并在该Model的rules方法中指定该属性的校验规则为“captcha”。暂时这样理解。
使用CCaptcha注意:
1. 在Model中声明一个属性,其校验规则为“captcha”。
2. 在须要渲染CCaptcha的Controller中重写actions 方法,将CCaptchaAction导入进来,做为当前Controller的一个action,此action的ID为“captcha”。由于生成的<img>请求的地址是当前Controller的captcha。
3. 在须要渲染CCaptcha的页面中,添加input以对应Model中声明的属性。
4. 在须要渲染CCaptcha的页面中,使用<?php $this->widget(‘CCaptcha’); ?>
这样,当咱们在input输入框中填完CCaptcha生成的校验码的时候,就会触发Ajax校验,本质上就是执行CCaptchaAction的validate方法,该方法会获取用户输入的校验码,而后从Session中获取正确的校验码,而后比对。
CCaptchaAction是怎么样生成校验码的?
CCaptchaAction的generateVerifyCode()方法用来生成校验码。默认状况下,校验码的长度为6或者7,固然这个能够自定义。YII将26个英文字母分红两组,一组21个,一组5个。而后按必定的随机规则从其中一组中选出一个字母。
CCaptchaAction是怎么样生成图片的?
CCaptchaAction的renderImage()方法用来生成图片。该方法中依次指定图片的宽度,高度,背景色,背景色是否透明,前景色,字体(字体采用Duality.ttf,在CCaptchaAction同目录下咱们能够看到该字体文件)。图片字体的不规则排列是由字体大小,angle,位置来决定的,这些参数会在必定范围内随机生成。
Yii上传文件
CUploadedFile的使用:
$image =
CUploadedFile::getInstance($model,’image’);
$name =
$image->getName();
$size =
$image->getSize();
$type =
$image->getType();
$file =
“E:/temp.jpg”;
$image->saveAs($file,true);