Yii2 自动分表 model

其实是借鉴了一下某同学的《动态model》

<?php
/**
 * Created by IntelliJ IDEA.
 * User: Septan
 * Date: 2017/8/4
 * Time: 下午5:25
 */

namespace app\library;


use Yii;
use yii\base\InvalidCallException;
use yii\base\InvalidConfigException;
use yii\db\ActiveQuery;
use yii\db\Exception;
use yii\helpers\ArrayHelper;

/**
 * 分表基础类
 * Class Modelx
 * @package library
 */

class Modelx extends \yii\db\ActiveRecord
{

    //原始表名,表结构母版
    protected static $originalName = '';
    //动态表名
    protected static $tableName = '';
    //分表关键字
    protected static $targetKey = '';
    //redis表名set key
    protected static $tableSetKey = '';

    /**
     * @param null $targetKey
     * @param array $config
     * @throws Exception
     */
    public function __construct($targetKey = null, $config = []){
        parent::__construct($config);
        static::$tableName = static::renderTable($targetKey);
    }

    public static function tableName(){
        return static::$tableName;
    }

    /**
     * 根据关键值获得表名
     * @throws Exception
     * @return string
     */
    public static function getTableName(){
        throw new Exception(get_called_class() . "::" . __FUNCTION__ . ' must be override');
        //return static::$originalName . '_'. (static::$targetKey % 10);
    }

    /**
     * 根据vip_card探测表名
     * @param null $targetKey
     * @return string
     * @throws Exception
     */
    public static function renderTable($targetKey = null){
        if(!$targetKey)
            return static::$originalName;

        static::$targetKey = $targetKey;
        $tableName = static::getTableName();
        //if hit cache
        /** @var \Redis $redis */
        $redis = Yii::$app->redis;
        if($redis->sismember(static::$tableSetKey, $tableName))
            return $tableName;

        //if hit db
        $db = static::getDb();
        if($db->createCommand("SHOW TABLES LIKE '{$tableName}'")->queryAll()){
            $redis->sadd(static::$tableSetKey, $tableName);
            return $tableName;
        }else{ //maybe
            $redis->srem(static::$tableSetKey, $tableName);
        }

        //just do it
        $originalTable = static::$originalName;
        $createTableRet = $db->createCommand("SHOW CREATE TABLE `{$originalTable}`")->queryOne();
        $createTable = str_replace("`{$originalTable}`","`{$tableName}`",$createTableRet['Create Table']);
        $createTable = preg_replace('/\sAUTO_INCREMENT=\d+/','',$createTable);
        try{
            $db->createCommand($createTable)->execute();
            $redis->sadd(static::$tableSetKey, $tableName);
        }catch (Exception $ex){
            throw new Exception("Error execute sql: {$createTable}");
        }

        return $tableName;

    }

    /**
     * 扩展的find
     * @param $targetKey
     * @return \yii\db\ActiveQuery
     */
    public static function findx($targetKey = null){
        static::$tableName = static::renderTable($targetKey);
        return Yii::createObject(ActiveQuery::className(), [get_called_class(), ['from' => [static::tableName()]]]);
    }

    /**
     * 扩展的findAll
     * @param null $targetKey
     * @param array $params
     * @return \yii\db\ActiveQuery[]
     */
    public static function findAllx($targetKey = null,$params = []){
        return static::findByConditionx($targetKey, $params)->all();
    }

    /**
     * @Override
     * @param array $row
     * @return static
     */
    public static function instantiate($row){
        return new static(static::$targetKey);
    }

    /**
     * 禁止使用findBySql
     * @param string $sql
     */
    public static function findBySql($sql){
        throw new InvalidCallException("not allowed. {$sql}");
    }

    /**
     * 扩展的findOne
     * @param null $targetKey
     * @param array $condition
     * @return null|static|ActiveQuery
     */
    public static function findOnex($targetKey = null, $condition = []){
        return static::findByConditionx($targetKey, $condition)->one();
    }

    /**
     * 内部实现
     * @param null $targetKey
     * @param $condition
     * @return ActiveQuery[]|ActiveQuery
     * @throws InvalidConfigException
     */
    protected static function findByConditionx($targetKey = null, $condition){

        $query = static::findx($targetKey);

        if (!ArrayHelper::isAssociative($condition)) {
            // query by primary key
            $primaryKey = static::primaryKey();
            if (isset($primaryKey[0])) {
                $condition = [$primaryKey[0] => $condition];
            } else {
                throw new InvalidConfigException('"' . get_called_class() . '" must have a primary key.');
            }
        }

        return $query->andWhere($condition);
    }

}

分表model

<?php

namespace app\models;

use Yii;
use app\library\Modelx;

/**
 * Test 根据vip_card分表实现
 * Class Test
 * @package app\models
 * @use $m = new Test($vip_card);
 * @use Test::findx($vip_card)->where([]);
 * @use Test::findAllx($vip_card,[]);
 * @use Test::findOnex($vip_card,[]);
 *
 */

class Test extends Modelx
{
    //原始表名,表结构母版
    protected static $originalName = 'test';
    //redis表名set key
    protected static $tableSetKey = 'project:tableset';

    /**
     * 根据分表关键值获得表名
     * @Override
     * @return string
     */
    public static function getTableName(){
        return static::$originalName . '_'. (static::$targetKey % 10);
    }


}


分表使用原始表为模板进行结构复制,复制后的分表结构,不会同步原始表的结构、索引等改变。
建议在分表使用之前,对原始表做好充分优化。
分表之后的Model,不再支持findBySql方法。


  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值