ECMALL源码阅读四:对象数据映射

ECMALL中,对象数据映射,是通过BaseModel(eccore\model\model.base.php)对象实现的。所有的模型都继承了这一对象。 在这一对象中,实现了模型对数据库的对应关系及操作方法。

<?php
//货物表的操作:  
$model_goods = & m('goods');  
$goods_info = $model_goods->find(array(  
                'conditions' => "if_show=1 and closed=0",  
                'fields'         => 'goods_id,goods_name,s.store_id,s.store_name',  
                'join'              => 'blongs_to_store'  
)); 
?>

我们通过goods模型的find方法获得所有正在销售的货物。而find的方法就是在BaseModel中实现的。

而模型跟数据库之间的对应关系以及模型与模型的关系是如何实现的呢?

货物模型(Goods.model.php)

<?php
class GoodsModel extends BaseModel
{
    var $table  = 'goods';
    var $prikey = 'goods_id';
    var $alias  = 'g';//缩写
    var $_name  = 'goods';
    var $temp; // 临时变量
    var $_relation = array(
        // 一个商品对应一条商品统计记录
        'has_goodsstatistics' => array(
            'model'         => 'goodsstatistics',
            'type'          => HAS_ONE,
            'foreign_key'   => 'goods_id',
            'dependent'     => true
        ),
        // 一个商品对应多个规格
        'has_goodsspec' => array(
            'model'         => 'goodsspec',
            'type'          => HAS_MANY,
            'foreign_key'   => 'goods_id',
            'dependent'     => true
        ),
        // 一个商品对应一个默认规格
        'has_default_spec' => array(
            'model'         => 'goodsspec',
            'type'          => HAS_ONE,
            'refer_key'     => 'default_spec',
            'foreign_key'   => 'spec_id',
        ),
        // 一个商品对应多个属性
        'has_goodsattr' => array(
            'model'         => 'goodsattr',
            'type'          => HAS_MANY,
            'foreign_key'   => 'goods_id',
            'dependent'     => true
        ),
        // 一个商品对应多个图片
        'has_goodsimage' => array(
            'model'         => 'goodsimage',
            'type'          => HAS_MANY,
            'foreign_key'   => 'goods_id',
            'dependent'     => true
        ),
        // 一个商品只能属于一个店铺
        'belongs_to_store' => array(
            'model'         => 'store',
            'type'          => BELONGS_TO,
            'foreign_key'   => 'store_id',
            'reverse'       => 'has_goods',
        ),
        // 商品和分类是多对多的关系
        'belongs_to_gcategory' => array(
            'model'         => 'gcategory',
            'type'          => HAS_AND_BELONGS_TO_MANY,
            'middle_table'  => 'category_goods',
            'foreign_key'   => 'goods_id',
            'reverse'       => 'has_goods',
        ),
        // 商品和会员是多对多的关系(会员收藏商品)
        'be_collect' => array(
            'model'         => 'member',
            'type'          => HAS_AND_BELONGS_TO_MANY,
            'middle_table'  => 'collect',
            'foreign_key'   => 'item_id',
            'ext_limit'     => array('type' => 'goods'),
            'reverse'       => 'collect_goods',
        ),
        // 商品和推荐类型是多对多的关系 todo
        'be_recommend' => array(
            'model'         => 'recommend',
            'type'          => HAS_AND_BELONGS_TO_MANY,
            'middle_table'  => 'recommended_goods',
            'foreign_key'   => 'goods_id',
            'reverse'       => 'recommend_goods',
        ),
    );

}
?>

table,prikey属性分别定义了表跟主键。$_relation 数组定义了模型与模型的关系。BaseModel对象的_getJoinString方法负责为这种对应关系返回表链接SQL语句

返回join SQL语句(Module.base.php)

<?php
 function _getJoinString($relation_info)
    {
        switch ($relation_info['type'])
        {
            case HAS_ONE:
				 /* 一对一对应 */
                $model =& m($relation_info['model']);

                /* 联合限制 */
                $ext_limit = '';
                $relation_info['ext_limit'] && $ext_limit = ' AND ' 
				. $this->_getExtLimit($relation_info['ext_limit']);

                /* 获取参考键,默认是本表主键(直接拥有),否则为间接拥有 */
                $refer_key = isset($relation_info['refer_key']) ?
				$relation_info['refer_key'] : $this->prikey;

                /* 本表参考键=外表外键 */
                return " LEFT JOIN {$model->table} {$model->alias} ON {$this->alias}
				.{$refer_key}={$model->alias}.
				{$relation_info['foreign_key']}{$ext_limit}";
            break;
            case BELONGS_TO:
                /* 属于关系与拥有是一个反向的关系 */
                $model =& m($relation_info['model']);
                $be_related = $model->getRelation($relation_info['reverse']);
                if (empty($be_related))
                {
                    /* 没有找到反向关系 */
                    $this->_error('no_reverse_be_found', $relation_info['model']);

                    return '';
                }
                $ext_limit = '';
                !empty($relation_info['ext_limit']) && $ext_limit = ' AND ' 
				. $this->_getExtLimit($relation_info['ext_limit'], 
				$this->alias);
                /* 获取参考键,默认是外表主键 */
                $refer_key = isset($be_related['refer_key']) ? 
				$be_related['refer_key']:$model->prikey ;

                /* 本表外键=外表参考键 */
                return " LEFT JOIN {$model->table} {$model->alias} ON {$this->alias}
				.{$be_related['foreign_key']} = {$model->alias}.
				{$refer_key}{$ext_limit}";
            break;
            case HAS_AND_BELONGS_TO_MANY:
                /* 连接中间表,本表主键=中间表外键 */
                $malias = isset($relation_info['alias']) ? $relation_info['alias'] 
				: $relation_info['middle_table'];
                $ext_limit = '';
                $relation_info['ext_limit'] && $ext_limit = ' AND ' 
				. $this->_getExtLimit($relation_info['ext_limit'],
				$malias);
                return " LEFT JOIN {$this->_prefix}{$relation_info['middle_table']} 
				{$malias} ON {$this->alias}
				.{$this->prikey} = {$malias}.
				{$relation_info['foreign_key']}{$ext_limit}";
            break;
        }
    }
?>

find方法,通过以上这些属性生成完整SQL语句查询表数据。

货物模型(Goods.model.php)

<?php
function find($params = array())
{
	extract($this->_initFindParams($params));

	/* 字段(SELECT FROM) */
	$fields = $this->getRealFields($fields);
	$fields == '' && $fields = '*';

	/*通过对象table属性获得数据表名*/
	$tables = $this->table . ' ' . $this->alias;

	/* 左联结(LEFT JOIN) */
	$join_result = $this->_joinModel($tables, $join);

	/*通过对象prikey属性获得数据表主键*/
	if ($index_key == $this->prikey || (is_array($index_key) 
	&& in_array($this->prikey, $index_key)))
	{
		/* 如果索引键里有主键,则默认在要查询字段后加上主键 */
		$fields .= ",{$this->alias}.{$this->prikey}";
	}

	/* 条件(WHERE) */
	$conditions = $this->_getConditions($conditions, true);

	/* 排序(ORDER BY) */
	$order && $order = ' ORDER BY ' . $this->getRealFields($order);

	/* 分页(LIMIT) */
	$limit && $limit = ' LIMIT ' . $limit;
	if ($count)
	{
		$this->_updateLastQueryCount("SELECT COUNT(*) as c 
		FROM {$tables}{$conditions}");
	}

	/* 生成完整的SQL */
	$sql = "SELECT {$fields} FROM {$tables}{$conditions}{$order}{$limit}";

	return $index_key ? $this->db->getAllWithIndex($sql, $index_key) :
						$this->db->getAll($sql);
}
?>

我们可以扩展BaseModel对象的方法,编写自己需要的对象数据映射方法。