入口和前期铺垫就不说了,有兴趣的朋友自己看。
主要是在model的同一个函数内操作不同表碰到的问题,所以看下源代码,分析thinkphp的处理流程。
案例:直接从action里面使用model
class testaction extends action { public function cb(){ $db = new gamesmodel(); $db->setUserActiveGame(); } }上面是action,下面是model
class gamesmodel extends model { protected $connection = 'DB_GAMES'; public function setUserActiveGame(){ $activegame = array( 'user_id'=> $userid, 'login_name'=>$username, 'game_id' => $gameid, 'server_id'=> $serverid, ); //这里的params是db类初始化默认参数 $params = array( 'autoCheckFields' => false, //在同一个db类的生命周期内操作多张表,一定要关闭字段检测,否则字段数据会被model的_facade函数unset掉 'trueTableName' => 'table1', //指定表名 ); $status1 = $this->db('1','DB_GAMES',$params)->data($activegame)->add(); $acdata = array( 'login_name'=>$username, 'user_id'=>$userid, ); $where = 'sn=\''.$sn.'\' AND status <> 1'; $params2 = array( 'autoCheckFields' => false, //同params 'trueTableName' => 'table2', ); $status2 = $this->db('1','DB_GAMES',$params2)->where($where)->save($acdata); }
一步步看,首先action里面new了gamesmodel, gamesmodel继承model,model的构造函数开始执行,
public function __construct($name='',$tablePrefix='',$connection='') { // 模型初始化 $this->_initialize(); // 获取模型名称 if(!empty($name)) { if(strpos($name,'.')) { // 支持 数据库名.模型名的 定义 list($this->dbName,$this->name) = explode('.',$name); }else{ $this->name = $name; } }elseif(empty($this->name)){ $this->name = $this->getModelName();//获取到model的名称,这里是games } // 设置表前缀 if(is_null($tablePrefix)) {// 前缀为Null表示没有前缀 $this->tablePrefix = ''; }elseif('' != $tablePrefix) { $this->tablePrefix = $tablePrefix; }else{ $this->tablePrefix = $this->tablePrefix?$this->tablePrefix:C('DB_PREFIX'); } // 数据库初始化操作 // 获取数据库操作对象 // 当前模型有独立的数据库连接信息 //$this->db(0,empty($this->connection)?$connection:$this->connection); }
这里我用$m代表gamemodel的实例化对象,model类的构造函数设置了
$m->name=games
$m->tablePrefix=xxx//表前缀
在看setUserActiveGame(),$this->db('1','DB_GAMES',$params)->data($activegame)->add();这一句,
链式操作,实现方式很简单,在每个函数的最后return $this,返回自己的对象即可,我们一个一个来看
$this->db('1','DB_GAMES',$params),第一个参数是链接编号,第二个是配置文件的链接数组,第三个参数是实例化db类的带入参数,
/** +---------------------------------------------------------- * 切换当前的数据库连接 +---------------------------------------------------------- * @access public +---------------------------------------------------------- * @param integer $linkNum 连接序号 * @param mixed $config 数据库连接信息 * @param array $params 模型参数 +---------------------------------------------------------- * @return Model +---------------------------------------------------------- */ public function db($linkNum,$config='',$params=array()){ if(''===$linkNum && $this->db) { return $this->db; } static $_linkNum = array(); static $_db = array(); if(!isset($_db[$linkNum]) || (isset($_db[$linkNum]) && $config && $_linkNum[$linkNum]!=$config) ) { // 创建一个新的实例 if(!empty($config) && is_string($config) && false === strpos($config,'/')) { // 支持读取配置参数 $config = C($config); } $_db[$linkNum] = db::getInstance($config);//获取db类的实例 }elseif(NULL === $config){ $_db[$linkNum]->close(); // 关闭数据库连接 unset($_db[$linkNum]); return ; } if(!empty($params)) { if(is_string($params)) parse_str($params,$params); foreach ($params as $name=>$value){ $this->setProperty($name,$value); //设置db类的属性 } } // 记录连接信息 $_linkNum[$linkNum] = $config; // 切换数据库连接 $this->db = $_db[$linkNum]; $this->_after_db(); // 字段检测 if(!empty($this->name) && $this->autoCheckFields) $this->_checkTableInfo();//这里请看前面的函数里,在db函数的第三个参数里关闭了字段检测,重点 return $this; }
注意$_db[$linkNum] = db::getInstance($config);,他获取了db类的实例,并在后面$this->db = $_db[$linkNum];
也即$m->db是db类的实例,是一个object,
继续看db::getInstance($config);
public static function getInstance() { $args = func_get_args();//获取参数 return get_instance_of(__CLASS__,'factory',$args);//common.php中定义get_instance_of,这里__CLASS__是db }
继续看get_instance_of(__CLASS__,'factory',$args);
function get_instance_of($name, $method='', $args=array()) { static $_instance = array(); $identify = empty($args) ? $name . $method : $name . $method . to_guid_string($args);//数据库链接编号验证字符串 if (!isset($_instance[$identify])) { if (class_exists($name)) { $o = new $name(); //实例化db类 if (method_exists($o, $method)) { //调用method if (!empty($args)) { $_instance[$identify] = call_user_func_array(array(&$o, $method), $args);//db->factory() } else { $_instance[$identify] = $o->$method(); } } else $_instance[$identify] = $o;//放入db对象的$_instance[$identify], } else halt('_CLASS_NOT_EXIST_' . ':' . $name); } return $_instance[$identify];//返回db对象 }
这里得到了db对象,他又调用了factory(),去看看
明天在写,下班闪人
2013-02-01
public function factory($db_config='') { // 读取数据库配置 $db_config = $this->parseConfig($db_config); if(empty($db_config['dbms'])) throw_exception('_NO_DB_CONFIG_'); // 数据库类型 $this->dbType = strtolower($db_config['dbms']); $class = 'db'. $this->dbType; if(is_file(CORE_PATH.'lib/'.$class.'.class.php')) { // 内置驱动 $path = CORE_PATH; } // 检查驱动类 if(require_cache($path.'lib/'.$class.'.class.php')) { $db = new $class($db_config); //这里配置文件使用的mysql类型,实例化了dbmysql类 // 获取当前的数据库类型 if( 'pdo' != strtolower($db_config['dbms']) ) $db->dbType = strtoupper($this->dbType); else $db->dbType = $this->_getDsnType($db_config['dsn']); if(APP_DEBUG) $db->debug = true; //debug开关,用于记录各种调试信息 }else { // 类没有定义 throw_exception('_NOT_SUPPORT_DB_'.': ' . $db_config['dbms']); } return $db; //返回dbmysql对象 }ok,到这里,model层的具体实例化算是完成了,从头理一下,
$this->db('1','DB_GAMES',$params),调用model类的db方法,db函数里面的db::getInstance($config)获取了dbmysql类的实例,
这时候在gamesmodel里面var_dump($this);,可以看到这个对象是model类,dbmysql对象被当成model对象的一个元素,
即$m->db,
这样,具体的数据操作层的对象就实例化完毕了,等待下一步动作,$m接收后面的链式操作,处理具体事务了。
例如model中的query方法
public function query($sql,$parse=false) { if(!is_bool($parse) && !is_array($parse)) { $parse = func_get_args(); array_shift($parse); } $sql = $this->parseSql($sql,$parse); return $this->db->query($sql); //$this->db前面讲过,就是dbmysql的对象了(根据具体情况来,你配置的mongo,那就是mongo的实例对象!) }
以上就是具体的流程了,下面来说一下一个另外的东西,
在gamesmodel里面的setUserActiveGame()里面,操作了非games表的table1和table2,往这2张不同的表插入数据。
所以在$this->db('1','DB_GAMES',$params)的时候,传入了第三个参数,设置了表名和关闭掉了字段类型检测,
在model类的db()里面if(!empty($this->name) && $this->autoCheckFields) $this->_checkTableInfo();
如果开启(默认开启),会缓存当前模型表的字段结构,存入$this->fields
在后面的插入操作会检测插入的字段是否符合缓存的模型表结构(model类的_facade()方法),不符的字段会被unset掉,
所以不设置$params的话,在实例化的时候就会自动开启字段检测,缓存表字段了,
$params = array( 'fields' => '', 'autoCheckFields' => false, 'trueTableName' => $table, );
后面2张表的insert操作的字段会被全部unset掉,插入一条只有主键的空记录!!!
这样解决
声明:此文系舞林cuzn(www.wulinlw.org)原创稿件,转载请保留版权