ecmall的orm的实现[转]

转自  http://anysky131.javaeye.com/blog/435557#

对于数据库关系模型的分析,我觉得需要从两个函数说起:

Php代码

  1. //获取一个模型
  2. function &m($model_name, $params = array(), $is_new = false) 
  3. static $models = array(); 
  4. $model_hash = md5($model_name . var_export($params, true)); 
  5. if ($is_new || !isset($models[$model_hash])) 
  6.     { 
  7. $model_file = ROOT_PATH . '/includes/models/' . $model_name .  
  8. '.model.php'; 
  9. if (!is_file($model_file)) 
  10.         { 
  11. /* 不存在该文件,则无法获取模型 */
  12. return false; 
  13.         } 
  14. include_once($model_file); 
  15. $model_name = ucfirst($model_name) . 'Model'; 
  16. if ($is_new) 
  17.         { 
  18. return new $model_name($params, db()); 
  19.         } 
  20. $models[$model_hash] = new $model_name($params, db()); 
  21.     } 
  22. return $models[$model_hash]; 
  23. //获取一个业务模型
  24. function &bm($model_name, $params = array(), $is_new = false) 
  25. static $models = array(); 
  26. $model_hash = md5($model_name . var_export($params, true)); 
  27. if ($is_new || !isset($models[$model_hash])) 
  28.     { 
  29. $model_file = ROOT_PATH . '/includes/models/' . $model_name .  
  30. '.model.php'; 
  31. if (!is_file($model_file)) 
  32.         { 
  33. /* 不存在该文件,则无法获取模型 */
  34. return false; 
  35.         } 
  36. include_once($model_file); 
  37. $model_name = ucfirst($model_name) . 'BModel'; 
  38. if ($is_new) 
  39.         { 
  40. return new $model_name($params, db()); 
  41.         } 
  42. $models[$model_hash] = new $model_name($params, db()); 
  43.     } 
  44. return $models[$model_hash]; 

所谓模型,则是一个一个的数据实体,换句话说就是一个数据表,你可以基于这个模
型,调用model.base.php中的数据库操作函数来对数据进行增、删、改、查的操作。

这里的业务模型,是在实体模型基础上,再继承一次,然后对一些方法进行重写。
系统中只有三个实体有业务模型:
推荐类型 recommend;商品数据模型 goods;商品分类业务模型 gcategory;

具体操作例子:

Php代码

  1. //物品表的操作:
  2. $model_goods = & m('goods'); 
  3. $goods_info = $model_goods->get($goods_id);  

这里需要解释一下对于数据模型的操作是怎样的一个函数调用过程:
首先:$model_goods = &m('goods');
我们看一下&m()函数的代码,其中var_export()函数则是将传进来的实体,返回相应的实体类对象,因为所有的model都继承至model.base.php中的BaseModel类,这个类中定义了基本所有的操作函数,因此$model_goods对象可以对数据库进行相应的操作。
而我们再看看goods.model.php中的GoodsModel的代码:

Php代码

  1. class GoodsModel extends BaseModel 
  2. var $table  = 'goods'; 
  3. var $prikey = 'goods_id'; 
  4. var $alias  = 'g';//缩写
  5. var $_name  = 'goods'; 
  6. var $temp; // 临时变量
  7. var $_relation = array( 
  8. // 一个商品对应一条商品统计记录
  9. 'has_goodsstatistics' => array( 
  10. 'model'         => 'goodsstatistics', 
  11. 'type'          => HAS_ONE, 
  12. 'foreign_key'   => 'goods_id', 
  13. 'dependent'     => true 
  14.         ), 
  15. // 一个商品对应多个规格
  16. 'has_goodsspec' => array( 
  17. 'model'         => 'goodsspec', 
  18. 'type'          => HAS_MANY, 
  19. 'foreign_key'   => 'goods_id', 
  20. 'dependent'     => true 
  21.         ), 
  22. // 一个商品对应一个默认规格
  23. 'has_default_spec' => array( 
  24. 'model'         => 'goodsspec', 
  25. 'type'          => HAS_ONE, 
  26. 'refer_key'     => 'default_spec', 
  27. 'foreign_key'   => 'spec_id', 
  28.         ), 
  29. // 一个商品对应多个属性
  30. 'has_goodsattr' => array( 
  31. 'model'         => 'goodsattr', 
  32. 'type'          => HAS_MANY, 
  33. 'foreign_key'   => 'goods_id', 
  34. 'dependent'     => true 
  35.         ), 
  36. // 一个商品对应多个图片
  37. 'has_goodsimage' => array( 
  38. 'model'         => 'goodsimage', 
  39. 'type'          => HAS_MANY, 
  40. 'foreign_key'   => 'goods_id', 
  41. 'dependent'     => true 
  42.         ), 
  43. // 一个商品只能属于一个店铺
  44. 'belongs_to_store' => array( 
  45. 'model'         => 'store', 
  46. 'type'          => BELONGS_TO, 
  47. 'foreign_key'   => 'store_id', 
  48. 'reverse'       => 'has_goods', 
  49.         ), 
  50. // 商品和分类是多对多的关系
  51. 'belongs_to_gcategory' => array( 
  52. 'model'         => 'gcategory', 
  53. 'type'          => HAS_AND_BELONGS_TO_MANY, 
  54. 'middle_table'  => 'category_goods', 
  55. 'foreign_key'   => 'goods_id', 
  56. 'reverse'       => 'has_goods', 
  57.         ), 
  58. // 商品和会员是多对多的关系(会员收藏商品)
  59. 'be_collect' => array( 
  60. 'model'         => 'member', 
  61. 'type'          => HAS_AND_BELONGS_TO_MANY, 
  62. 'middle_table'  => 'collect', 
  63. 'foreign_key'   => 'item_id', 
  64. 'ext_limit'     => array('type' => 'goods'), 
  65. 'reverse'       => 'collect_goods', 
  66.         ), 
  67. // 商品和推荐类型是多对多的关系 todo
  68. 'be_recommend' => array( 
  69. 'model'         => 'recommend', 
  70. 'type'          => HAS_AND_BELONGS_TO_MANY, 
  71. 'middle_table'  => 'recommended_goods', 
  72. 'foreign_key'   => 'goods_id', 
  73. 'reverse'       => 'recommend_goods', 
  74.         ), 
  75.     ); 
  76. var $_autov = array( 
  77. 'goods_name' => array( 
  78. 'required'  => true, 
  79. 'filter'    => 'trim', 
  80.         ), 
  81.     ); 

这里贴出了实体goods模型类中的内容,先是表格的属性,再就是goods与其它实体之间的关联关系的定义。然后我们再看看这个函数,它是BaseModel构造函数里调用的方法,对对象中的基础变量进行初使化:

Php代码

  1. function BaseModel($params, $db) 
  2.     { 
  3. $this->db =& $db; 
  4.         !$this->alias && $this->alias = $this->table; 
  5. $this->_prefix = DB_PREFIX; 
  6. $this->table = $this->_prefix . $this->table; 
  7. if (!emptyempty($params)) 
  8.         { 
  9. foreach ($params as $key => $value) 
  10.             { 
  11. $this->$key = $value; 
  12.             } 
  13.         } 
  14.     } 

大家已经看出$_relation 中间是此实体的关联信息,然后在BaseModel类中的一个函数:

Php代码

  1. function _getJoinString($relation_info) 
  2.     { 
  3. switch ($relation_info['type']) 
  4.         { 
  5. case HAS_ONE://
  6. $model =& m($relation_info['model']); 
  7. /* 联合限制 */
  8. $ext_limit = ''; 
  9. $relation_info['ext_limit'] && $ext_limit = ' AND ' . $this->_getExtLimit($relation_info['ext_limit']); 
  10. /* 获取参考键,默认是本表主键(直接拥有),否则为间接拥有 */
  11. $refer_key = isset($relation_info['refer_key']) ? $relation_info['refer_key'] : $this->prikey; 
  12. /* 本表参考键=外表外键 */
  13. return " LEFT JOIN {$model->table} {$model->alias} ON {$this->alias}.{$refer_key}={$model->alias}.{$relation_info['foreign_key']}{$ext_limit}"; 
  14. break; 
  15. case BELONGS_TO: 
  16. /* 属于关系与拥有是一个反向的关系 */
  17. $model =& m($relation_info['model']); 
  18. $be_related = $model->getRelation($relation_info['reverse']); 
  19. if (emptyempty($be_related)) 
  20.                 { 
  21. /* 没有找到反向关系 */
  22. $this->_error('no_reverse_be_found', $relation_info['model']); 
  23. return ''; 
  24.                 } 
  25. $ext_limit = ''; 
  26.                 !emptyempty($relation_info['ext_limit']) && $ext_limit = ' AND ' . $this->_getExtLimit($relation_info['ext_limit'], $this->alias); 
  27. /* 获取参考键,默认是外表主键 */
  28. $refer_key = isset($be_related['refer_key']) ? $be_related['refer_key'] :$model->prikey ; 
  29. /* 本表外键=外表参考键 */
  30. return " LEFT JOIN {$model->table} {$model->alias} ON {$this->alias}.{$be_related['foreign_key']} = {$model->alias}.{$refer_key}{$ext_limit}"; 
  31. break; 
  32. case HAS_AND_BELONGS_TO_MANY: 
  33. /* 连接中间表,本表主键=中间表外键 */
  34. $malias = isset($relation_info['alias']) ? $relation_info['alias'] : $relation_info['middle_table']; 
  35. $ext_limit = ''; 
  36. $relation_info['ext_limit'] && $ext_limit = ' AND ' . $this->_getExtLimit($relation_info['ext_limit'], $malias); 
  37. return " LEFT JOIN {$this->_prefix}{$relation_info['middle_table']} {$malias} ON {$this->alias}.{$this->prikey} = {$malias}.{$relation_info['foreign_key']}{$ext_limit}"; 
  38. break; 
  39.         } 
  40.     } 
  41. /* 模型相关常量定义 */
  42. define('HAS_ONE', 1);                     //一对一关联
  43. define('BELONGS_TO', 2);                  //属于关联
  44. define('HAS_MANY', 3);                    //一对多关联
  45. define('HAS_AND_BELONGS_TO_MANY', 4);     //多对多关联
  46. define('DROP_CONDITION_TRUNCATE', 'TRUNCATE');  //清空

从这个函数中,我们可以看到,对于不同的关联关系,它会返回不同的关联时的查询语句片断,然后连接上主sql语句,就可以针对实体的关联实体进行相应的关联操作了。

具体操作例子:

Php代码

  1. //物品表的操作:
  2. $model_goods = & m('goods'); 
  3. $goods_info = $model_goods->find(array( 
  4. 'conditions' => "if_show=1 and closed=0", 
  5. 'fields'         => 'goods_id,goods_name,s.store_id,s.store_name', 
  6. 'join'              => 'blongs_to_store'
  7. ));  

这里的'join'  => 'blongs_to_store' ,我们从上面的:

Php代码

  1. // 一个商品只能属于一个店铺
  2. 'belongs_to_store' => array( 
  3. 'model'         => 'store', 
  4. 'type'          => BELONGS_TO, 
  5. 'foreign_key'   => 'store_id', 
  6. 'reverse'       => 'has_goods', 
  7.         ), 

这里我们可以知道这是在与store表进行关联查找了。
到这里,读者就可以知道,如果在上面进行二次开发的话,怎样进行数据库操作就已经很明确的了。
在BaseModel与cls_mysql(mysql.php)中,有很多的有关数据操作的函数,这里就不需要再一一进行解释了,而在cls_mysql中,有一些更基础的操作函数,还有仿真 Adodb 的函数,可以直接跳过BaseModel中的函数
以上介绍了如何在ecmall的平台上进行数据库操作,如果操作更加的复杂,这里还有一种更加直接的方法:

Php代码

  1. $sql = "select g.goods_id,g.goods_name, from ".DB_PREFIX."goods g, ".DB_PREFIX."goods_spec gs , ".DB_PREFIX."store s where cate_id='".$cate_id."' AND g.if_show = 1 AND g.closed = 0 and g.goods_id=gs.goods_id and g.store_id=s.store_id and gs.stock>0 and s.state=1 order by g.add_time desc limit 6"; 
  2. $goods_mod =& m('goods'); 
  3. $category_goods = $goods_mod->getAll($sql); 
  4. if(!$category_goods){ 
  5. $category_goods=array(); 
  6.             } 
  7. return $category_goods; 

就可以直接使用sql语句进行数据操作了。

还可以在BaseModel中定义自己的操作方法,其中可以使用$this->db->(cls_mysql中定义的方法) 来调用cls_mysql中的函数,从而可以添加更加复杂的数据操作函数。
好了,数据操作分析就这些了,有不对之处,还请拍砖!

posted @ 2010-05-18 15:20  liuwei0514  Views(303)  Comments(0)    收藏  举报