《Yii 1.1应用程序开发实例》

第四章:使用表单

本章中,我们将涵括以下内容:

  • 写自己的验证
  • 上传文件
  • 添加验证码
  • 定制验证码
  • 用CInputWidget创建一个自定义输入挂件

介绍

有关Yii表单的使用文档写的很详尽,但一些地方仍然需要一些说明和例子。我们在这一章描述他们。

写自己的验证器

Yii提供一套内置的验证器包括了最典型的开发人员的需求和高度的配置。然而,有时候,开发者需要面对的是创建一个定制验证器。

一个好的例子是网站所有权验证。使用Google的服务,要求上传一个文件,文件的名字和内容指定你的网站并检查它是否存在。我们也同样。

有两种实现方法。第一,你能够使用一个类方法作为验证器,第二种方法创建一个分开的类。

准备工作

按照官网上向导的做法使用yiic webapp创建一个新的应用。

怎么做...

  1. 我们将从类方法的方式开始。第一,我们需要实现一个表单模型。所以,创建protected/models/SiteConfirmation.php如下:
<?php
class SiteConfirmation extends CFormModel {
    public $url;
    
    public function rules()
    {
        return array(
            array('url', 'confirm'),
        );
    }
 
    public function confirm($attribute,$params)
    {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $this->url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        $output = curl_exec($ch);
        curl_close($ch);
        if(trim($output)!='code here')
            $this->addError('url','Please upload file first.');
    }
}
  1. 现在你将使用我们的model测试controller。创建protected/ controllers/TestController.php如下
<?php
class TestController extends CController
{
    function actionIndex()
    {
        $confirmation = new SiteConfirmation();
        $confirmation->url = 'http://yiicookbook.org/verify.html';
        if($confirmation->validate())
            echo 'OK';
        else
            echo 'Please upload a file.';
    }
}
  1. 现在常熟运行testcontroller。应该可以正常运行,因为你可以http://yiicookbook.org/verify.html得到代码文本。如果你与另一个人更换确认URL,你首先得到请上传文件因为你没有上传这样的文件。

它是如何工作的...

在SiteConfirmation模型里,我们定义了一个$url字段添加了一个规则方法为这个字段定义了一个单一的验证规则。由于没有内置验证器确认,Yii假设我们想描述验证规则在一个名为confirm的方法里。在这个方法里,我们使用标准PHP的CURL从远程主机得到verify.html文件内容比较它的内容。如果文件内容不同,我们添加一个error使用addError方法。

可选项,我们能够使用两个验证方法参数:$attribute和$params。例如,如果我们指定验证规则在下面的方法里:

array('url', 'confirm', 'param1' => 'value1', 'param2' => 'value3'),

我们将得到$attribute的值设置'url',$params值设置如下:

array (
    'param1' => 'value1'
    'param2' => 'value3'
)

还有更多...

我们可能想要重用这种验证器,我们将其从一个模块方法修改成一个类。所以,创建一个文件命名为protected/components/RemoteFileValidator.php:

<?php
class RemoteFileValidator extends CValidator
{
    public $content = '';
    protected function validateAttribute($object,$attribute)
    {
        $value=$object->$attribute;
        
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $value);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        $output = curl_exec($ch);
        curl_close($ch);
        
        if(trim($output)!=$this->content)
            $this->addError($object,$attribute,'Please upload file first.');
    }
}

普通的验证器类应当扩展CValidator并实现其抽象方法。参数传递给$object,这是一个验证模型的实例,$attribute还是包含验证属性名。传递的参数被分配到相应的公共属性确认器类。

现在我们将使用它。在SiteConfirmation模块,我们将改变验证规则如下:

array('url', 'RemoteFileValidator', 'content' => 'code here'),

我们在这个使用了外部验证器的名字。如果没有方法名称相同的模型并没有相同的命名内置验证器。然后Yii将试图找到一个外部验证器类的名称或路径别名指定。

剩下的代码保持不变,现在您可以重用在其他模型的验证器。

进一步阅读

有关更多信息请参考下面的网站:

  • http://www.yiiframework.com/doc/api/CValidator/
  • http://www.yiiframework.com/doc/api/CModel#rules-detail

上传文件

处理文件上传是一个相当常见的任务对于一个web应用程序,Yii有一些有用的类内置。让我们创建一个简单的形式允许上传ZIP并存储在 protected/uploads。

准备工作

  • 使用yiic webapp创建一个新的应用
  • 在你的protected的目录下创建一个上传目录

怎么做...

  1. 我们将从模块开始,创建protected/models/Upload.php如下:
<?php
class Upload extends CFormModel
{
    public $file;
    
    public function rules()
    {
        return array(
            array('file', 'file', 'types'=>'zip'),
        );
    }
}
  1. 现在我们将开始说明控制器,创建 protected/controllers/UploadController.php
<?php
class UploadController extends Controller
{
    function actionIndex()
    {
        $dir = Yii::getPathOfAlias('application.uploads');
        $uploaded = false;
        
        $model=new Upload();
        
        if(isset($_POST['Upload']))
        {
            $model->attributes=$_POST['Upload'];
            $file=CUploadedFile::getInstance($model,'file');
            if($model->validate()){
                $uploaded = $file->saveAs($dir.'/'.$file->getName());
            }
        }
        
        $this->render('index', array(
            'model' => $model,
            'uploaded' => $uploaded,
            'dir' => $dir,
        ));
    }
}
  1. 最后,视图 protected/views/upload/index.php:
<?php if($uploaded):?>
<p>File was uploaded. Check <?php echo $dir?>.</p>
<?php endif ?>
<?php echo CHtml::beginForm('','post',array
        ('enctype'=>'multipart/form-data'))?>
    <?php echo CHtml::error($model, 'file')?>
    <?php echo CHtml::activeFileField($model, 'file')?>
    <?php echo CHtml::submitButton('Upload')?>
<?php echo CHtml::endForm()?>
  1. 现在运行upload控制器并尝试上传ZIP压缩文件和其他文件,见下面的截图:

它是如何工作的...

我们使用的模型非常简单。我们只定义一个字段$file和一个验证规则使用文件验证器写着"只允许zip文件"。

控制器有点复杂。我们逐行回顾它:

$dir = Yii::getPathOfAlias('application.uploads');
$uploaded = false;
$model=new Upload();
if(isset($_POST['Upload']))
{
    $model->attributes=$_POST['Upload'];

$dir是一个目录实现ZIP的上传。我们将它设置为protected/uploads。$uploaded是一个标志决定是否我们需要显示成功信息。我们创建一个model的实例并且如果提交了表单$_POST的数据填补它。

$file=CUploadedFile::getInstance($model,'file');
if($model->validate()){
    $file->saveAs($dir.'/'.$file->getName());
    $uploaded = true;

然后,我们使用CUploadedFile::getInstance能让我们访问CUploadedFile实例。这是一封装类,当文件上传后,围绕php的$_FILE数组进行填充。

如果我们确定文件是一个ZIP文档通过调用模型的验证方法,然后我们用CUploadedFile::saveAs保存文件。

The rest is passing some values to the view:

<?php if($uploaded):?>
<p>File was uploaded. Check <?php echo $dir?>.</p>
<?php endif ?>

如果$uploaded标志设置为真,则显示信息。

为了上传文件,HTML一定要遵守以下两种重要的要求:

  1. 应该使用post方法
  2. enctype属性应该设置为'multipart/form-data'。

我们使用CHtlm或是 CActiveForm的htmlOptions生成HTML,CHtml使用如下:

<?php echo CHtml::beginForm('','post',array('enctype'=>'multipart/form-data'))?>

The rest is the standard form: We display an error and a field for model's file attribute and render a submit button.

还有更多...

如果上传很多个文件,你应该用下面的方法修改代码:

if(isset($_POST['Upload']))
{
    $model->attributes=$_POST['Upload'];
    $files=CUploadedFile::getInstance($model,'file');
    if($model->validate())
    {
        foreach($files as $file)
            $file->saveAs($dir.'/'.$file->getName());

视图文件,你应该用下面的方式输出文件字段:

<?php echo CHtml::activeFileField($model, "[0]file")?>
<?php echo CHtml::activeFileField($model, "[1]file")?>
<?php echo CHtml::activeFileField($model, "[2]file")?>
文件验证

文件验证器中使用一个模型不仅让我们可以限制某些类型文件,但也设置其他限制,例如文件大小或是文件数量。例如,下面的规则只允许上传图片,文件大小小于一兆字节:

array('file', 'file', 'types'=>'jpg, gif, png', 'maxSize' => 1048576),

进一步阅读

有关更多信息请参考下面的网站:

  • http://www.yiiframework.com/doc/api/CFileValidator
  • http://www.yiiframework.com/doc/api/CUploadedFile

另请参阅

  • 第三章AJAX 和 jQuery

添加验证码

当今,在互联网上,如果你留下一个表单没有垃圾邮件的保护,你将在很短的时间内得到大量的垃圾数据。Yii包括一个验证码组件添加这样一个保护。问题是没有系统化的指导如何让使用它。

在下面的例子中,我们将添加给表单添加一个验证码的保护。

准备工作

  1. 使用yiic webapp创建一个新应用
  2. Create a form model named prot创建一个模型protected/models/EmailForm.php如下:
<?php
class EmailForm extends CFormModel
{
    public $email;
    function rules(){
        return array(
            array('email', 'email'),
        );
    }
}
  1. 创建一个控制器protected/controllers/EmailController.php如下:
<?php
class EmailController extends Controller
{
    public function actionIndex()
    {
        $success = false;
        $model = new EmailForm();
        if(!empty($_POST['EmailForm']))
        {
            $model->setAttributes($_POST['EmailForm']);
            if($model->validate())
            {
                $success = true;
                // handle form here
            }
        }
        $this->render('index', array(
            'model' => $model,
            'success' => $success,
        ));
    }
}
  1. 创建视图protected/views/email/index.php如下:
<?php if($success):?>
<p>Success!</p>
<?php endif?>
<?php echo CHtml::beginForm()?>
    <p>
        <?php echo CHtml::activeLabel($model, 'email')?>
        <?php echo CHtml::activeTextField($model, 'email')?>
        <?php echo CHtml::error($model, 'email')?>
    </p>
    <p>
        <?php echo CHtml::submitButton()?>
    </p>
<?php echo CHtml::endForm()?>
  1. 现在,我们有一个e-mail提交表单的验证字段。

怎么做...

  1. 首先,我们需要定制表单模型。我们需要添加$verifyCode这将保证验证码输入并添加一个验证规则。
<?php
class EmailForm extends CFormModel
{
    public $verifyCode;
    public $email;
    
    function rules(){
        return array(
            array('email', 'email'),
            array('verifyCode', 'captcha', 'allowEmpty'=>
                !CCaptcha::checkRequirements()),
        );
    }
}
  1. 然后,我们需要添加外部的控制器。添加如下代码:
public function actions()
{
    return array(
        'captcha'=>array(
            'class'=>'CCaptchaAction',
        ),
    );
}
  1. 在view中,需要显示一个额外的字段和验证码图片。代码如下:
<?php if(CCaptcha::checkRequirements()&& Yii::app()->user->isGuest):?>
    <p>
        <?php echo CHtml::activeLabelEx($model, 'verifyCode')?>
        <?php $this->widget('CCaptcha')?>
    </p>
    <p>
        <?php echo CHtml::activeTextField($model, 'verifyCode')?>
        <?php echo CHtml::error($model, 'verifyCode')?>
    </p>
<?php endif?>
  1. 现在,运行email控制器检验验证码,如下:

如果没有错误,表单里没有验证码,最大的可能性是你没有安装GD和配置php扩展。GD要求CATCHA生成图片。我们添加 CCaptcha::checkRequirements() ,所以如果图片没有出现将无法使用验证码,但是仍旧在工作。

它是如何工作的...

在一个视图中,我们称之为 CCaptcha挂件使<img标签与src属性指向CCaptchaAction我们添加控制器。在这个action,一个随机的图片生成。代码生成的字符,用户应该输入表单内。它存储用户的会话,并把图片显示给用户。

当用户在表单中输入email和验证码后,我们分配这些值到表单模型然后验证它。对于验证码字段,我们使用CCaptchaValidator。从表单中获取的验证码与用户会话中的验证码进行对比。如果不匹配,则模型数据无效。

还有更多...

如果你限制访问控制器中的操作方法,可以使用控制器中的accessRules方法,然后不要忘记给予每个人访问它:

public function accessRules() {
    return array(
        // ...
        array('allow',
            'actions'=>array('captcha'),
            'users'=>array('*'),
        ),
        array('deny',
            'users'=>array('*'),
        ),
    );
}
进一步阅读:

有关更多信息请参考下面的网站:

  • http://www.yiiframework.com/doc/api/CCaptcha/
  • http://www.yiiframework.com/doc/api/CCaptchaAction/
  • http://www.yiiframework.com/doc/api/CCaptchaValidator/

另请参阅

  • 第二章 Router, Controller, and Views
  • 本章的定制验证码

定制验证码

一个标准的Yii验证码足够好的防止广告发送程序,但是有时候你可能想要设置它,如下:

  • 你面对一个可以读取图片的广告发送程序需要增加更多的挑战。
  • 你想使他更有趣或是更容易输入验证码的文本

在我们的例子中,我们将修改Yii的验证码,它将要求用户解决一个非常简单的算术题而不只重复一个文本一个图像。

准备工作

作为这个例子的一个开始,我们将添加验证码的结果。或者,你可以采取任何形式,使用验证码,因为我们无法选择修改现成的代码。

怎么做...

我们需要 CCaptchaAction生成代码图片。code应该是随机的数,表示应该是一个算数表达式赋予相同的结果。

  1. 创建protected/components/MathCaptchaAction.php如下:
<?php
class MathCaptchaAction extends CCaptchaAction
{
    protected function generateVerifyCode()
    {
        return mt_rand((int)$this->minLength, (int)$this->maxLength);
    }
    
    public function renderImage($code)
    {
        parent::renderImage($this->getText($code));
    }
    
    protected function getText($code)
    {
        $code = (int)$code;
        $rand = mt_rand(1, $code-1);
        $op = mt_rand(0, 1);
        if($op)
            return $code-$rand.»+».$rand;
        else
        return $code+$rand.»-».$rand;
    }
}
  1. 现在,我们控制器里的方法,我们需要用下面我们自己的验证替换CCaptchaAction:
public function actions()
{
    return array(
        'captcha'=>array(
            'class'=>'MathCaptchaAction',
            'minLength' => 1,
            'maxLength' => 10,
        ),
    );
}
  1. 现在,运行表单测试新的验证。它将显示算数表达式1到10,输入答案,见下面的截图:

它是如何工作的...

覆盖两个CCaptchaAction方法。在generateVerifyCode,我们生成随机文本。因为我们需要一个表达式而不是仅仅显示文本,我们覆盖renderImage。生成表达式本身在我们的getText方法里。

还有更多...

为了学习更多有关验证码的修改方法,你可以使用以下资源:

  • http://www.yiiframework.com/doc/api/CCaptcha/
  • http://www.yiiframework.com/doc/api/CCaptchaAction/
  • http://www.yiiframework.com/doc/api/CCaptchaValidator/

另请参阅

  • 第三章 Router, Controller, and Views
  • 本章的添加验证码

用CInputWidget创建一个自定义输入挂件

Yii有一些非常好的表单挂件,但是任何一个框架,Yii没有全部的。在这里我们学习怎样创建我们自己的输入挂件。对于我们的例子,我们将创建一个输入挂件。

准备工作

用yiic webapp创建一个新的应用。

怎么做...

我们从widget开始。

  1. 创建一个widget类 protected/components/RangeInputField.php 如下:
<?php
class RangeInputField extends CInputWidget
{
    public $attributeFrom;
    public $attributeTo;
    
    public $nameFrom;
    public $nameTo;
    
    public $valueFrom;
    public $valueTo;
    
    function run()
    {
        if($this->hasModel())
        {
            echo CHtml::activeTextField
                ($this->model, $this->attributeFrom);
            echo ' &rarr; ';
            echo CHtml::activeTextField
                ($this->model, $this->attributeTo);
        }
        else {
            echo CHtml::textField($this->nameFrom, $this->valueFrom);
            echo ' &rarr; ';
            echo CHtml::textField($this->nameTo, $this->valueTo);
        }
    }
}
    现在我们需要测试它。我们需要一个表单模型protected/models/RangeForm.php:
<?php
class RangeForm extends CFormModel
{
    public $from;
    public $to;
    
    function rules()
    {
        return array(
            array('from, to', 'numerical', 'integerOnly' => true),
            array('from', 'compare', 'compareAttribute' => 'to',
                'operator' => '<=', 'skipOnError' => true),
        );
    }
}
  1. 现在创建一个controller protected/controllers/RangeController. php如下:
<?php
class RangeController extends Controller
{
    function actionIndex()
    {
        $success = false;
        $model = new RangeForm();
        if(!empty($_POST['RangeForm']))
        {
            $model->setAttributes($_POST['RangeForm']);
            if($model->validate())
                $success = true;
        }
        
        $this->render('index', array(
            'model' => $model,
            'success' => $success,
        ));
    }
}
  1. 创建一个视图protected/views/range/index.php如下:
<?php if($success):?>
<p>Success!</p>
<?php endif?>
 
<?php echo CHtml::errorSummary($model)?>
<?php echo CHtml::beginForm()?>
    <?php $this->widget('RangeInputField', array(
        'model' => $model,
        'attributeFrom' => 'from',
        'attributeTo' => 'to',
    ))?>
    <?php echo CHtml::submitButton('Submit')?>
<?php echo CHtml::endForm()?>
  1. 现在,运行range控制器,截图如下:

它是如何工作的...

一个典型的输入挂件能够被作为一个有模型的Active字段挂件或没有模型。Active字段挂件处理值并自动验证。

因为有两个字段在我们的widget里,我们定义三对公共属性:属性,名称,值。属性成对使用,如果有模型传递给目录;这意味着挂件被作为动态输入。如果你想生成输入自定义名称,名称和值对将被使用。

在我们的例子中,我们覆盖run方法呈现两个字段。实际是用CHtml::activeTextField和CHtml::textField处理的。

为了在一个视图中使用widget,我们用CController::widget 如下:

<?php $this->widget('RangeInputField', array(
    'model' => $model,
    'attributeFrom' => 'from',
    'attributeTo' => 'to',
))?>

所有选项中设置一个数组分配给相应的挂件公共属性。

还有更多...

为了学习更多挂件,我们能从下面获得资源:

  • http://www.yiiframework.com/doc/api/CInputWidget/
  • http://www.yiiframework.com/doc/api/CWidget/

另请参阅

  • 第一章, 深入底层
  • 第一章中的配置挂件默认属性。
评论 X

      友荐云推荐
      Copyright 2011-2014. YiiBook.com