Chinaunix首页 | 论坛 | 博客
  • 博客访问: 697656
  • 博文数量: 160
  • 博客积分: 8847
  • 博客等级: 中将
  • 技术积分: 1656
  • 用 户 组: 普通用户
  • 注册时间: 2010-11-25 16:46
个人简介

。。。。。。。。。。。。。。。。。。。。。。

文章分类

全部博文(160)

文章存档

2015年(1)

2013年(1)

2012年(4)

2011年(26)

2010年(14)

2009年(36)

2008年(38)

2007年(39)

2006年(1)

分类: 系统运维

2011-03-14 22:34:18

一、YII建一个真正的项目 -- stackstar

(一)目标:实战 -- YII开发一套项目开发生命周期的跟踪程序

(二)项目需求分析:

1) 项目开发准备用敏捷开发的方法,使用测试驱动开发完成。

2) 项目组成员可以CRUD项目和对应项目中出现的问题、bug和各项开发任务,非项目组成员只能登录或者注册。

3) 一个项目组的成员可以在项目下面建立各种事件,并指定某个项目组成员去解决对应;

4) 各种事件有:

3种状态:not yet started/started/finished

3种类型:bugs/features/tasks

5) 由于我们的数据需要存储起来,有CRUD操作,所以我们选择MySQL数据库,并设置数据库的编码为UTF8.

6) 用户权限管理

7) 事件和用户相关联,也就是说项目组成员只能看到和他相关的事件

(三)开发分析

这个应用有三个主要的角色(域):userprojectissue

(四)角色分析

1) User -- 也就是项目组成员,分为已登录和未登录(访客)

2) 已登录用户可以做CRUD,为登录用户无法执行任何操作

3) 项目所有者(创建者)可以对自己的项目进行CRUD操作,并且可以添加新的成员进来,除创建者以外的项目组成员,可以在下面添加事件,并指派成员去对应各种问题

4) Project -- 新建的或者已经建好的项目,是本次开发的重点和基础。Project下面又分为各种小的模块,用来描述Project中各种事件

5) Issue -- Project下面的事件,包括:

Features -- 跟项目开发相关的事件,比如:开发一个用户登录模块

Tasks -- 跟项目开发无关,但是又在本开发项目范围之内的事件,比如:设置并建立开发环境

Bugs -- 项目开发中出现的错误和BUG,比如:无法验证用户输入是一个email地址

Issue分为3种状态:还没有开始,已经开始,已经完成

(五)工作流程


上图都能看明白吧,以下省略200字。

(六)定义数据结构和模型


上面的图定义了3个数据对象之间的关系,简单的说:

一个项目组成员有一个或者多个(HAS-MANY)项目,一个项目有一个或者多个成员(HAS-MANY),所以,projectuser之间是MANY-TO-MANY的关系;

一个成员有0个或者多个事件(建立此事件的是requester),一个事件只属于一个成员,这个事件指派给谁,谁就是这个事件的owner

一个项目下面有0个或者多个事件,而这个事件只属于这个特定的项目。

(七)建立应用

使用YIIC工具建立webapp,修改主配置main.php,连接到MySql数据库(数据库需要5.1+的版本)。

(八)测试数据连接

protected/tests/unit下面新建一个类:
class DbConnectionTest extends CDbTestCase{

function testConn(){

$this->assertTure(true); //passed -- green

$this->assertNotEquals(NULL, Yii::app()->db); //failed -- red

}

}

(九)设置和连接数据库

         /* 注释掉原来末日的sqlite连接,新建一个db

'db'=>array(

'connectionString' => 'sqlite:'.dirname(__FILE__).'/../data/testdrive.db',

),

// uncomment the following to use a MySQL database

*/

'db'=>array(

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

'emulatePrepare' => true,

'username' => 'root',

'password' => ' 数据库密码 ',

'charset' => 'utf8',

),

(十)YII 支持的数据库

YIIDAO(数据库访问对象)建立在PHPPDO之上,使得数据库和应用程序相互独立,可以支持的数据库包括主流的:

Oracle -- oci:dbname=

MySQL -- mysql:dbname=trackstar;host=localhost; port=3306

PGSQL -- pgsql:dbname=trackstar;port=5432;host=localhost

Sqlite -- sqlite:/path/to/trackstar.db

(十一)

二、建模

模型对象之间是多对多的关系(many-to-many),应该建立至少3个表。

比如:userproject之间,一个用户属于一个或者多个项目,一个项目有一个或者多个项目成员,所以就应该建立tbl_user,tble_project,以及tbl_project_user3个表来表示,另外,应该建立外键约束来保证删除和更新的时候达到同步。项目又会分好多小项目,比如:tbl_issue

建立外键,我们可以使用DDL语句,也可以使用sqlyog或者phpMyAdmin这种工具来实现。

注意事项:

表必须是InnoDB作为引擎,从而支持事务处理,不能用MyISAM

用这种工具的时候,每次建立一个对应的外键,而不是多个;

三、mysql使用

MySQLdatetime设置当前时间为默认值 

由于MySQL目前字段的默认值不支持函数,所以用:

create_time datetime default now()

的形式设置默认值是不可能的。

代替的方案是使用TIMESTAMP类型代替DATETIME类型。

CURRENT_TIMESTAMP :当我更新这条记录的时候,这条记录的这个字段不会改变。

CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP :当我更新这条记录的时候,这条记录的这个字段将会改变。即时间变为了更新时候的时间。(注意一个UPDATE设置一个列为它已经有的值,这将不引起TIMESTAMP列被更新,因为如果你设置一个列为它当前的值,MySQL为了效率而忽略更改。)如果有多个TIMESTAMP列,只有第一个自动更新。

四、小物件Widget

在试图View中,使用:

echo $form->errorSummary($model);

$form=$this->beginWidget('CActiveForm', array('id' => 'issue-form',  'enableAjaxVlidation' => false,));

...

$this->endWidget();

?>

来创建form标签,这样可以使用model中的特性(attributes)来填充表单中的值。

$form->labelEx($model, 'type_id') -- 表示label标签,$model表示当前的标签的显示内容和$model attributeLabels()中的对应;

$form->fieldText($model, 'type_id') -- 表示文本框,用户输入的值和model中的属性type_id对应,并且通过$model->name[type_id]的方式post值。用户输入的值会被rules拦截并进行验证,任何对type_id有效的验证,都会执行。

$form->error($model, 'type_id') -- 表示验证的错误提示

$form->errorSumarry($model) 表示验证错误后的全部错误的集合。

五、filter中设置获取上下文的PID并实现PID的获取方法

用户请求执行一个控制器中的方法之前或者之后执行的过滤器。

一个项目下面有固定的几个项目组成员,并不是所有成员。所以在项目下面建立事件的时候,如果要选择事件的发起人(requester)和对应人(owner),必然要知道这个项目的id,一旦项目确定了,那么发起人和对应人就可以在下拉列表中提供选择了。

但是YIIGII在生成代码的时候,并没有限制。

比如我们要在PID = 1project下面建立一个事件,这个项目组的成员有AB两个人,那么在下拉列表中就应该只有这两个人作为选择,而不是所有的成员。

那么,既然要知道项目的PID才可以在下面新建事件,我们应该在IssueController->actionCreate之前,就应该知道这个PID才可以,这个PID要么是post的,要么是GET的,反正必须知道才行。所以在IssueControllerfilter中添加如下的过滤:

1:首先要获取PID,如果PID不知道,无法完成这个操作

2:将跟这个PID相关的成员找出来。

首先在IssueController中的filter方法下面添加:

...

'projectContext + create' //表示在create一个新的事件之前,需要执行filterProjectContext过滤

  

接下来,在IssueController中添加两个个方法和一个属性

...

private $_project = null;

protected function loadProject($projectId){

if ($this->_project ===null){

$this->_project = Issue::model()->findByPk($projectId);

if ($this->_project === null){

trhrow new CHttpException(404, 'project is not exist.');

}

}

return $this->_project;

}

public function filterProjectContext($projectChian){

$projectId = null;

if (isset($_GET['pid'] )){

$projectId = $_GET['pid'];

}else if (isset($_POST['pid']) ){

$projectId = $_POST['pid'];

}

$this->loadProject($projectId);

}

这样,我们在新建一个事件的时候,就必须保证有一个PID存在了。

六、用户验证

(一)GII自动生成的验证机制比较简单,并非基于数据库,而是在程序里面写死的,只有(demo/demoadmin/admin)。要想实现比较好的验证机制,YII提供了一个验证接口,并在配置文件中可以配置,被称为:user,它是一个实现了IWebUser这个接口的对象。

(二)这个组件封装了当前用户的所有身份信息,例如:IDnamereturnUrlloginUrlallowAutoLogin

(三)YII的验证类是独立的,一般我们基于数据库的用户名和密码验证,只需要实现UserIdentify类的authenticate方法(验证逻辑),就可以了。

(四)身份验证的过程:


1) Controller里,设置了accessRules@或者用户名的方法,例如:

public function accessRules(){

return array(

array(

'allow', //允许执行的操作

'actions' => array('delete', 'update'),

'users' => array( 'admin'), //只允许登录用户admin执行

),

);

}

当触发访问条件时,如果当前用户没有登录(isGuest),就好转到SiteController/actionLogin,并把returnUrl 设置为当前要返回的页面地址。

2) 用户输入用户名和密码,提交后触发validation,也就是rules定义的数据检查,例如:

public function rules()

{

return array(

array('username, password', 'required'),

array('rememberMe', 'boolean'),

array('password', 'authenticate'),

);

}

3) 执行authenticate方法去验证密码。密码验证会新建一个UserIdentity实例,并以用户提交的数据填充,并调用UserIdentityauthenticate方法,如果密码验证通过,那么设置用户为登录用户。并返回之前要访问的URL

实际上,YII密码验证的过程就在UserIdentityauthenticate方法中实现,所以,只要把验证过程放在这里就可以了。

private $_id;

public function authentication(){

$criteria = new CDbCriteria;

$criteria->condition = 'usernmae = :name';

$criteria->parmas = array(':name' => $this->username);

$user = User::model()->find($criteria);

if (NULL == $user){

$this->errorCode =  self::ERROR_USERNAME_INVALID;

}else if ($user->password !=  $user->encrypt($this->password)){

$this->errorCode = self::ERROR_PASSWORD_INVALID;

}else{

$this->_id = $user_id;

$this->errorCode = self::ERROR_NONE;

}

return !$this->errorCode;

}

public function getId(){

return $this->_id;

}

(待续。。。)
阅读(1145) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~