04月22, 2017

在 CodeIgniter 3 中高效使用 Model 类及其事务处理(2)

上一篇中,调用 Mdb::transaction() 方法同时取得了数据库链接的实例,并开启了事务,另外该方法只使用了调用名 db ,也就是只取得了 master 配置的事务支持。在下面修改后的 model 类中,支持了多配置的事务支持,跨库操作省事多了,而且是当 transaction 块中真正需要访问数据库了,才实时的取得数据库链接的实例,下面直接看代码吧。

<?php
/**
 * Mdb 自动加载的静态Model类
 * Autoload:
 *          //配置文件(config/autoload.php)中增加对该类的自动加载
 *          $autoload['model'] = array('Mdb');
 * @author xlang(xlangersir@gmail.com)
 */
class Mdb extends CI_Model
{
    private static $load; //静态引用CI_loader
    private static $curclass; //静态引用当前类
    private static $dbcontainer = array(); //数据库对象容器(区分配置名而不是调用名)
    private static $tagtrans = array(); //标记正在事务块中执行的配置名
    private static $notrans = array('default', 'default2'); //不走事务的配置名
    private static $dbconf = array( //数据库调用配置:调用名 => 配置名
        'db' => 'master',
        '_db' => 'default',
        'db2' => 'master2',
        '_db2' => 'default2'
    );

    public function __construct()
    {
        parent::__construct();
        self::$load =& $this->load;
        self::$curclass =& $this;
    }

    /**
     * 调用model
     * @param string $modelName 标准CI定义中的model类名称
     * @return mixed
     */
    public static function model($modelName)
    {
        self::$load->model($modelName);
        return self::$curclass->{$modelName};
    }

    /**
     * 事务执行
     * @param closure $closure 执行事务块
     * @return true
     */
    public static function transaction($closure)
    {
        self::_setTransactionStatus(true);
        try {
            call_user_func($closure);

            foreach (self::$tagtrans as $cfn) {
                if (self::_initDBUseConfName($cfn)->trans_status() === false) {
                    throw new \Exception('Transaction exception!');
                }
            }

            foreach (self::$tagtrans as $cfn) {
                self::_initDBUseConfName($cfn)->trans_commit();
            }
            $status = true;
        } catch (\Exception $e) {
            foreach (self::$tagtrans as $cfn) {
                self::_initDBUseConfName($cfn)->trans_rollback();
            }
            $status = false;
        }
        self::_setTransactionStatus(false);
        return $status;
    }

    /**
     * 判断当前是否正在执行事务块
     * @return bool
     */
    public static function isTransaction()
    {
        return self::_setTransactionStatus();
    }

    /**
     * 获取指定配置的数据库对象
     * @param string $dbCallName 调用名
     * @return mixed
     */
    private static function _initDBUseCallName($dbCallName)
    {
        return self::_initDBUseConfName(self::$dbconf[$dbCallName]);
    }

    /**
     * 获取指定配置的数据库对象
     * @param string $dbConfName 配置名
     * @return mixed
     */
    private static function _initDBUseConfName($dbConfName)
    {
        if (!isset(self::$dbcontainer[$dbConfName]) || !is_a(self::$dbcontainer[$dbConfName], 'CI_DB')) {
            self::$dbcontainer[$dbConfName] = self::$load->database($dbConfName, true);
        }

        $db = self::$dbcontainer[$dbConfName];
        if (self::isTransaction() && !in_array($dbConfName, self::$notrans)) {
            if (self::_tagTransaction($dbConfName)) {
                $db->trans_begin(); //实时标记事务,取得实例并开启事务
            }
        }

        return $db;
    }

    /**
     * 标记事务块中调用到的(数据库配置名)
     * action 1: self::_tagTransaction('string'); //标记"string",返回布尔值:表示是否实时标记的
     * action 2: self::_tagTransaction(null); //清空所有标记,返回值无意义
     */
    private static function _tagTransaction($name = null)
    {
        $tagStatus = false;
        if (is_string($name) && !in_array($name, self::$tagtrans)) {
            self::$tagtrans[] = $name;
            $tagStatus = true;
        } elseif (is_null($name))
            self::$tagtrans = []; //清空标记

        return $tagStatus;
    }

    /**
     * 修改是否正在执行事务块状态
     * @param bool $status
     * @return bool
     */
    private static function _setTransactionStatus($status = null)
    {
        static $isTrans = false;
        if (is_bool($status)) {
            $isTrans = $status;
            if ($isTrans === false) {
                self::_tagTransaction(null);
            }
        }
        return $isTrans;
    }

    public function __get($key)
    {
        if (array_key_exists($key,  self::$dbconf)) {
            return self::_initDBUseCallName($key);
        } else {
            return parent::__get($key);
        }
    }
}

本文链接:https://xlange.com/post/modify-static-call-model-for-codeigniter

-- EOF --

Comments

?