数据库类几乎是每个PHP框架必不可少的组件之一,Yii的数据库核心主要包含两类,一类是管理数据库连接的CDbConnection及相关类,另一类是ORM(Object Relation Model),即管理数据库表记录的CActiveRecord及相关类。
首先我将介绍和分析数据库连接管理类CDbConnection,CDbConnect作为CApplication的核心组件,在应用初始化时会自动进行初始化,对于使用者来说,需要在配置文件中将配置CDbConnection的初始化配置:
//protected/config/main.php:
'components'=>array(
……
'db'=>array(
//连接字符串,设置数据库类型,数据库主机地址,数据库名
'connectionString' => 'mysql:host=localhost;dbname=blog',
//用户名
'username' => 'root',
//密码
'password' => '123456',
//字符集
'charset' => 'utf8',
),
……
在应用初始化时,会自动初始化CDbConnectiont:
//CApplication:
abstract class CApplication extends CModule
//构造函数
public function __construct($config=null)
{
……
//注册核心组件
$this->registerCoreComponents();
……
}
……
protected function registerCoreComponents()
{
//核心组件列表
$components=array(
……
'db'=>array(
'class'=>'CDbConnection',
),
……
};
//调用父类函数注册
$this->setComponents($components);
}
//CModule:
abstract class CModule extends CComponent
public function setComponents($components,$merge=true)
{
foreach($components as $id=>$component)
{
//如果是接口IApplicationComponent的一个实例
//调用setComponent
//CDbConnect继承自CApplicationComponent
//CApplicationCompnent继承了IApplicationCompnent接口
if($component instanceof IApplicationComponent)
$this->setComponent($id,$component);
else if(isset($this->_componentConfig[$id]) && $merge)
$this->_componentConfig[$id]=CMap::mergeArray($this->_componentConfig[$id],$component);
else
$this->_componentConfig[$id]=$component;
}
}
public function setComponent($id,$component)
{
if($component===null)
unset($this->_components[$id]);
else
{
$this->_components[$id]=$component;
//初始化组件
if(!$component->getIsInitialized())
$component->init();
}
}
}
接下来我们看看CDbConnect在初始化阶段,都做了那些事情:
/**
* CDbConnection与CDbCommand、CDbDataReader和CDbTransaction一起合作提供对DBMS的数据访问功能
* 它使用PDO作为数据库连接驱动
*/
class CDbConnection extends CApplicationComponent
{
//以下属性在配置文件中设置
public $connectionString;
public $username='';
public $password='';
public $charset;
//表结构缓存时间,单位是秒,默认不缓存
public $schemaCachingDuration=0;
//表结构缓存排除列表
public $schemaCachingExclude=array();
//表结构缓存ID
public $schemaCacheID='cache';
//自动连接,默认为true,如果设置为false,在使用时才会连接
public $autoConnect=true;
//模仿prepare,默认为false,PDO会使用原生的prepare,对于某些数据库(例如MySql),最好设置为true,这样就不会使用有bug的PDO原生prepare,关于PDO的prepare相关信息可以查阅:http://php.net/manual/en/pdo.prepare.php
public $emulatePrepare=false;
//是否记录绑定变量(如果使用prepare方式进行查询,如果记录sql语句,是看不到绑定变量的值的),调试时使用
public $enableParamLogging=false;
//是否记录SQL语句,调试时使用
public $enableProfiling=false;
//数据表前缀
public $tablePrefix;
//在建立数据库连接之后立即执行的SQL语句
public $initSQLs;
//目前支持的数据库驱动
public $driverMap=array(
'pgsql'=>'CPgsqlSchema', // PostgreSQL
'mysqli'=>'CMysqlSchema', // MySQL
'mysql'=>'CMysqlSchema', // MySQL
'sqlite'=>'CSqliteSchema', // sqlite 3
'sqlite2'=>'CSqliteSchema', // sqlite 2
'mssql'=>'CMssqlSchema', // Mssql driver on windows hosts
'dblib'=>'CMssqlSchema', // dblib drivers on linux (and maybe others os) hosts
'sqlsrv'=>'CMssqlSchema', // Mssql
'oci'=>'COciSchema', // Oracle driver
);
……
//初始化
public function init()
{
//调用父类初始化
parent::init();
//
if($this->autoConnect)
$this->setActive(true);
}
……
public function setActive($value)
{
//如果需要设置的状态与当前状态不同
if($value!=$this->_active)
{
if($value)
$this->open();
else
$this->close();
}
}
protected function open()
{
if($this->_pdo===null)
{
if(empty($this->connectionString))
throw new CDbException(Yii::t('yii','CDbConnection.connectionString cannot be empty.'));
try
{
Yii::trace('Opening DB connection','system.db.CDbConnection');
//创建PDO实例
$this->_pdo=$this->createPdoInstance();
//初始化连接
$this->initConnection($this->_pdo);
$this->_active=true;
}
catch(PDOException $e)
{
//异常处理
……
}
}
}
……
//创建PDO实例
protected function createPdoInstance()
{
$pdoClass='PDO';
if(($pos=strpos($this->connectionString,':'))!==false)
{
$driver=strtolower(substr($this->connectionString,0,$pos));
if($driver==='mssql' || $driver==='dblib')
$pdoClass='CMssqlPdoAdapter';
}
return new $pdoClass($this->connectionString,$this->username,
$this->password,$this->_attributes);
}
……
//初始化连接
protected function initConnection($pdo)
{
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
if($this->emulatePrepare && constant('PDO::ATTR_EMULATE_PREPARES'))
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES,true);
if($this->charset!==null)
{
$driver=strtolower($pdo->getAttribute(PDO::ATTR_DRIVER_NAME));
//pg,mysql,mysqli需要设置字符编码
if(in_array($driver,array('pgsql','mysql','mysqli')))
$pdo->exec('SET NAMES '.$pdo->quote($this->charset));
}
//如果有初始化SQL,则执行
if($this->initSQLs!==null)
{
foreach($this->initSQLs as $sql)
$pdo->exec($sql);
}
}
}
以上便是CDbConnect初始化的过程,其实CDnConnection主要功能是为用户封装了PDO的实例,并且自定义了一些配置,方便用户使用。
如果我们在项目中不想使用应用核心组件数据库连接,想自定义一个数据库连接,那么可以直接创建CDbConnection的对象:
$connection=new CDbConnection($dsn,$username,$password);
//CDbConnection的祖先类Ccomponent包含有__isset方法,自动会调用名称为set[attribute]的方法,即会调用CdbConnection::setActive方法
$connection->active=true;
如果想直接执行一条SQL:
$command=$connection->createCommand($sqlStatement);
$command->execute(); //
// 执行SQL获取结果
$reader=$command->query();
//$row代表一条记录
foreach($reader as $row) ...
与CDbConnection密切相关的几个类有:
CDbCommand:用户生成SQL语句,执行SQL语句
CDbTransaction:用于管理数据库事务的类
CDbDataReader:用于管理SQL语句执行结果的类,实现了Iterator接口,能够像遍历数组一样遍历自身
这里不再详述每个类的内部细节,我们只需要了解用法即可。