YII Framework学习教程-YII的Model-数据库操作1-CActiveRecord
YII中的DAO(数据库访问对象)是建立在PHP的PDO之上的,所以你开发的应用可以很容易的在不同的数据库系统平台之间进行切换,而只需要修改少量代码。YII采用ORM(Object-Relational Mapping)的设计模式进行数据库编程,简化了一些繁琐的的数据库操作。
在YII中的DAO的相关类主要存放在/yii_dev/yii/framework/db和/yii_dev/yii/framework/web,通常名称带有DB或者Data。
在YII中操作数据库我们主要是使用CActiveRecord,它继承CModel。用CActiveRecord可以完成对数据CURD操作的大部分任务。 但是对于有复杂逻辑的数据库查询操作,用CActiveRecord就力不从心了,后续将会讲解YII如何处理复杂的数据库查询操作。
在ORM中通常用Object(对象)来表示一个Relation(关系),可以理解为一个类对应一个数据表或者视图。类的属性可以认为是和数据表的列一一对应的,类中的方法是用于对数据库操作的方法例如CURD。
这里讲一下CActiveRecord如何使用。
一般如果是基本的操作我们可以用yiic自动生产curd系列的操作。这里在不多说明。主要说明如果手写代码,我们的程序要遵循的基本规则。
1.设计表
开始之前我们先来定义我们要操作的表。这里使用mysql,数据库是testdrive,使用的表结构如下:
[sql] view plaincopy
- CREATE TABLE `tbl_user` (
- `id` INT(11) NOT NULL AUTO_INCREMENT,
- `username` VARCHAR(128) NOT NULL,
- `password` VARCHAR(128) NOT NULL,
- `email` VARCHAR(128) NULL DEFAULT NULL,
- PRIMARY KEY (`id`)
- )
- COLLATE="utf8_general_ci"
- ENGINE=MyISAM
2.配置数据库链接
在/testwebap/protected/config/main.php配置文件
[php] view plaincopy
- "db"=>array(
- "connectionString" => "mysql:host=localhost;dbname=testdrive",
- "emulatePrepare" => true,
- "username" => "root",
- "password" => "",
- "charset" => "utf8",
- ),
3.定义数据库操作类。
要继承CActiveRecord ,其实经过前面章节的说明,这里的代码完全可以自动生成,这里定义基本的类,做说明。基本代码如下:
[php] view plaincopy
- <?php
- class TblUser extends CActiveRecord
- {
- public static function model($className=__CLASS__)
- {
- return parent::model($className);
- }
- public function tableName()
- {
- return "tbl_user";
- }
- }
4.使用:实例化我们的数据库操作类。
在/yii_dev/testwebap/protected/modules/testmod/controllers/DefaultController.php定义DefaultController。具体结构如下:
[php] view plaincopy
- <?php
- class DefaultController extends Controller
- {
- public function actionIndex()
- {
- $this->render("index");
- }
- }
实例化我们的数据库操作类,具体代码如下:
[php] view plaincopy
- <?php
- class DefaultController extends Controller
- {
- public function actionIndex()
- {
- $model=new TblUser;
- var_dump($model);
- exit;
- $this->render("index");
- }
- }
运行http://www.localyii.com/testwebap/index.php/testmod/default/index
如果不出异常,会打印如下类似的信息,表示你的操作类可以正常使用
[php] view plaincopy
- object(TblUser)#14 (12) { ["_md":"CActiveRecord":private]=> object(CActiveRecordMetaData)#16 (5) { ["tableSchema"]=> object(CMysqlTableSchema)#20 (9) { ["schemaName"]=> NULL ["name"]=> string(8) "tbl_user" ["rawName"]=> string(10) "`tbl_user`" ["primaryKey"]=> string(2) "id" ["sequenceName"]=> string(0) "" ["foreignKeys"]=> array(0) { } ["columns"]=> array(4) { ["id"]=> object(CMysqlColumnSchema)#21 (14) { ["name"]=> string(2) "id" ["rawName"]=> string(4) "`id`" ["allowNull"]=> bool(false) ["dbType"]=> string(7) "int(11)" ["type"]=> string(7) "integer" ["defaultValue"]=> NULL ["size"]=> int(11) ["precision"]=> int(11) ["scale"]=> NULL ["isPrimaryKey"]=> bool(true) ["isForeignKey"]=> bool(false) ["autoIncrement"]=> bool(true) ["_e":"CComponent":private]=> NULL ["_m":"CComponent":private]=> NULL } ["username"]=> object(CMysqlColumnSchema)#22 (14) { ["name"]=> string(8) "username" ["ra
5.使用:插入一行数据。
- <?php
- class DefaultController extends Controller
- {
- public function actionIndex ()
- {
- $model = new TblUser();
- $model->username = "admin";
- $model->password = "123456";
- $model->email = "admin@admin.com";
- var_dump($model->save());
- exit();
- $this->render("index");
- }
- }
http://www.localyii.com/testwebap/index.php/testmod/default/index
如果打印的是bool(true),查看一下数据库可以看到,我们的数据已经存入到数据库中。
这里TblUser中虽然没有明确的指定数据表的列的名称,但是我们还是可以直接使用。这都是yii悄悄为我们做的。这样的操作岂不是很爽。还有TblUser是继承的CActiveRecord,也继承与CModel当然也是CComponent,所以他们的方法我们都可以使用。修改,删除,查找,比较,你想要的函数应有尽有,这里不再详细做讲解,自己慢慢分析代码,才能更好的掌握这些方法的使用。
下的时间交给官方文档,看看官方文档讲CActiveRecord的常见方法。
具体网址是:http://www.yiiframework.com/doc/guide/1.1/zh_cn/database.ar
//详细内容如下://///////////////
虽然 Yii DAO 可以处理几乎任何数据库相关的任务, 但很可能我们会花费 90% 的时间以编写一些执行普通 CRUD(create, read, update 和 delete)操作的 SQL 语句。 而且我们的代码中混杂了SQL语句时也会变得难以维护。要解决这些问题,我们可以使用 Active Record。
Active Record (AR) 是一个流行的 对象-关系映射 (ORM) 技术。 每个 AR 类代表一个数据表(或视图),数据表(或视图)的列在 AR 类中体现为类的属性,一个 AR 实例则表示表中的一行。 常见的 CRUD 操作作为 AR 的方法实现。因此,我们可以以一种更加面向对象的方式访问数据。 例如,我们可以使用以下代码向 tbl_post
表中插入一个新行。
$post=new Post; $post->title="sample post"; $post->content="post body content"; $post->save();
下面我们讲解怎样设置 AR 并通过它执行 CRUD 操作。我们将在下一节中展示怎样使用 AR 处理数据库关系。 为简单起见,我们使用下面的数据表作为此节中的例子。注意,如果你使用 MySQL 数据库,你应该将下面的 SQL 中的 AUTOINCREMENT
替换为 AUTO_INCREMENT
。
CREATE TABLE tbl_post ( id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, title VARCHAR(128) NOT NULL, content TEXT NOT NULL, create_time INTEGER NOT NULL );
注意: AR 并非要解决所有数据库相关的任务。它的最佳应用是模型化数据表为 PHP 结构和执行不包含复杂 SQL 语句的查询。 对于复杂查询的场景,应使用 Yii DAO。
1. 建立数据库连接
AR 依靠一个数据库连接以执行数据库相关的操作。默认情况下, 它假定 db
应用组件提供了所需的CDbConnection 数据库连接实例。如下应用配置提供了一个例子:
return array( "components"=>array( "db"=>array( "class"=>"system.db.CDbConnection", "connectionString"=>"sqlite:path/to/dbfile", // 开启表结构缓存(schema caching)提高性能 // "schemaCachingDuration"=>3600, ), ), );
提示: 由于 Active Record 依靠表的元数据(metadata)测定列的信息,读取元数据并解析需要时间。 如果你数据库的表结构很少改动,你应该通过配置 CDbConnection::schemaCachingDuration 属性的值为一个大于零的值开启表结构缓存。
对 AR 的支持受 DBMS 的限制,当前只支持下列几种 DBMS:
- MySQL 4.1 或更高版本
- PostgreSQL 7.3 或更高版本
- SQLite 2 和 3
- Microsoft SQL Server 2000 或更高版本
- Oracle
注意: 1.0.4 版开始支持 Microsoft SQL Server;1.0.5 版开始支持 Oracle。
如果你想使用一个不是 db
的应用组件,或者如果你想使用
AR 处理多个数据库,你应该覆盖CActiveRecord::getDbConnection()。 CActiveRecord 类是所有
AR 类的基类。
提示: 通过 AR 使用多个数据库有两种方式。如果数据库的结构不同,你可以创建不同的 AR 基类实现不同的 getDbConnection()。否则,动态改变静态变量 CActiveRecord::db 是一个好主意。
2. 定义 AR 类
要访问一个数据表,我们首先需要通过集成 CActiveRecord 定义一个
AR 类。 每个 AR 类代表一个单独的数据表,一个 AR 实例则代表那个表中的一行。 如下例子演示了代表 tbl_post
表的
AR 类的最简代码:
class Post extends CActiveRecord { public static function model($className=__CLASS__) { return parent::model($className); } public function tableName() { return "tbl_post"; } }
提示: 由于 AR 类经常在多处被引用,我们可以导入包含 AR 类的整个目录,而不是一个个导入。 例如,如果我们所有的 AR 类文件都在
protected/models
目录中,我们可以配置应用如下:return array( "import"=>array( "application.models.*", ), );
默认情况下,AR 类的名字和数据表的名字相同。如果不同,请覆盖 tableName() 方法。 model() 方法为每个 AR 类声明为如此(稍后解释)。
信息: 要使用 1.1.0 版本中引入的 表前缀功能 AR 类的 tableName() 方法可以通过如下方式覆盖
public function tableName() { return "{{post}}"; }这就是说,我们将返回通过双大括号括起来的没有前缀的表名,而不是完整的表的名字。
数据表行中列的值可以作为相应 AR 实例的属性访问。例如,如下代码设置了 title
列
(属性):
$post=new Post; $post->title="a sample post";
虽然我们从未在 Post
类中显式定义属性 title
,我们还是可以通过上述代码访问。
这是因为 title
是tbl_post
表中的一个列,CActiveRecord
通过PHP的 __get()
魔术方法使其成为一个可访问的属性。
如果我们尝试以同样的方式访问一个不存在的列,将会抛出一个异常。
信息: 此指南中,我们在表名和列名中均使用了小写字母。 这是因为不同的 DBMS 处理大小写的方式不同。 例如,PostgreSQL 默认情况下对列的名字大小写不敏感,而且我们必须在一个查询条件中用引号将大小写混合的列名引起来。 使用小写字母可以帮助我们避免此问题。
AR 依靠表中良好定义的主键。如果一个表没有主键,则必须在相应的 AR 类中通过如下方式覆盖 primaryKey()
方法指定哪一列或哪几列作为主键。
public function primaryKey() { return "id"; // 对于复合主键,要返回一个类似如下的数组 // return array("pk1", "pk2"); }
3. 创建记录
要向数据表中插入新行,我们要创建一个相应 AR 类的实例,设置其与表的列相关的属性,然后调用 save() 方法完成插入:
$post=new Post; $post->title="sample post"; $post->content="content for the sample post"; $post->create_time=time(); $post->save();
如果表的主键是自增的,在插入完成后,AR 实例将包含一个更新的主键。在上面的例子中, id
属性将反映出新插入帖子的主键值,即使我们从未显式地改变它。
如果一个列在表结构中使用了静态默认值(例如一个字符串,一个数字)定义。则 AR 实例中相应的属性将在此实例创建时自动含有此默认值。改变此默认值的一个方式就是在 AR 类中显示定义此属性:
class Post extends CActiveRecord { public $title="please enter a title"; ...... } $post=new Post; echo $post->title; // 这儿将显示: please enter a title
从版本 1.0.2 起,记录在保存(插入或更新)到数据库之前,其属性可以赋值为 CDbExpression 类型。
例如,为保存一个由 MySQL 的 NOW()
函数返回的时间戳,我们可以使用如下代码:
$post=new Post; $post->create_time=new CDbExpression("NOW()"); // $post->create_time="NOW()"; 不会起作用,因为 // "NOW()" 将会被作为一个字符串处理。 $post->save();
提示: 由于 AR 允许我们无需写一大堆 SQL 语句就能执行数据库操作, 我们经常会想知道 AR 在背后到底执行了什么 SQL 语句。这可以通过开启 Yii 的 日志功能 实现。例如,我们在应用配置中开启了CWebLogRoute ,我们将会在每个网页的最后看到执行过的 SQL 语句。 从 1.0.5 版本起,我们可以在应用配置中设置 CDbConnection::enableParamLogging 为 true ,这样绑定在 SQL 语句中的参数值也会被记录。
4. 读取记录
要读取数据表中的数据,我们可以通过如下方式调用 find
系列方法中的一种:
// 查找满足指定条件的结果中的第一行 $post=Post::model()->find($condition,$params); // 查找具有指定主键值的那一行 $post=Post::model()->findByPk($postID,$condition,$params); // 查找具有指定属性值的行 $post=Post::model()->findByAttributes($attributes,$condition,$params); // 通过指定的 SQL 语句查找结果中的第一行 $post=Post::model()->findBySql($sql,$params);
如上所示,我们通过 Post::model()
调用 find
方法。
请记住,静态方法 model()
是每个
AR 类所必须的。 此方法返回在对象上下文中的一个用于访问类级别方法(类似于静态类方法的东西)的 AR 实例。
如果 find
方法找到了一个满足查询条件的行,它将返回一个 Post
实例,实例的属性含有数据表行中相应列的值。
然后我们就可以像读取普通对象的属性那样读取载入的值,例如 echo
$post->title;
。
如果使用给定的查询条件在数据库中没有找到任何东西, find
方法将返回
null 。
调用 find
时,我们使用 $condition
和 $params
指定查询条件。此处 $condition
可以是
SQL 语句中的WHERE
字符串,$params
则是一个参数数组,其中的值应绑定到 $condation
中的占位符。例如:
// 查找 postID=10 的那一行 $post=Post::model()->find("postID=:postID", array(":postID"=>10));
注意: 在上面的例子中,我们可能需要在特定的 DBMS 中将
postID
列的引用进行转义。 例如,如果我们使用 PostgreSQL,我们必须将此表达式写为"postID"=:postID
,因为 PostgreSQL 在默认情况下对列名大小写不敏感。
我们也可以使用 $condition
指定更复杂的查询条件。
不使用字符串,我们可以让 $condition
成为一个CDbCriteria 的实例,它允许我们指定不限于 WHERE
的条件。
例如:
$criteria=new CDbCriteria; $criteria->select="title"; // 只选择 "title" 列 $criteria->condition="postID=:postI