#1 jake

SpeedPHP框架采用的MVC架构,让开发者可以很方便地对MVC原有功能进行扩展和加强,无需改变原有的使用方法、而且对顶层代码而言是透明的,使用这些方法的开发者可以不需要在意内部实现而专注于实际的开发。本文介绍通过继承的方式,重写spModel操作的方法和例子。spModel原有的众多成员函数,都是对数据库操作的简便方法,而开发者可以覆盖重写这些方法,以达到更复杂的功能,例如:


  • 读写数据库分离(PHP代码级别),让spModel中的增删改操作可对主数据库(写库),而查找操作将对从数据库(读库)。
  • 在原spModel成员函数基础上,加入更便捷的功能,如为create/update的字段加上当前时间。
  • 关联操作,如在更新或查找的时候,同时对多表或多库进行操作(需要这些操作间的逻辑是非常紧密的)。
  • 返回更恰当的数据,如find/findAll之后,将结果数据中序列化(serialize)后的数据,进行反序列化(unserialize),使得控制器可以直接使用这些数据而不需要再反序列化。
  • 任何开发者可以想到的增强spModel原有函数的操作。
通过继承方式来重写spModel,我们可以考虑是否需要建立一个中层PHP类,该中层类将代替spModel的作用——因为它是继承与spModel,所以它可以拥有spModel的一切功能,而系统中其他的模型类(子类),都继承于这个中间层以获得spModel的功能。
model-to-model-overwrite(1)[1].jpg


建立中层类m_model

考虑是否建立中层类的要素,是看需要重写的操作是否具有普遍性?如读写分离是比较普遍性的操作,因其是作用在整个spModel的函数上——全部spModel的函数都应该进行读写分离。

建立中层类对项目未来model类库的扩展性很有帮助,可以加入更复杂的功能而无需改变现有的代码,但在系统设计初期复杂度会有所增加。

部分模型类如不需要中层类的功能,可以不继承于中层类而直接继承于spModel。

开发者无需为中层类的资源开销考虑,因为SpeedPHP框架的spModel与其他PHP框架模型对象相比,去除数据库抽象层而直接对数据库驱动操作,避免数据库抽象层带来的资源开销。而开发者建立的中层类,比起数据库抽象层而言,耗费的资源开销更小。

SpeedPHP框架之所以去除数据库抽象类,是因为在实际开发中,较复杂的数据库操作是直接使用SQL语句进行操作会更有效率,而没必要保留数据库抽象层的作用——数据库抽象层的作用类似数据库SQL语言编译器,将上层Model的方法,如create等转换成SQL语句——这一步可以直接Model和数据库驱动类中共同实现。

下面,我们以重写create加入新建时间和findAll结果反序列化来说明一下重写spModel功能的方法。


重写create以加入新建时间
class m_guestbook extends spModel
{
        public $pk = "gid";
        public $table = "guestbook";
  
        public function create($row) // 参数与create的相同
        {
                $row['ctime'] = date("Y-m-d H:i:s"); // ctime是新建时间的数据库字段
                parent::create($row); // 调用父类spModel的create方法来插入新记录
        }

}
?>

这里的m_guestbook是直接继承于spModel,它重写覆盖了create方法。开发者在调用m_guestbook的create方法的时候,就不需要考虑ctime字段的填充,m_guestbook的create将自动把当前时间填入ctime字段中。



findAll结果反序列化

通常在开发中,我们会遇到一些数据结构有某些数据是不作为条件进行查找,但也需要存在的。如通讯录的数据结构中,地址、电话等数据是作为条件查找的,而身高体重之类的数据,就是不作为条件查找的。那么,这些不作为查找条件的的数据,我们可以用PHP的序列化方法,把这些数据都统一存放到一个字段中,以简化数据表结构。

当然,哪些数据是不作为条件查找,这样视乎数据结构本身的业务逻辑——也就是这个数据表是做什么用的。在通讯录上面身高体重是不作为查找条件,但如果数据结构是体检表而不是通讯录,那么身高体重就是作为条件而地址电话反而是不作为查找条件的。
PHP序列化方法(serialize/unserialize)是PHP中对变量(包括实例对象、数组、字符串等)进行存放的一种方法,如serialize可以将数组变量变成字符串,让其可以存放到数据库或其他地方,而unserialize就可以将这些字符串“还原”成原来的数组变量供使用。
class m_tongxunlu extends spModel
{
        public $pk = "tid";
        public $table = "tongxunlu";
  
        public function create($row) // 参数与create的相同
        {
                $additions = array(
                        'height' => $row['height'], // 身高
                        'weight' => $row['weight'], // 体重
                );
                $row['additions'] = serialize($additions); // additions是存放身高体重等附加数据的字段
                parent::create($row); // 调用父类spModel的create方法来插入新记录
        }
       
        public function findAll($conditions = null, $sort = null, $fields = null, $limit = null) // 参数和findAll相同
        {
                $results = parent::findAll($conditions, $sort, $fields, $limit); // 调用spModel的findAll来进行查找
                if( !$results )return false; // 无返回结果则直接返回FALSE
                foreach( $results as $key => $value ){ // 循环将additions字段取出并反序列化
                        $results[$key]['additions'] = unserialize($value['additions']);
                }
                return $results; // 返回处理后的数据
        }

}
?>

上例中我们看到,在重写的create方法中,我们将身高和体重的信息,序列化后记录到additions字段中;在查询的时候,也就是重新的findAll方法中,我们将其反序列化后再返回。

通过以上的重写spModel,开发者在调用m_tongxuelu类的时候,就无需考虑身高/体重等信息的存放方式,可以按原有的create和findAll方式来进行操作。

关于数据库读写分离的技术,可以参考相关文章。

2012-08-04 22:59:14