Also available in these languages:
DeutschEnglishEspañolFrançaisעִבְרִיתBahasa Indonesia日本語polskiPortuguêsRomâniaРусскийsvenska简体中文

Active Record

虽然Yii DAO可以处理几乎任何数据库相关的任务,但很可能我们会花费90%的时间 用来书一些通用的SQL语句来执行CRUD操作(创建,读取,更新和删除)。 同时我们也很难维护这些PHP和SQL语句混合的代码。要解决这些问题,我们可以使用Active Record。

Active Record(AR)是一种流行的对象关系映射(ORM)技术。每个AR代表一个数据表(或视图), 其字段作为AR的属性,一个AR实例代表在表中的一行。常见的CRUD操作对应AR中的成员函数。 于是,我们可以使用更面向对象的方法处理我们的数据。例如,我们可以使用下面的代码在Post表中插入一个新行:

$post=new Post;
$post->title='sample post';
$post->content='post body content';
$post->save();

在下面我们将介绍如何设置AR和用它来执行CRUD操作。在下一小节我们将展示如何使用AR 处理数据库中的关系。为了简单起见,我们使用本节下面的数据库表作为例子。 请注意,如果你使用MySQL数据库,在下面的SQL中您应该替换AUTOINCREMENTAUTO_INCREMENT

CREATE TABLE Post (
    id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
    title VARCHAR(128) NOT NULL,
    content TEXT NOT NULL,
    createTime INTEGER NOT NULL
);

注意: AR不是要解决所有与数据库相关的任务。在SQL不包含复杂的语法的情况下, 用在在PHP代码中对数据库建模。Yii DAO应该用于上述的复杂情况下。

建立数据库连接

AR需要一个数据库连接以执行数据库相关的操作。默认情况下,应用中的db组件提供了 CDbConnection实例作为我们需要的数据库连接服务。下面的应用程序配置提供了一个例子:

return array(
    'components'=>array(
        'db'=>array(
            'class'=>'system.db.CDbConnection',
            'connectionString'=>'sqlite:path/to/dbfile',
            // turn on schema caching to improve performance
            // 'schemaCachingDuration'=>3600,
        ),
    ),
);

提示: 由于Active Record需要表的元数据来确定数据的列信息, 这需要时间来读取和分析元数据。如果您的数据库结构是比较固定的,你应该打开缓存。 打开方法是配置CDbConnection: :schemaCachingDuration属性为一个大于0的值。

AR的支持受限于数据库管理系统。目前,只有以下数据库管理系统支持:

注意: Microsoft SQL Server自1.0.4版本提供支持;而甲骨文自1.0.5版本即提供支持。

如果你想使用其他组件而不是db,你应该重写CActiveRecord::getDbConnection()CActiveRecord类是所有AR类的基类。

提示: 有两种方法可以在AR模式下使用多种数据库系统。如果数据库的模式不同, 您可以以不同的方式实现getDbConnection()。否则,动态改变静态变量CActiveRecord::DB 是一个更好的主意。

Defining AR Class

为了使用一个数据表,我们首先需要集成CActiveRecord来定义一个AR类。 每个AR类代表一个数据库表,每个AR实例代表数据表中的一行。下面的代码介绍了 要创建一个对应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类中重新声明(这个稍后会解释)。

Info: 为了使用1.1.0版本加入的新功能表前缀,你需要使用重写AR类中的 tableName()方法。示例如下:

public function tableName()
{
    return '{{post}}';
}

就这样,不再直接写完整的表名,而是返回一个用双花括号包围的表名称。

在AR类中,数据表的字段可以像类的属性那样使用。举个例子,下面的代码可以设置title字段(属性):

$post=new Post;
$post->title='一个示例文章';

尽管我们没有明确地在Post类中添加title属性,但是我们仍旧可以通过上面的代码来访问它。 这是因为titletbl_post表中的一个字段,CActiveRecord可以通过使用__get()魔术方法来让它可以直接被使用。 如果你要访问一个并不存在的字段的时候,Yii会抛出一个异常。

信息: 在这个手册中,我们使用小写的表名和字段名。这是因为不同的数据库对于大小写的敏感不同。 比如说,PostgreSQL对字段名的大小写就敏感,并且如果同时含有大小写,我们还必须在查询语句中将字段名括起来。 使用全小写可以避免这些问题。

AR需要我们明确指定表的主键字段。如果一个表没有设置主键,就需要相应的AR类重写primaryKey()来指定一个或者一组可以视为主键的字段。

public function primaryKey()
{
    return 'id';
    // 对于包含多个字段的一组主键,可以使用数组方式返回,比如
    // return array('pk1', 'pk2');
}

创建数据记录

为了在数据库中插入一行新的数据,我们可以创建一个相应AR类的实例。然后设置这个对象的属性,就可以设置相应的数据表的字段。 最后使用save()来保存数据到数据库中。

$post=new Post;
$post->title='示例文章';
$post->content='文章的正文';
$post->create_time=time();
$post->save();

如果数据表的主键字段是自增的,在保存之后会自动将主键的值更新到对象中。在上面的代码中, id属性会指向这个新文章的主键的值,尽管我们一般不会修改这个。

如果一个字段在数据表中定义了默认值(比如一个字符串或者一个数字)。那么AR类在实例化的同时 会自动将相应字段设置为这个默认值。如果你想自己设定另外的默认值,你可以通过显式定义类属性的方式实现。

class Post extends CActiveRecord
{
    public $title='请输入标题';
    ......
}
 
$post=new Post;
echo $post->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的Log-Features 来达到我们的目的。据个例子,我们可以在应用的配置文件中打开CWebLogRoute,然后我们就可以 在页脚看到被执行过的SQL语句。在1.0.5版本起,我们还可以设置CDbConnection::enableParamLogging 为true来记录SQL语句中绑定的参数。

If you find any typos or errors in the tutorial, please create a Yii ticket to report it. If it is a translation error, please create a Yiidoc ticket, instead. Thank you.

Your Comment:

You may enter comment using Markdown syntax.

Please login with your forum account.
Note: you must have at least ONE forum post with your account.