yii创建的blog项目的一点点分析

来源:互联网 发布:农银网络大学 编辑:程序博客网 时间:2024/05/13 12:30

1年前的笔记了,现在回想起学yii的时候,满满都是泪啊~~~(本来没有写博客的习惯的,但是作为一枚程序员,是应该要有一些博客记录的~~~废话不说了)


1、 我们用yii创建的blog项目,对于其中的contact联系页页面,下面我们描述一下它的工作流程:

(1) 用户请求此URL(即contact页面):http://localhost/blog/index.php?r=site/contact;

(2) 入口脚本index.php被网站服务器执行以处理此请求;

(3) 一个应用的实例被创建,其配置参数为/wwwroot/blog/protected/config/main.php应用配置文件中指定的初始值;我们所有的配置都在main.php里面;

(4) 应用分派此请求到一个控制器(Controller)和一个控制器动作(Controller action)。对于联系页(Contact)的请求,它分派到了site控制器和contact动作(即/wwwroot/blog/protected/ controllers/SiteController.php中的actionContact方法);

(5) 应用按照SiteController实例创建了site控制器并执行;

(6) SiteController实例通过调用它的actionContact()方法执行contact动作;

(7) actionContact方法为用户渲染一个名为contact的视图(View)。在程序内部,这是通过包含一个视图文件/wwwroot/blog/protected/views/site/contact.php并将结果插入布局文件/wwwroot/blog/protected/views/layouts/column1.php实现的;

 

2、 创建数据库:

例如打开phpMyAdmin,,新建数据库名为blog,使用字符集为utf8_unicode_ci,然后点击创建按钮,然后选择导入点击选择文件选择wwwroot/yii/demos/blog/protected/data/下的schema.mysql,点击执行,于是就创建好了。

 

3、 值得注意的是在数据表中:

(1) 我们对所有表和列的命名使用了小写字母。这是因为不同的 DBMS 通常有不同的大小写敏感处理方式,我们通过这种方式来避免这种问题;

(2) 我们同时对所有的表使用了tbl_前缀。这出于两个目的。第一,此前缀对这些表提供了一个命名空间以使他们和同一数据库中的其他表共存,此情况常出现在在共享的主机环境中,一个数据库常被多个应用使用。第二,使用表前缀减少了表名中出现DBMS保留字的可能;

 

4、 创建了数据库之后,我们就得在配置文件中进行配置以建立数据库连接,该配置文件就是/wwwroot/blog/protected/config/main.php,主要是如下内容:

return array(

    ......

    'components'=>array(

        ......

        'db'=>array(

            'connectionString' => 'mysql:host=localhost;dbname=blog',

            'emulatePrepare' => true,

            'username' => 'root',

            'password' => '',

            'charset' => 'utf8',

            'tablePrefix' => 'tbl_',

        ),

    ),

  ......

);

对上面配置中的tablePrefix属性解释一下。此属性告诉db连接它应该关注我们使用了tbl_作为数据库表前缀。具体来说,如果一条SQL语句中含有一个被双大括号括起来的标记(例如{{post}}),那么db连接应该在把它提交给DBMS执行前,先将其翻译成带有表前缀的名字(例如tbl_post) 。这个特性非常有用,如果将来我们需要修改表前缀,就不需要再动代码了。例如,如果我们正在开发一个通用内容管理系统(CMS),我们就可以利用此特性,这样当它被安装在一个不同的环境中时,我们就能允许用户选择一个他们喜欢的表前缀。

 

5、 这样,通过上面配置,我们就可以在代码的任意位置使用Yii::app()->db来访问数据库连接对象了。注意的是,Yii::app()会返回我们在入口脚本中创建的应用实例。然而,在多数情况下,我们并不会直接使用这个数据库连接。而是使用被称为ActiveRecord的东西来访问数据库。

 

6、 创建、读取、更新、删除(CRUD)是应用的数据对象中的四个基本操作。由于在Web应用的开发中实现CURD的任务非常常见,Yii为我们提供了一些可以使这些过程自动化的代码生成工具,名为Gii(也被称为脚手架)。下面介绍一下安装Gii的步骤:

打开文件/wwwroot/blog/protected/config/main.php,添加如下代码:

return array(

    ......

    'modules'=>array(

        'gii'=>array(

            'class'=>'system.gii.GiiModule',

            'password'=>'这儿设置一个密码',

            'ipFilters'=>array('127.0.0.1','::1'),

        ),

    ),

);

这样就安装了一个名为gii的模块了,于是我们就可以通过在浏览器中浏览如下URL来访问Gii模块:http://localhost/blog/index.php?r=gii

我们将被提示要求输入一个密码。输入我们在 上面所设置的密码即可,我们将看到一个页面,它列出了所有可用的代码生成工具。

需要注意的是,为了web应用的安全,配置文件中的gii模块代码在生产环境中应当移除,代码生成工具只应当用于开发环境,在生产环境中,我们则应当把上面我们所添加的几行代码给注释掉;

 

7、 接下来首先我们需要为每个数据表创建一个模型(Model)类,模型类可以使我们可以通过一种直观的、面向对象的风格访问数据库。进入http://localhost/blog/index.php?r=gii页面后,点击Model Generator链接使用模型创建工具。在Model Generator页中,在Table Name一栏输入tbl_user,然后按下Preview按钮。一个预览表将显示在我们面前。我们可以点击表格中的链接来预览要生成的代码。如果一切OK,我们可以按下Generate 按钮来生成代码并将其保存在一个文件中,一般默认会保存在blog/protected/models/下面;对剩余的其他表重复同样的步骤,包括tbl_post, tbl_comment, tbl_tagtbl_lookup。我们还可以在Table Name栏中输入一个星号 '*' 。这样就可以通过一次点击就对所有的数据表生成相应的模型类了。

于是,我们就有了如下新创建的文件:

(1)models/User.php包含了继承自CActiveRecordUser类,可用于访问tbl_user数据表;

(2)models/Post.php包含了继承自CActiveRecord Post类,可用于访问tbl_post数据表;

(3)models/Tag.php 包含了继承自CActiveRecord Tag类,可用于访问tbl_tag数据表;

(4)models/Comment.php包含了继承自CActiveRecordComment类,可用于访问 tbl_comment 数据表;

(5)models/Lookup.php包含了继承自CActiveRecordLookup类,可用于访问tbl_lookup数据表;

 

8、 为数据表创建好模型类之后,我们就可以进入http://localhost/blog/index.php?r=gii页面里面使用Crud Generator来创建为这些模型实现CRUD操作的代码了,例如我们对创建好的Post模型模型执行此操作。则有:

Crud Generator页面中,Model Class一栏输入Post (就是我们刚创建的Post模型的名字),然后按下Preview按钮。我们会看到有很多文件将被创建。按下Generate按钮来创建它们。

这些文件可以分为控制器文件和视图文件,分别在/blog/protected/controllers/blog/protected/views下:

其中的控制器文件是

controllers/PostController.php包含负责所有CRUD操作的PostController 控制器类

其中的视图文件是

views/post/create.php一个视图文件,用于显示创建新日志的HTML表单;

views/post/update.php一个视图文件,用于显示更新日志的HTML表单;

views/post/view.php一个视图文件,用于显示一篇日志的详情;

views/post/index.php一个视图文件,用于显示日志列表;

views/post/admin.php一个视图文件,用于在一个带有管理员命令的表格中显示日志;

views/post/_form.php一个插入views/post/create.phpviews/post/update.php的局部视图文件,它显示用于收集日志信息的HTML表单;

views/post/_view.php一个在views/post/index.php中使用的局部视图文件。它显示单篇日志的摘要信息;

views/post/_search.php一个在views/post/admin.php中使用的局部视图文件。它显示一个搜索表单;

 

9、 为了理解8提到的文件是如何使用的,我们通过访问http://localhost/blog/index.php?r=post来列出当一个日志列表时所发生的工作流程:

(1)用户请求访问URLhttp://localhost/blog/index.php?r=post

(2)入口脚本index.phpWeb服务器执行,它创建并实例化了一个应用实例来处理此请求;

(3)应用创建并执行了PostController实例;

(4)PostController实例通过调用它的actionIndex()方法执行了index动作,注意,如果用户没有在URL中指定执行一个动作,则index就是默认的动作;

(5)actionIndex()方法查询数据库,带回最新的日志列表;

(6)actionIndex()方法使用日志数据渲染index视图;

 

10、我们所创建的blog应用已经提供了用户验证功能了,它会判断用户和密码是不是都为demoadmin,下面我们通过修改些代码来使身份验证通过User数据表实现:

首先用户验证是在一个实现了IUserIdentity接口的类中进行的,此程序骨架通过UserIdentity类实现此目的,此类存储在/wwwroot/blog/protected/components/UserIdentity.php文件中;我们将UserIdentity类做如下修改:

<?php

class UserIdentity extends CUserIdentity

{

    private $_id;

    public function authenticate()

    {

        $username=strtolower($this->username);

        $user=User::model()->find('LOWER(username)=?',array($username));

        if($user===null)

            $this->errorCode=self::ERROR_USERNAME_INVALID;

        else if(!$user->validatePassword($this->password))

            $this->errorCode=self::ERROR_PASSWORD_INVALID;

        else

        {

            $this->_id=$user->id;

            $this->username=$user->username;

            $this->errorCode=self::ERROR_NONE;

        }

        return $this->errorCode==self::ERROR_NONE;

    }

 

    public function getId()

    {

        return $this->_id;

    }

}

上面进行的修改中,在authenticate()方法里,我们使用了User类来查询tbl_user表中username列值(不区分大小写)和提供的用户名一致的行为,注意的是User类是我们通过gii工具创建的,由于User类继承自CActiveRecord,我们可以利用ActiveRecord功能以OOP的风格访问tbl_user表。接着我们为了检查用户是否输入了一个有效的密码,我们调用了User类的validatePassword方法,而这个方法需要我们在/blog/protected/models/User.php文件里创建如:

class User extends CActiveRecord

{

    ......

    public function validatePassword($password)

    {

        return $this->hashPassword($password,$this->salt)===$this->password;

    }

    public function hashPassword($password,$salt)

    {

        return md5($salt.$password);

    }

}

这里需要注意两点:

(1)如果使用上面段代码,那么当所安装的yii版本不是1.1.6.r2877的话,那么blog的数据库就要从yii1.1.6.r2877这个版本里面的blog数据库样本安装,否则会提示$salt没有定义的错误;

(2)另外,上面代码中,我们在数据库中存储了密码的加密串和随机生成的SALT密钥,而不是存储明文密码。 所以当要验证用户输入的密码时,我们应该和加密结果做对比;

 

11、注意,类文件的名字必须是相应的类名加上.php后缀。遵循此约定,就可以通过一个路径别名(path alias)指向此类。例如,我们可以通过别名application.components.UserIdentity指向UserIndentity类。Yii的许多API都可以识别路径别名(例如Yii::createComponent()),使用路径别名可以避免在代码中插入文件的绝对路径,绝对路径的存在往往会导致在部署应用时遇到麻烦。

 

12、在10我们可以看到,在UserIdentity类中,我们重写了getId()方法,它会返回在User表中找到的用户的id,父类CUserIdentity则会返回用户名。usernameid属性都将存储在用户SESSION中,可在代码的任何部分通过Yii::app()->user访问。

UserIdentity类中,我们没有显式包含即include相应的类文件就访问了CUserIdentity类,这是因为CUserIdentity是一个由Yii框架提供的核心类。Yii将会在任何核心类被首次使用时自动包含类文件。

我们也对User类做了同样的事情,这是因为User类文件放在/blog/protected/models目录,而此目录已经通过应用配置protected/config/main.php中的下面几行代码被添加到了PHPinclude_path里面了:

return array(

    ......

    'import'=>array(

        'application.models.*',

        'application.components.*',

    ),

    ......

);

上面的配置说明,位于/blog/protetced/models/blog/protected/components目录中的任何类将在第一次使用时被自动包含。

 

13、UserIdentity类主要用于LoginForm(该类在/blog/protected/models/)中,它基于用户名和从登录页中收到的密码来实现用户验证。下面的代码展示了UserIdentity的使用:

$identity=new UserIdentity($username,$password);

$identity->authenticate();

switch($identity->errorCode)

{

  case UserIdentity::ERROR_NONE:

      Yii::app()->user->login($identity);

      break;

  ......

}

14、关于identityuser应用组件,前者代表的是一种验证方法,后者代表当前用户相关的信息。一个应用只能由一个user组件,但它可以有一个或多个identity类,这取决于它支持什么样的验证方法,一旦验证通过,identity实例会把它自己的状态信息传递给user组件,这样它们就可以通过user实现全局可访问。

 

15、总结一下,对上面这个博客项目来说,我们首先是设计并创建博客的数据库;然后修改应用配置,添加数据库连接;接着为博客的日志和评论生成实现CRUD操作的代码;然后我们接着修改了验证方法以实现通过tbl_user表验证身份;

总之,我们首先要修改模型类,添加适当的验证规则并声明相关的对象,然后我们要为每个CRUD操作修改其控制器动作和视图代码。

 

16、下面我们自定义日志模型,即由yiic工具生成的Post日志模型类给做如下两处修改:

(1)修改rules()方法:指定对模型属性的验证规则;

(2)修改relations()方法:指定关联的对象;

值得注意的是,模型包含了一系列属性,每个属性关联到数据表中相应的列,属性可以在类成员变量中显示定义,也可以隐式定义,不需要事先声明。

 

17、自定义Post日志模型里的rules()方法:

我们先来指定验证规则,这样它可以确保用户输入的信息在保存到数据库之前是正确的。例如,Poststatus属性应该是123中的任一数字。yiic工具其实也为每个模型生成了验证规则,但是这些规则是基于数据表的列信息的,可能并不是非常恰当;

下面是我们对rules()方法做的修改:

public function rules()

{

    return array(

        array('title, content, status', 'required'),

        array('title', 'length', 'max'=>128),

        array('status', 'in', 'range'=>array(1,2,3)),

        array('tags', 'match', 'pattern'=>'/^[\w\s,]+$/','message'=>'Tags can only contain word characters.'),

        array('tags', 'normalizeTags'),

        array('title, status', 'safe', 'on'=>'search'),

    );

}

上面代码中,我们指定了title,contentstatus属性是必填项;title的长度不能超过128status属性应该是1(草稿)2(已发布)3(已存档)tags属性应只允许使用单词字母和逗号。另外,我们使用normalizeTags来规范化用户输入的Tag,使Tag是唯一的且整齐地通过逗号分隔;而最后的规则会被搜索功能用到;

required,length,inmatch这几个验证器是Yii提供的内置验证器,normalizeTags验证器是一个基于方法的验证器,我们需要在Post类中定义它,如下:

public function normalizeTags($attribute,$params)

{

    $this->tags=Tag::array2string(array_unique(Tag::string2array($this->tags)));

}

而上面normalizeTags()方法中的array2stringstring2arrayTag模型里面的方法来的,这两个需要我们在/models/Tag.php里面定义,而array_unique()这个函数是移除数组中重复的值的并返回结果数组。在/models/Tag.php里面定义的2个方法如下:

    public static function string2array($tags)

{

return preg_split('/\s*,\s*/',trim($tags),-1,PREG_SPLIT_NO_EMPTY);

}

public static function array2string($tags)

{

return implode(', ',$tags);

}

我们在rules()方法中定义的规则会在模型实例调用其validate()save()方法时逐一执行的。

 

18、我们需要务必记住rules()中出现的属性必须是那些通过用户输入的属性,其他的属性,如Post模型中的idcreate_time,是通过我们的代码或数据库设定的,不应该出现在rules()中。

0 0