saber.wang 2013-05-16 17:27:16 4025次浏览 0条回复 0 0 0

开发项目过程中我们经常碰到需求变动,需求的变动经意味着数据库结构的变动(经常是加字段),数据库变动带来的开销一般比较大(dal层重构,发布难度加大),对于某些大的需求数据库变动不得不做,但如果是一些小需求,特别是一些临时性的需求,似乎为一些小需求去变动数据得不偿失,这时候如果我们的表有一个弹性列来保存需要新增的列,效果会好很多。一般我们的做法是在表结构中预置一个不定长字符串字段,通过一定保存格式将一些临时字段和它的值拼接成字符串保存至该列,在需要的时候通过解析字符串取得临时字段的值。 比如: Category表

id	int
name	string
extra	string

extra字段就是我们的万金油字段,正常category表一般只要一个name字段,如果这时候我们需要一个额外字段,比如别名(alias)来给每个category取个别名,在没有extra字段我们只能新增一个字段,但现在我们可以在extra中保存alias, 通过一定格式保存,这里我们约定extra里的格式如下 columnName1=value;columnName2=value(=分割列名和值,;分割不同列) 这样我们就可以在不改变表结构的前提下满足新的需求,但这里有一个问题,extra是字符串格式,这使得我们每次使用都要解析一下字符串,这会给开发带来很多不便,特别是对于使用orm的dal层,extra里的字段无法像表其他正常列一样通过属性来访问, 本文提供基于yii activerecord的解决方案,使得开发过程中通过正常访问对象属性访问extra列里的字段,主要是通过扩展CActiveRecord这个类来实现

class ActiveRecord extends CActiveRecord
{
    const EXTRA_COLUMN_NAME='extra';
    
    private $_extraAttributes = array(); // extra attribute name => attribute value
    private $_extraAttributeLabels = array(); //extra attribute labels name=>label
    
    public function __construct($scenario = 'insert') {
        
        $this->_extraAttributeLabels = $this->extraAttributeLabels();
        
        parent::__construct($scenario);       
    }
    
    public function beforeSave()
    {
        $this->importExtra();
        return parent::beforeSave();
    }


    public function afterFind()
    {
        $this->exportExtra();
        parent::afterFind();
    }

    /**
    * PHP getter magic method.
    * This method is overridden so that extra attribute from extra column can be accessed like properties.
    * @param string $name property name
    * @return mixed property value
    * @see getAttribute
    */
    public function __get($name) 
   {  
            if(isset($this->_extraAttributes[$name]))
                return $this->_extraAttributes[$name];
            else if(isset ($this->_extraAttributeLabels[$name]))
                return null;
            else 
                return parent::__get($name);            
    }
 

    public function __set($name, $value) {
        if(isset($this->_extraAttributeLabels[$name])){
            $this->_extraAttributes[$name] = $value;
        }
        else
            parent::__set($name, $value);
    }

    public function __isset($name) {
        if(isset($this->_extraAttributes[$name]))
            return true;
        else if(isset ($this->_extraAttributeLabels[$name]))
            return false;
        else
            return parent::__isset($name);
    }


    /**
     * @return array extra attribute labels (name=>label)
     * you can overwrite it to return customized extra attribute labels
     */
    public function extraAttributeLabels()
    {
        return array();
    }
    
    
    private function setExtraAttribute($name, $value)
    {
        if(isset($this->_extraAttributeLabels[$name])) {
            $this->_extraAttributes[$name] = $value;
            return true;
        }
        else return false;
    }

     /**
     * 导出extra列至_extraAttributes
     */
    public function exportExtra()
    {
        if(isset($this->getMetaData()->columns[self::EXTRA_COLUMN_NAME])){
            $extra = self::EXTRA_COLUMN_NAME;
            if(isset($this->$extra)){
                $nameAndValues = explode(';', $this->extra);
                foreach ($nameAndValues as $value) {
                    $nameAndValue = explode('=', $value,2);
                    if(count($nameAndValue) == 2)
                    {
                        $name = $nameAndValue[0];
                        $this->setExtraAttribute($name, $nameAndValue[1]);
                    }
                }
            }
        }
    }
    
    /**
     * 合并属性至extra列
     */
    public function importExtra()
    {
        if(isset($this->getMetaData()->columns[self::EXTRA_COLUMN_NAME])){
            $extra = self::EXTRA_COLUMN_NAME;
            $nameAndValues = array();
            foreach ($this->_extraAttributes as $name => $value) {
                $nameAndValues[] = $name.'='.$value;
            }
            
            $this->$extra = implode(';', $nameAndValues);
        }       
    }
}

修改后的Category不再继承CActiveRecord而是直接继承ActiveRecord,同时重新extraAttributeLabes方法,

class Category extends ActiveRecord
{
     public function extraAttributeLabels()
    {
	return array(
	  'alias'=>'Alias'
	);
    }
}

这样我们可以想调用category的name属性($c->name)一样调用$c->alias

    没有找到数据。
您需要登录后才可以回复。登录 | 立即注册