用CI也有一段时间了,感觉paperen我也挺喜欢CI的,因为觉得上手真的很容易也很方便,暂时还不会考虑其他框架。不过通过这段时间的使用,加上同事比较多疑问(也算是好事吧),自己也有点对CI中控制器与模型部分使用产生些少疑问,特别是模型,因为之前的开发就带来一些比较混乱的状况,老实说之前写的代码比较糟糕。

20111122011355

很多疑问都是产生在控制器与模型之间的,疑问最严重的是模型的重用性(况且可以这么说吧),平平君与奇奇君本来是分开一人负责一块功能的,但是免不了要涉及一些公用的模型,这就导致了一些不能避免的问题,平平君要与奇奇君协商某个公用的模型方法,但是别扭的是前者可能额外附件一些条件,比如要查询state为3的记录而后者则又会附加其他限制条件,而且paperen不太同意在控制器中对模型进行控制(或者下面的例子展示后你就明白什么意思),结果要写一个能符合两者需求的模型方法,在这个模型方法中写了比较复杂的逻辑,本来比较简单的模型突然看到一段复杂的逻辑,这个真的是我不想要的,paperen提议的是即使你查询的条件不同的话就可以考虑分出来单独作为一个方法了,而不要花太多时间在万能的模型方法,因为那可能会增加了模型的复杂度或者增加了控制器与模型的耦合度。说了这么多还不如看个例子吧。

/**
* 用户模型
*/
class User_model extends CI_Model
{
/**
* table映射
* @var array
*/
private $_table = array(
'user' => 'user',
'group' => 'group',
);

/**
* 字段映射
* @var array
*/
private $_fields = array(
'user' => array(
'id' => 'id',
'username' => 'username',
'password' => 'password',
'groupid' => 'groupid',
),
'group' => array(
'id' => 'id',
'name' => 'name',
),
);

/**
* 根据用户ID查询用户信息
* @param int $userid 用户ID
* @return array 用户数据
*/
public function get( $userid )
{
return $this->db->from( $this->_table['user'] )
->where( $this->_fields['user']['id'], $userid )
->get()
->row_array();
}

/**
* 用户验证
* @param string $username 用户帐号
* @param string $password 用户密码
* @return array 用户数据
*/
public function verify( $username, $password )
{
return $this->db->from( $this->_table['user'] )
->where( $this->_fields['user']['username'], $username )
->where( $this->_fields['user']['password'], $password )
->get()
->row_array();
}

/**
* 在控制器中就决定查询哪些字段决定where
* @param arrray $where 限制条件
* @param string $select 查询字段
* @return array 查询结果
*/
public function get2( $where, $select = '*' )
{
return $this->db->select( $select )
->from( 'user' )
->where( $where )
->get()
->row_array();
}
}

控制器

class Home extends CI_Controller
{
public function test()
{
//引入用户模型
$this->load->model( 'user_model' );

//用户ID
$userid = 1;
//用户名
$username = 'test';
//用户密码
$password = 'test';

//get
//获取确定ID的用户数据

$user_data = $this->user_model->get( $userid );

//verify
//验证用户是否合法
$valid_user = $this->user_model->verify( $username, $password );

//get2
//在控制器中可以决定查询的限制条件
//不需要查询到password字段
$select = 'id,username';
//条件
$where = array( 'id' => $userid );
$user_data = $this->user_model->get2( $where, $select );

//get2
//比如登陆验证时还可以重用get2这个方法
$where = array( 'username' => $username, 'password' => $password );
$valid_user = $this->user_model->get2( $where );
}
}

前面模型那些什么映射大家可以不用管,其实没太大用途只是paperen个人感觉而已,主要看get、verify与get2这三个方法,get是根据用户ID获取用户数据、verify是根据用户帐号与密码去验证用户是否存在、get2则无从解析因为它灵活度很大,你可以决定where也可以决定查询的字段,听起来get2更好对不,但是事实上paperen不认同get2这样的写法,因为这样做导致的一个问题就是,模型已经不再是模型了,因为主动权不在自己手上,看看在控制器中我们是如何调用这个get2查询的

$select = 'id,username';
$where = array( 'id' => $userid );
$user_data = $this->user_model->get2( $where, $select );

你必需知道这代表了什么,我在控制器中已经控制限制了模型,那为什么还需要模型呢,如果我数据结构更改了,我们将要修改的不是模型而是修改控制器,MVC的味道似乎已经没了。所以papern还是推荐get、verify这样的写法,确实没有get2灵活但是这样才保证了MVC味道所在,在这个面前坚持将sql的控制放回到模型是必需的。

而后来paperen又再封装了控制器调用模型时的处理,因为原来在控制器中调用某个模型的方法时,一般要手动加载相关的模型(除非你开始时就已经将所有模型引入)

$this->load->model( 'user_model' );

然后要调用某个方法时

$this->user_model->get( $userid );

paperen想这里再封装一下就更好了,于是我扩展了CI_Controller控制器类,在它基础上加入了一个魔法方法__call

/**
* 魔法方法 减少模型与控制器之间的耦合度
* @param string $name 调用的模型的方法名 规则为 model_+模型名称+模型方法
* @param mixed $arguments 参数
* @return mixed
*/
private function __call( $name, $arguments )
{
$tmp = explode('_', $name);
if ( count($tmp) == 3 && strtolower( $tmp['0'] ) == 'model' )
{
$modelName = "{$tmp[1]}_model";
$modelMethod = $tmp[2];
if ( !isset( $this->$modelName ) ) $this->load->model( $modelName );
return call_user_func_array(array($this->$modelName, $modelMethod), $arguments);
}
else
{
return '';
}
}

大家可以试试这样做是不是比较好,那么上面的两句代码完全可以变成一句了

$user_data = $this->model_user_get( $userid );

而且我们只要预先将模型中的方法都定好那么写控制器的人对着相应的模型方法解析文档就可以展开自己的编码,完全不会影响也不会干涉到模型这边,都告诉你这个方法返回什么数据了,你就用这些数据去完成你的逻辑与流程好了。这样也就貌似很好了。

好吧,就到此为止,困了~~

不过最后paperen想再次提醒一下写代码的人三句(包括myself)“要多去想想如何能在代码中更偷懒更好看更能重用,不要做一个被后继接手程序员骂的程序员,你写代码是为了实现什么功能”。