qcheat702 2015-12-15 12:17:16 5899次浏览 3条评论 1 2 0

说明

处理使用类似WordPress Posts 表 和 Postmeat 表 之间的元数据关系的增删改查操作。

版本

V 0.8.0

使用步骤

假定:表postmeta是表posts的元数据从表(数据结构请查询 WordPress 对应的表结构)
1、主表模型(posts)继承 MetaModel
2、重写静态函数 metaModelName() 和 metaRelevanceField()。必须重写。
3、使用处理函数meta进行元数据处理

示例


$post = Posts::findOne(1);
$post->meta('key'); //获取元数据 key 的反序列化内容 ,为空返回 null
$post->meta('key',false); //直接获取元数据 key 内容
$post->meta('key','value'); //为元数据 key 写入序列化后的value值
$post->meta('key','value',false); //为元数据 key 直接写入value值
$post->meta(['key'...],['value'...]); //为元数据键值序列写入对应的值
$post->deleteMeta('key'); //删除元数据 $key


更新地址:http://www.sv0.cn/blog/metamodel/
V0.8.0 下载地址:http://www.sv0.cn/blog/wp-content/uploads/2015/12/MetaModel.rar
下载地址在更新后会发生变化,请移步 更新地址 查看


/**
 * MetaModel - Yii2 - 元数据子表数据处理类
 * 
 * 处理使用类似WordPress Posts 表 和 Postmeat 表 之间的元数据关系的增删改查操作。
 *
 * 使用步骤 
 * 假定:posts 是主表 postmeta 是从表(数据结构请查询 WordPress 对应的表结构)
 * 1、主表(posts)继承 MetaModel 类别
 * 2、重写静态函数 metaModelName() 和 metaRelevanceField()。必须重写。
 * 3、使用处理函数meta进行元数据处理
 *     $post = Posts::findOne(1);
 *     $post->meta('key'); //获取元数据 key 的反序列化内容 ,为空返回 null
 *     $post->meta('key',false); //直接获取元数据 key 内容
 *     $post->meta('key',$value); //为元数据 key 写入序列化后的value值
 *     $post->meta('key',$value,false); //为元数据 key 直接写入value值
 *     $post->meta(['key'...],[$value...]); //为元数据键值序列写入对应的值
 *     $post->delete('key'); //删除元数据 $key *     
 * @version V 0.8.0
 * @link http://www.sv0.cn/
 * @copyright qcheat702@163.com
 * @license http://www.sv0.cn/blog/metamodel/
 * @update http://www.sv0.cn/blog/metamodel/
 */

namespace app\models;

use Yii;
use yii\base\Model;

class MetaModel extends \yii\db\ActiveRecord
{
    public $_config;
    public $_metas;
    public $_metaErrors;
    /**
     * =======================需要重写的函数
     */

    /**
     * 元数据规则,(格式与Yii2 Rule定义方式一致)需要重写
     * @return array 规则序列 目前只有 exist 生效 [[*],'safe'] 代表接受所有未定义的元数据
     */
    public function metaRules(){ 
        return [
            [['*'], 'safe']
        ];}

    /**
     * 元数据键值标签,未定义的键值标签将是键值本身 需要重写
     * @return  array 标签序列
     */
    public function metaLabels(){return [];}

    /**
     * 静态 包含命名空间的完整元数据模型名称,需要重写
     * @return String [description]
     */
    static public function metaModelName(){return '[namaspace]/ModelName';}

    /**
     * 静态 元数据关联字段,需要重写
     * @return String 元数据模型与主体主键关联的字段名称
     */
    static public function metaRelevanceField(){return 'ModelName_ID';}

    /**
     * =======================需要重写的函数 end
     */


    
    /**
     * 获取/设置 元数据
     * @param  string/array $keys 
     *             元数据键值/元数据键值对序列
     *             keys为字符串,不设置$values,则返回值
     *             keys为字符串,设置$values,则设置值
     *             keys键值序列,按序列则返回值
     *             keys键值序列,$values与之一一对应,则设置值
     * @param  mixed/array/null(default) $value 值
     * @param  Boolean $encode 是否编码,默认true
     * @return [type]        [description]
     */
    public function meta($keys=null,$values=null,$encode=true)
    {
        //如果键值是字符串
        if(  is_string($keys)  )
        {
            //如果不设置值
            if(  is_null($values)  )
            {
                $meta = $this->getMetas($keys,$encode);
                if(  is_null($meta)  )return null;
                // return $meta;
                return $meta->meta_value;
                // else return $meta->meta_value;  
            }else
                $this->setMatas($keys,$values,$encode);
        }
        //如果键值是数组
        if(  is_array($keys)  ){
            if(  is_array($values)  ){
                if(  count($keys) !== count($values)  )
                    throw new ErrorException('键值序列与值序列长度不一致'); 
                foreach ($keys as $ko => $option)
                    $this->setMeta($option,$values[$ko],$encode);
            }else
                return $this->getMetas();
        }
    }

    //获取配置 暂未使用
    public function getConfig(){
        $config = $this->_config;
        if(  !empty($config)  )
            foreach ($config as $key => $value)
            {
                if(  !isset($value['label'])  )$value['label'] = $key;
                $config[$key] = $value;
            }
        return $config;
    }

    //写入配置 暂未使用
    public function setConfig($config){
        $this->_config = $config;
    }

    /**
     * 元数据值编码
     * @param  mixed $data 数据
     * @return string  编码后的元数据值
     */
    public function encodeMetaValue($data)
    {
        return serialize($data);
    }

     /**
     * 元数据值解码
     * @param  string $data 数据
     * @return mixed  解码后的元数据值
     */
    public function decodeMetaValue($string)
    {
        return @unserialize($string);
    }

    /**
     * 获取元数据标签
     * @param  mixed[string/array/null] $metas 元数据键值或键值序列
     * @return mixed[string/array/null]        对应的元数据标签。元数据为字符串,返回其标签字符串,未定义标签则返回其本身;元数据为数组,则返回以其自身为键值的元数据标签序列;为空返回所有已定义的标签
     */
    public function getMataLabels($meta_keys=null)
    {
        //获取所有的元数据标签
        $metaLabels = $this->metaLabels();

        //如果没有参数,返回全部已定义以的标签
        if(  is_null($meta_keys)  )return $metaLabels;

        //如果参数是字符串
        if(  is_string($meta_keys)  )
            return isset($metaLabels[$meta_keys])?$metaLabels[$meta_keys]:$meta_keys;

        //如果参数是数组
        if(  is_array($meta_keys)  )
        {
            $_arr = null;
            foreach ($meta_keys as $key => $value)
                $_arr[$value] = isset($metaLabels[$value])?$metaLabels[$value]:$value;            
            return $_arr;
        } 

        return null;
    }

    /**
     * 获取元数据规则
     * @param  string $meta_key 元数据键值
     * @return array/null 元数据规则数组,无效的键值会抛出错误
     */
    public function getMetaRules($meta_key=null)
    {
        $metarules = null;
        foreach ($this->metaRules() as $kr => $vr) 
        {
            foreach ($vr[0] as $km => $vm)
                $metarules[$vm][$vr[1]]  =  isset($vr[2])?$vr[2]:true;
                $metarules[$vm]['exist'] =  true;
        }
        return $metarules;
    }

    /**
     * 校验 (该函数尚未完成)
     * @param  string $meta_key 键
     * @param  string/null(default) $value    值,值为空只校验是否存在,否则校验值是否符合规则
     * @return boolean 是否校验成功         
     */
    public function metaValidate($meta_key,$value=null)
    {
        $metarules = $this->getMetaRules();
        //校验是否存在
        if(  isset($metarules['*']['safe']) || isset($metarules[$meta_key]['exist'])  )
            return true;
        
        //校验键值是否符合规则
        // (待补充)
        $this->addError('_metaErrors',$meta_key.' : 不在可接受的元数据简直范围内');
        return false;
    }

    /**
     * 获取元数据属性键值对
     * @param  mixed[string/array] $metas 元数据键值/元数据键值序列
     * @return array/null        参数为字符串返回单一元数据/参数为元数据键值序列时返回以元数据键值为键值的元数据序列
    */
    public function getMetaAttributes($meta_keys)
    {
        if(  empty($meta_keys)  ) return null;
        //如果元数据键值是字符串
        if (  is_string($meta_keys)  )
            return $this->getMeta($meta_keys);
        //如果元数据键值是数组序列
        if(  is_array($meta_keys)  )
        {
            $_arr = null;
            foreach ($meta_keys as $km => $meta_key)
            {
                $_mata = $this->getMeta($meta_key);
                if(  !is_null($_mata)  )
                    $_arr[$meta_key] = $this->getMeta($meta_key);
            }
            return $_arr;
        }

        return null;
    }
    
    /**
     * 获取单个元数据属性
     * @param  string $meta 元数据键值
     * @return array/null   元数据属性数组
     */
    public function getMetaAttribute($meta_key)
    {
        if(  $this->metaValidate($meta_key)  )return null;

        return 
        [
            'key' => $meta_key,
            'label' => $this->getMataLabels($meta_key),
            'rules' => $this->getMetaRules($meta_key)
        ];
    }


    /**
     * 批量设置元数据
     * @param string/array $options 元数据键值或键值对序列
     * @param mixed $value  元数据值,当参数$options为字符串时生效,空值不更新
     * @param boolean $encode  是否编码,默认true
     */
    public function setMatas($options,$value=null,$encode=true)
    {
        if(  is_string($options) )
            $this->setMeta($options,$value,$encode);

        if(  is_array($options)  )
            foreach ($options as $key => $option){                
                $this->setMeta($key,$option,$encode);
            }
                
    }

    /**
     * 设置元数据
     * @param string $key   元数据键值
     * @param mixed $value 元数据值
     * @param boolean $encode 是否编码,默认true
     * @return  true/false 设置成功或者失败
     */
    public function setMeta($key,$value,$encode=true)
    {

        if(  empty($this->primaryKey)  )return false;
        //校验
        if(  !$this->metaValidate($key,$value)  )            
            return false;

        $meta = $this->getMetas($key);
        if(  is_null($meta)  ){            
            $meta =  static::metaModel();            
            $relevance = static::metaRelevanceField();
            $meta->$relevance = $this->primaryKey;
            $meta->meta_key = $key;
        }
  
        $meta->meta_value = ($encode?$this->encodeMetaValue($value):$value.'');
        if(  !$meta->save()  ){
            // $this->addError('_metaErrors',$meta->getErrors());
            //errors:保存到元数据表出错
            throw new ErrorException('保存到元数据表'.$meta->tableName().'出错'); 
            return false;
        }
        return true ;            
    }


    /**
     * 获取指定的元数据
     * @param  mixed[string/array/null(default)] $metas 元数据键值或者键值序列,为空返回所有已经存在的元数据
     * @param boolean $decode 是否解编码,默认true
     * @return array/null        元数据AR对象或AR对象序列
     */
    public function getMetas($meta_keys=null,$decode=true)
    {
        $metas = $this->findMetaModels();
        if(  $decode  )
            foreach ($metas as $km => $meta){
                $_decodeValue = $this->decodeMetaValue($meta->meta_value);
                //如果解码正确,则输出解码后的数据
                if(  $_decodeValue !== false  )
                    $meta->meta_value = $_decodeValue;
            }

        if(  is_string($meta_keys)  ){
            foreach ($metas as $key => $meta)
                if(  $meta->meta_key ===  $meta_keys ) return $meta;
            return null;
        }
        if(  is_array($meta_keys)  ){
            $metas = null;
            foreach ($metas as $key => $meta)
                if(  in_array($meta->meta_key, $meta_keys)  )
                    $metas[] = $meta;
            return $metas;
        }
        return null;
    }

    /**
     * 获取元数据
     * @param  [type]  $meta_key 元数据键值
     * @param  boolean $decode   是否解码( decodeMetaValue )
     * @return models[]          元数据模型序列
     */
    public function getMeta($meta_key,$decode=true)
    {
        $meta = $this->findMetaModels($meta_key);
        if(  is_null($meta)  )return false;
        if(  $decode  ){
            $_decodeValue = $this->decodeMetaValue($meta->meta_value);
            //如果解码正确,则输出解码后的数据
            if(  $_decodeValue !== false  )
                $meta->meta_value = $this->decodeMetaValue($meta->meta_value);
        }
        return $meta;
    }

    /**
     * 删除元数据
     * @param  String $meta_keys [description]
     * @return true/False            [description]
     */
    public function deleteMeta($meta_key){
        $metas = $this->getMetas($meta_key);
        if(  empty($metas)  )return false;
        return $metas->delete();
    }

    /**
     * 获取元数据模型
     * @return model 
     */
    static private function metaModel()
    {
        $model = static::metaModelName();
        return new $model;
    }


    /**
     * 查找元数据模型集
     */     
    private function findMetaModels($forceNew = false)
    {
        if(  !isset($this->_metas) || $forceNew  )
        {
            $model = static::metaModel();
            $relevance = static::metaRelevanceField();
            $query = $model->find();
            $query->andFilterWhere([$relevance=>$this->primaryKey]);
            $this->_metas = $query->all();
        }
        $metas = $this->_metas;
        return $metas;
    }

    public function getMetaQuery(){
        $meta = static::metaModel();
        $metaRelevanceField = static::metaRelevanceField();
        $primaryKey = static::primaryKey();
        $primaryKey = $primaryKey[0];
        return $this->hasMany($meta->className(), [$metaRelevanceField =>$primaryKey ]);
    }

    /**
     * [findWithMeta description]
     * @param  [type] $conditions [description]
     * @return ActiveQuery
     */
    static public function findWithMeta($conditions){

        $query = static::find();
        $query->from(static::tableName() . ' m');
        $meta = static::metaModel();
        $primaryKey = static::primaryKey();
        $primaryKey = $primaryKey[0];
        $metaRelevanceField = static::metaRelevanceField();

        if(  is_array($conditions)  )
        {
            $i = 1;
            foreach ($conditions as $key => $condition)
            {
                $alias = 't'.$i;
                $on = '
                    m.`'.$primaryKey.'` = '.$alias.'.`'.$metaRelevanceField.'`
                    AND ('.$alias.'.meta_key=\''.$key.'\' AND '.$alias.'.meta_value = \''.$condition.'\') 
                    AND '.$alias.'.meta_value != \'\'';
                $query->innerJoin($meta::tableName().' '.$alias,$on);
                $i++;
            }
        }

        return $query;
    }
}


觉得很赞
您需要登录后才可以评论。登录 | 立即注册