框架登录

By admin in 编程 on 2019年5月4日

表单的生成
表单中的方法

原地址:

    ActiveForm::begin()方法
    ActiveForm::end()方法
    getClientOptions()方法
    其它方法:errorSummary、validate、validateMultiple

 

表单中的参数

最近在利用Yii
2.0框架进行项目后台的编写,遇到的第一个问题是用户登陆,包括利用cookie,session登陆等等,笔者从源码角度结合实例为各位详细解析如何编写一个完整的用户登陆模块。(笔者的本地环境是PHP
5.5+MySQL5.6) 

    表单form自身的属性
    表单中各个项(field)输入框相关的属性
        $fieldConfig
        关于验证的属性
        关于每个field容器样式的属性
    ajax验证
    前端js事件
    表单中的其它属性

一、准备


开始编写代码之前,我们需要思考一下:用户登陆模块,实现的是什么功能?很明显,是登陆功能,那么,登陆需要用户名和密码,我们在数据库的一张表中就应该
准备好用户名和密码的字段,再思考一下,如果要实现自动登陆的功能,那么还需要什么?Cookie,是专门用于自动登陆的,所以,我们的数据表可能需要准
备一个字段,专门用于储存客户端登陆所生成的cookie,这样,就能通过验证客户端和服务端的cookie是否相同来进行自动登陆了。基于以上思考,我
们的数据表应该包含以下字段:

id(primarykey,auto_increment),username(varchar),password(varchar(32)),auth_key(varchar(32)),accessToken(varchar(32))(这个暂不解释,后文解释).

   
 1、首先,建立一个数据库:myDatabase,

   
 2、然后建立一张数据表:user,增加上述字段。

   
 对于如何建数据库和建表,这里不再赘述。

二、模型(Model)

Yii框架采用MVC设计模式,所以Model是一个模块的核心所在,所以我们先完成对Model的编写。

      1、LoginForm.php

用户登陆模块,所提交的是username和password,所以我们要先建立一个Model,专门处理用户提交的数据,所以先新建一个LoginForm.php,以下为代码:

 

[php] view
plain
copy
print?

  1. <?php  
  2.   
  3. namespace app\modules\backend\models;  
  4.   
  5. use Yii;  
  6. use yii\base\Model;  
  7.   
  8. /** 
  9.  * LoginForm is the model behind the login form. 
  10.  */  
  11. class LoginForm extends Model  
  12. {  
  13.     public $username;  
  14.     public $password;  
  15.     public $rememberMe = true;  
  16.   
  17.     private $_user = false;  
  18.   
  19.   
  20.     /** 
  21.      * @return array the validation rules. 
  22.      */  
  23.     public function rules()<span style=”white-space:pre”>     </span>//①  
  24.     {  
  25.         return [  
  26.             // username and password are both required  
  27.             [[‘username’, ‘password’], ‘required’,’message’=>””],  
  28.             // rememberMe must be a boolean value  
  29.             [‘rememberMe’, ‘boolean’],  
  30.             // password is validated by validatePassword()  
  31.             [‘password’, ‘validatePassword’],  
  32.         ];  
  33.     }  
  34.   
  35.     /** 
  36.      * Validates the password. 
  37.      * This method serves as the inline validation for password. 
  38.      * 
  39.      * @param string $attribute the attribute currently being validated 
  40.      * @param array $params the additional name-value pairs given in the rule 
  41.      */  
  42.     public function validatePassword($attribute, $params)  
  43.     {  
  44.         if (!$this->hasErrors()) {  
  45.             $user = $this->getUser();  
  46.   
  47.             if (!$user || !$user->validatePassword($this->password)) {  
  48.                 $this->addError($attribute, ‘Incorrect username or password.’);  
  49.             }  
  50.         }  
  51.     }  
  52.   
  53.     /** 
  54.      * Logs in a user using the provided username and password. 
  55.      * @return boolean whether the user is logged in successfully 
  56.      */  
  57.     public function login()  
  58.     {  
  59.         if ($this->validate()) {  
  60.             if($this->rememberMe)  
  61.             {  
  62.                 $this->_user->generateAuthKey();//③  
  63.             }  
  64.             return Yii::$app->user->login($this->getUser(), $this->rememberMe ? 3600*24*30 : 0);  
  65.         }  
  66.         return false;  
  67.     }  
  68.   
  69.     /** 
  70.      * Finds user by [[username]] 
  71.      * 
  72.      * @return User|null 
  73.      */  
  74.     public function getUser()  
  75.     {  
  76.         if ($this->_user === false) {  
  77.             $this->_user = User::findByUsername($this->username); //②  
  78.         }  
  79.   
  80.         return $this->_user;  
  81.     }  
  82. }  

该Model是根据basic模板自带的LoginForm修改而成,代码中大多有注释,这里关注以下代码:

①号代
码处是rules规则,rules规则定义了填充过来的数据的规则,验证所填的数据是否为空,是否符合格式之类的,其中有一栏是password,对应的
规则是validatePassword,会自动调用当前类的validatePassword()方法,注意与下文的User类对应的方法区分。

②号代码,调用了User类里面的findByUsername方法,这个User类下面会写到,主要是为了返回一个AR类实例,与当前LoginForm的数据进行比较。

③号代码,这里暂时不提,等讲到cookie登陆的时候再提。

2、User.php

(1)ActiveRecord 类


完成LoginForm后,我们还缺少一些东西,从用户接受到数据了,那么还需要从数据库取出相应的数据来进行比较,所以我们接下来需要完成的是一个从数
据库获取的数据的类——AR类,全称是ActiveRecord,活动记录类,方便用于查找数据,只要类名和数据表的表名相同,那么它就能从这个数据表中
获取数据,比如说这样:

 

[php] view
plain
copy
print?

  1. <?php  
  2. namespace app\modules\backend\models;  
  3. use yii\db\ActiveRecord;  
  4.   
  5. class User extends ActiveRecord{       } ?>  

此外,还能自己添加返回的表名,只要在这个类中重写以下方法:

 

[php] view
plain
copy
print?

  1. public static function tableName(){  
  2.         return ‘user’;  
  3.     }  

(2)IdentityInterface
接口


般来说,从数据库查找数据,只需要继承AR类即可,但是,我们这个是用户登录模型,核心是验证,所以自然需要实现核心的验证功能,就像LoginForm
模型提到的validatePassword一样,实际的验证逻辑是在当前的User模型完成的。一般来说,实现IdentityInterface接
口,需要实现以下方法:

 

[php] view
plain
copy
print?

  1. public static function findIdentity($id);  //①  
  2.   
  3. public static function findIdentityByAccessToken($token, $type = null);   //②  
  4.   
  5. public function getId();    //③  
  6.   
  7. public function getAuthKey();   //④  
  8.   
  9. public function validateAuthKey($authKey);    //⑤  

①findIdentity:是根据id查找数据表对应的数据

②findIdentityByAccessToken
是根据AccessToken(上文提到的)查找对应的数据,而AccessToken我们在数据表也有这个字段,那么它到底有什么用呢?其实
AccessToken在我们当前的用户登陆模型中用处并不大,它是专门用于Resetful登陆验证用到的,具体可自行百度,这里不展开说明。

③getId:返回当前AR类所对应的id

④getAuthKey:返回当前AR类所对应的auth_key

⑤validateAuthKey:这个方法比较重要,是我们后面要讲到的cookie登陆验证的核心所在。

好了,既然知道了这五个方法的用处,那么我们在我们的User.php实现接口,然后重写以上方法,完整的User.php的代码如下:

 

[php] view
plain
copy
print?

  1. <?php  
  2. namespace app\modules\backend\models;  
  3. use yii\db\ActiveRecord;  
  4.   
  5. class User extends ActiveRecord implements \yii\web\IdentityInterface  
  6. {  
  7.   
  8.     public static function tableName(){  
  9.         return ‘user’;  
  10.     }  
  11.   
  12.     public static function findIdentity($id){  
  13.         return static::findOne($id);  
  14.     }  
  15.   
  16.     public static function findIdentityByAccessToken($token,$type=null){  
  17.         return static::findOne([‘accessToken’=>$token]);  
  18.     }  
  19.   
  20.     public static function findByUsername($username){     //①  
  21.         return static::findOne([‘username’=>$username]);   
  22.     }  
  23.   
  24.     public function getId(){  
  25.         return $this->id;  
  26.     }  
  27.   
  28.     public function getAuthkey(){  
  29.         return $this->auth_key;  
  30.     }  
  31.   
  32.     public function validateAuthKey($authKey){  
  33.         return $this->auth_key === $authKey;  
  34.     }  
  35.   
  36.     public function validatePassword($password){          //②  
  37.         return $this->password === md5($password);  
  38.     }  
  39.   
  40.    <span style=”white-space:pre”> </span> /** 
  41.     <span style=”white-space:pre”>    </span> * Generates “remember me” authentication key 
  42.     <span style=”white-space:pre”>    </span> */  
  43.         public function generateAuthKey()                    //③  
  44.         {  
  45.        <span style=”white-space:pre”>     </span>$this->auth_key = \Yii::$app->security->generateRandomString();  
  46.        <span style=”white-space:pre”>     </span>$this->save();  
  47.         }  
  48.   
  49. }  
  50. ?>  

这里分析其中的三个方法:

①findByUsername():在LoginForm的代码中,引用了这个方法,目的是根据用户提交的username返回一个在数据表与username相同的数据项,即AR实例。

②validatePassword():这里对用户提交的密码以及当前AR类的密码进行比较。

③generateAuthKey():生成随机的auth_key,用于cookie登陆。

 

到此,我们完成了Model的编写,一共写了两个Model类:LoginForm和User,一个用于接收用户提交的数据,一个用于获取数据库的数据,接下来我们编写Controller.

三、控制器(Controller)

控制器,主要是用于数据的提交,把用户提交的数据填充到相应的模型(Model)中,然后根据模型返回的信息进一步渲染视图(View),或者执行其他逻辑。

   
 这里,把控制器命名为LoginController.php,以下是完整的实现代码:

 

[php] view
plain
copy
print?

  1. <?php  
  2.   
  3. namespace app\controllers;  
  4.   
  5. use Yii;  
  6. use yii\filters\AccessControl;  
  7. use yii\web\Controller;  
  8. use yii\filters\VerbFilter;  
  9. use app\models\LoginForm;  
  10. use app\models\ContactForm;  
  11.   
  12. class SiteController extends Controller  
  13. {  
  14.     public function actionIndex()  
  15.     {  
  16.         return $this->render(‘index’);  
  17.     }  
  18.   
  19.     public function actionLogin()  
  20.     {  
  21.         if (!\Yii::$app->user->isGuest) {     //①  
  22.             return $this->goHome();  
  23.         }  
  24.   
  25.         $model = new LoginForm();             //②  
  26.         if ($model->load(Yii::$app->request->post()) && $model->login()) {      //③  
  27.             return $this->goBack();          //④  
  28.         }  
  29.         return $this->render(‘login’, [      //⑤  
  30.             ‘model’ => $model,  
  31.         ]);  
  32.     }  
  33.   
  34.     public function actionLogout()  
  35.     {  
  36.         Yii::$app->user->logout();  
  37.   
  38.         return $this->goHome();  
  39.     }  
  40. }  

关注其中的actionLogin()方法:

①首先从\Yii::$app->user->isGuest中判断,当前是否是游客模式,即未登陆状态,如果用户已经登陆,会在user类中储存当前登陆用户的信息。

②如果当前是游客,会先实例化一个LoginForm模型

③这行
代码是整个login方法的核心所在,首先:$model->load(Yii::$app->request->post())把
post过来的数据填充进$model,即LoginForm模型,如果返回true,则填充成功。接着:$model->login():执行
LoginForm类里面的login()方法,可以从login()方法里面看到,将会执行一系列的验证。

关于Yii框架到底是怎样进行用户登陆的,底层是怎样实现的,我们在下一篇文章详谈,这里先说明实现方法。

四、视图(View)

在实现了model和controller,接下来是视图部分,由于用户需要输入数据,所以我们要提供一个表单,在Yii2中,提供了ActiveForm快速生成表单,代码如下:

 

[php] view
plain
copy
print?

  1. <?php  
  2.   
  3. /* @var $this yii\web\View */  
  4. /* @var $form yii\bootstrap\ActiveForm */  
  5. /* @var $model app\models\LoginForm */  
  6.   
  7. use yii\helpers\Html;  
  8. use yii\bootstrap\ActiveForm;  
  9.   
  10. $this->title = ‘Login’;  
  11. $this->params[‘breadcrumbs’][] = $this->title;  
  12. ?>  
  13. <div class=”site-login”>  
  14.     <h1><?= Html::encode($this->title) ?></h1>  
  15.   
  16.     <p>Please fill out the following fields to login:</p>  
  17.   
  18.     <?php $form = ActiveForm::begin([  
  19.         ‘id’ => ‘login-form’,  
  20.         ‘options’ => [‘class’ => ‘form-horizontal’],  
  21.         ‘fieldConfig’ => [  
  22.             ‘template’ => “{label}\n<div class=\”col-lg-3\”>{input}</div>\n<div class=\”col-lg-8\”>{error}</div>”,  
  23.             ‘labelOptions’ => [‘class’ => ‘col-lg-1 control-label’],  
  24.         ],  
  25.     ]); ?>  
  26.   
  27.         <?= $form->field($model, ‘username’)->textInput([‘autofocus’ => true]) ?>  
  28.   
  29.         <?= $form->field($model, ‘password’)->passwordInput() ?>  
  30.   
  31.         <?= $form->field($model, ‘rememberMe’)->checkbox([  
  32.             ‘template’ => “<div class=\”col-lg-offset-1 col-lg-3\”>{input} {label}</div>\n<div class=\”col-lg-8\”>{error}</div>”,  
  33.         ]) ?>  
  34.   
  35.         <div class=”form-group”>  
  36.             <div class=”col-lg-offset-1 col-lg-11″>  
  37.                 <?= Html::submitButton(‘Login’, [‘class’ => ‘btn btn-primary’, ‘name’ => ‘login-button’]) ?>  
  38.             </div>  
  39.         </div>  
  40.   
  41.     <?php ActiveForm::end(); ?>  
  42.   
  43.     <div class=”col-lg-offset-1″ style=”color:#999;”>  
  44.         You may login with <strong>admin/admin</strong> or <strong>demo/demo</strong>.<br>  
  45.         To modify the username/password, please check out the code <code>app\models\User::$users</code>.  
  46.     </div>  
  47. </div>  

$form=ActiveForm::begin()
:创建一个Form表单

$form=field()->textInput()  
:创建一个文本输入框

$form=field()->checkbox()
:创建一个checkbox

Html::submitButton():        
 创建一个登陆按钮

ActiveForm::end()    :   结束表单

以上,就是创建一个用户登陆模块的全流程,这里对用户登陆的细节和怎样实现cookie自动登陆只是一笔带过,更详细的源码分析请看下一篇博文,谢谢。

1、表单的生成
在Yii中表单即ActiveForm也是Widget,在上面可以看到是由begin开始

    <?php $form = ActiveForm::begin([‘id’ => ‘login-form’]);
?>

复制代码
中间为各个项的输入框,最后由end结尾

    <?php ActiveForm::end(); ?>

复制代码

2、表单中的方法
在Widget中begin()方法会调用int方法

    public function init()

复制代码
在最后的end()方法会调用run方法

    public function run()

复制代码

1、ActiveForm::begin()方法

    //这个是在执行 $form = ActiveForm::begin([‘id’ =>
‘login-form’]); 中的begin方法的时候调用的
    public function init()
    {
             //设置表单元素form的id
            if (!isset($this->options[‘id’])) {
                $this->options[‘id’] = $this->getId();
            }
            //设置表单中间的要生成各个field的所使用的类
            if (!isset($this->fieldConfig[‘class’])) {
                $this->fieldConfig[‘class’] =
ActiveField::className();
            }
            //这个就是输出表单的开始标签
            //如:<form id=”login-form”
action=”/lulublog/frontend/web/index.php?r=site/login”
method=”post”>
            echo Html::beginForm($this->action, $this->method,
$this->options);
    }

复制代码

2、ActiveForm::end()方法

    //这个是在执行 ActiveForm::end(); 中的end方法的时候调用的
    public function run()
    {
            //下面这个就是往视图中注册表单的js验证脚本,
            if (!empty($this->attributes)) {
                $id = $this->options[‘id’];
                $options = Json::encode($this->getClientOptions());
                $attributes = Json::encode($this->attributes);
                $view = $this->getView();
                ActiveFormAsset::register($view);
                /*
                 *
$attributes:为要验证的所有的field数组。它的值是在activeform中创建field的时候,在field的begin方法中给它赋值的。
                 * 其中每个field又是一个数组,为这个field的各个参数
                 * 比如username的field中的参数有
                 * validate、id、name、
                 *
validateOnChange、validateOnType、validationDelay、
                 * container、input、error
                 *
                 * $options:为这个表单整体的属性,如:
                 * errorSummary、validateOnSubmit、
                 *
errorCssClass、successCssClass、validatingCssClass、
                 * ajaxParam、ajaxDataType
                 */
                
               
$view->registerJs(“jQuery(‘#$id’).yiiActiveForm($attributes,
$options);”);
            }
            //输出表单的结束标签
            echo Html::endForm();
    }

复制代码

3、getClientOptions()方法

    /*
    * 设置表单的全局的一些样式属性以及js回调事件等
    */
    protected function getClientOptions()
    {
            $options = [
                ‘errorSummary’ => ‘.’ .
$this->errorSummaryCssClass,
                ‘validateOnSubmit’ => $this->validateOnSubmit,
                ‘errorCssClass’ => $this->errorCssClass,
                ‘successCssClass’ => $this->successCssClass,
                ‘validatingCssClass’ =>
$this->validatingCssClass,
                ‘ajaxParam’ => $this->ajaxParam,
                ‘ajaxDataType’ => $this->ajaxDataType,
            ];
            if ($this->validationUrl !== null) {
                $options[‘validationUrl’] =
Url::to($this->validationUrl);
            }
            foreach ([‘beforeSubmit’, ‘beforeValidate’,
‘afterValidate’] as $name) {
                if (($value = $this->$name) !== null) {
                    $options[$name] = $value instanceof JsExpression ?
$value : new JsExpression($value);
                }
            }

            return $options;
    }

复制代码

下面这个是生成的表单验证Js代码

    jQuery(document).ready(function () {
            jQuery(‘#login-form’).yiiActiveForm(
            {
                    “username”:{
                            “validate”:function (attribute, value,
messages) {
                                    yii.validation.required(value,
messages, {“message”:”Username cannot be blank.”});
                            },
                            “id”:”loginform-username”,
                            “name”:”username”,
                            “validateOnChange”:true,
                            “validateOnType”:false,
                            “validationDelay”:200,
                            “container”:”.field-loginform-username”,
                            “input”:”#loginform-username”,
                            “error”:”.help-block”},
                    “password”:{
                            “validate”:function (attribute, value,
messages) {
                                    yii.validation.required(value,
messages, {“message”:”Password cannot be blank.”});
                            },
                            “id”:”loginform-password”,
                            “name”:”password”,
                            “validateOnChange”:true,
                            “validateOnType”:false,
                            “validationDelay”:200,
                            “container”:”.field-loginform-password”,
                            “input”:”#loginform-password”,
                            “error”:”.help-block”
                            },

                    “rememberMe”:{
                            “validate”:function (attribute, value,
messages) {
                                    yii.validation.boolean(value,
messages, {
                                           
“trueValue”:”1″,”falseValue”:”0″,”message”:”Remember Me must be either
\”1\” or \”0\”.”,”skipOnEmpty”:1});
                            },
                            “id”:”loginform-rememberme”,
                           
“name”:”rememberMe”,”validateOnChange”:true,
                            “validateOnType”:false,
                            “validationDelay”:200,
                            “container”:”.field-loginform-rememberme”,
                            “input”:”#loginform-rememberme”,
                            “error”:”.help-block”}
            },
            {
                    “errorSummary”:”.error-summary”,
                    “validateOnSubmit”:true,
                    “errorCssClass”:”has-error”,
                    “successCssClass”:”has-success”,
                    “validatingCssClass”:”validating”,
                    “ajaxParam”:”ajax”,
                    “ajaxDataType”:”json”
            });
    });

复制代码

4、其它方法:errorSummary、validate、validateMultiple

    public function errorSummary($models, $options = [])

复制代码
它主要就是把model中的所有的错误信息汇总到一个div中。

    public static function validate($model, $attributes = null)
    public static function validateMultiple($models, $attributes =
null)

复制代码
这两个是获取错误信息的方法,比较简单也不说了。

3、表单中的参数

1、表单form自身的属性

    $action:设置当前表单提交的url地址,如果为空则是当前的url
    $method:提交方法,post或者get,默认为post
   
$option:这个里面设置表单的其它的属性,如id等,如果没有设置id,将会自动生成一个以$autoIdPrefix为前缀的自动增加的id
            //这个方法在Widget基本中
            public function getId($autoGenerate = true)
            {
                if ($autoGenerate && $this->_id === null) {
                    $this->_id = self::$autoIdPrefix .
self::$counter++;
                }

                return $this->_id;
            }
    复制代码

2、表单中各个项(field)输入框相关的属性

Yii生成的每个field由4部分组成:

    最外层div为每个field的容器,
    label为每个field的文本说明,
    input为输入元素,
    还有一个div为错误提示信息。

    <div class=”form-group field-loginform-username required
has-error”>
            <label class=”control-label”
for=”loginform-username”>Username</label>
            <input type=”text” id=”loginform-username”
class=”form-control” name=”LoginForm[username]”>
            <div class=”help-block”>Username cannot be
blank.</div>
    </div>

复制代码

$fieldConfig:这个是所有的field的统一的配置信息设置的属性。也就是说在field类中的属性都可以在这里进行设置。

    public function field($model, $attribute, $options = [])
    {
            //使用fieldConfig和options属性来创建field
           
//$options会覆盖统一的fieldConfig属性值,以实现每个field的自定义
            return Yii::createObject(array_merge($this->fieldConfig,
$options, [
                ‘model’ => $model,
                ‘attribute’ => $attribute,
                ‘form’ => $this,
    ]));
    }

复制代码

关于验证的属性:

   
$enableClientValidation:是否在客户端验证,也即是否生成前端js验证脚本(如果在form中设置了ajax验证,也会生成这个js脚本)。
    $enableAjaxValidation:是否是ajax验证
    $validateOnChange:在输入框失去焦点并且值改变的时候验证
    $validateOnType:正在输入的时候就进行验证
    $validationDelay:验证延迟的时间,单位为毫秒

这5个属性都可以在创建每个field的时候单独设置,因为在field类中就有这5个属性。

关于每个field容器样式的属性:

    $requiredCssClass:必填项的样式,默认为‘required’
    $errorCssClass:验证错误的样式,默认值为‘has-error’
    $successCssClass:验证正确的样式,默认值为‘has-success’
    $validatingCssClass:验证过程中的样式,默认值为‘validating’

3、ajax验证

    $validationUrl:ajax验证的url地方
    $ajaxParam:url中的get参数,用来标明当前是ajax请求,默认值为‘ajax’
    $ajaxDataType:ajax请求返回的数据格式

4、前端js事件属性

   
$beforeSubmit:在提交表单之前事件,如果返回false,则不会提交表单,格式为:
        function ($form) {
          …return false to cancel submission…
        }
    复制代码

    $beforeValidate:在每个属性在验证之前触发,格式为:
        function ($form, attribute, messages) {
          …return false to cancel the validation…
        }
    复制代码

    $afterValidate:在每个属性在验证之后触发,格式为:
        function ($form, attribute, messages) {
        }
    复制代码

5、表单中的其它属性

    $validateOnSubmit:提交表单的时候进行验证
    $errorSummary:总的错误提示地方的样式
   
$attributes:这个属性比较特殊,它是在创建field的时候,在field中为这个form中的$attributes赋值的。这样可以确保通过field方法生成的输入表单都可以进

原文链接:

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图
Copyright @ 2010-2019 澳门新葡亰官网app 版权所有