Active Record基础使用
Active Record (AR) 是一个流行的 对象-关系映射 (ORM) 技术。
每个 AR 类代表一个数据表(或视图),数据表(或视图)的列在 AR 类中体现为类的属性,
一个 AR 实例则表示表中的一行。
常见的 CRUD 操作作为 AR 的方法实现。因此,我们可以以一种更加面向对象(面向AR对象)的方式访问数据。 例如,我们可以使用以下代码向 tbl_post
表中插入一个新行。
$post = new Post;
$post->title = 'sample post';
$post->content = 'post body content';
$post->save();
使用下面的语句来创建mysql
tbl_post
表
CREATE TABLE tbl_post (
id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
title VARCHAR(128) NOT NULL,
content TEXT NOT NULL,
create_time INTEGER NOT NULL
);
定义主键
AR 依靠表中良好定义的主键。如果一个表没有主键,则必须在相应的 AR 类中通过如下方式覆盖 primaryKey() 方法指定哪一列或哪几列作为主键。
public function primaryKey()
{
return 'id';
// 对于复合主键,要返回一个类似如下的数组
// return array('pk1', 'pk2');
}
如果表的主键是自增的,在插入完成后,AR 实例将包含一个更新的主键。在一开始的例子中, $post->id
属性将反映出新插入帖子的主键值,即使我们从未显式地改变它。
修改默认值
如果一个列在表结构中使用了静态默认值(例如一个字符串,一个数字)定义。则 AR 实例中相应的属性将在此实例创建时自动含有此默认值。改变此默认值的一个方式就是在 AR 类中显示定义此属性:
class Post extends CActiveRecord
{
public $title = 'please enter a title';
......
}
创建记录
记录在保存(插入或更新)到数据库之前,其属性可以赋值为 CDbExpression 类型(mysql表达式)。 例如,为保存一个由 MySQL 的 NOW() 函数返回的时间戳,我们可以使用如下代码:
$post = new Post;
$post->create_time = new CDbExpression('NOW()');
// $post->create_time='NOW()'; 不会起作用,因为
// 'NOW()' 将会被作为一个字符串处理。
$post->save();
查询数据
find方法
find
方法定义
public function find($condition = '', $params = array())
参数find
方法有两个参数, 第一个参数为查询条件
, 第二个参数为绑定参数
其中第一个参数可以为以下值
string
: 当为字符串时, 直接写查询条件, 如id=1 and status=:status
, 然后使用第二个参数来进行参数绑定CDbCriteria
类的实例, 创建一个CDbCriteria
的实例, 直接设置该类的属性, 一般使用这种方式时不需要在第二个参数进行参数绑定了, 因为可以在该实例中设置params
属性直接绑定参数
,需要注意的是, 该类没有where
属性, 但是有一个condition
属性, 不过这个condition
属性只支持赋值字符串, 不支持数组, 更多方法请查看: https://www.yiichina.com/doc/api/1.1/CDbCriteriaarray
, 当为数组时, 一种替代CDbCriteria
的方法, 不需要我们主动去实例化CDbCriteria
,数组的键和值各自对应标准(criterion)的属性名和值
返回值find
方法返回一个对应model的实例, 如果没有查询到数据, 返回null
使用示例
// 使用string $condition
$post = Post::model()->find('id=1');
$post = Post::model()->find('id=:id', [':id'=>1,]);
$post = Post::model()->find('id=:id and title like :title', [':id'=>1, ':title' => '%t']);
// 使用 CDbCriteria $condition
$criteria = new CDbCriteria;
$criteria->select = 'title'; // 只选择 'title' 列
$criteria->alias = 'p'; // 表的别名
$criteria->condition = 'id=:id';
//$criteria->join = ''; // join 字符串, 如: `LEFT JOIN users ON users.id = authorID`
$criteria->params = [':id' => 1]; // 参数可以直接在这里绑定
$criteria->order = 'id desc';
$criteria->limit = 1;
$criteria->offset = 1;
$post = Post::model()->find($criteria);
// 使用数组
$post = Post::model()->find([
'select' => 'title',
'limit' => 1,
'offset' => 1,
'condition' => 'id=:id',
'params' => [':id' => 2],
]);
find系列方法会自动增加 limit 1, 所以即使我们设置的 limit 参数, 也是没有用的
findByAttributes方法
public function findByAttributes($attributes,$condition='',$params=array())
该方法和find
方法的不同之处在于可以优先根据字段查询数据, 区别在于第一个参数attributes
, 该参数需要一个数组[name=>value]
的方式, 如果value
是一个变量, 会自动绑定参数, 如果value是一个数组, 则会使用in
查询, 后面的两个参数用法和find
一样, 如:
$id = 1;
$post = Post::model()->findByAttributes(['id' => $id]);
// 当$id为数组时, 使用in查询
$id = [1, 2, 3];
$post = Post::model()->findByAttributes(['id' => $id]); // SELECT * FROM `tbl_post` `t` WHERE `t`.`id` IN (1, 2, 3) LIMIT 1
如果要使用追加条件的话, 传入后面两个参数就可以了
findByPk方法
public function findByPk($pkValue,$condition='',$params=array())
该方法很简单, 传入主键的值即可, 会自动转换成int型, 如果值是个数组的话, 会使用in
查询, 不过只有当数组的长度大于1时, 才会使用in
查询, 如果只有一个值的话, 还是会使用 =
用法如下:
$post = Post::model()->findByPk(10); // where `id` = 10;
$post = Post::model()->findByPk([10]); // where `id` = 10;
$post = Post::model()->findByPk([1, 2, 3]); // where `id` IN (1, 2, 3)
$post = Post::model()->findByPk(1, 'title=:title', [':title' => 't']); // WHERE `id`=1 AND `title`='t'
findBySql
public function findBySql($sql, $params=array())
用法很简单, 第一个为完整的sql语句, 第二个参数为可绑定的参数, 如:
$post = Post::model()->findBySql('select * from {{post}} where id = :id', [':id' => 1]);
findAll系列
有下面几种方法, 用法和find()
系列方法相同, 只是返回多条数据, 同时, 如果没有查询到数据, 返回一个空数组而不是null
findAll()
findAllByAttributes()
findAllByPk()
findAllBySql()
count系列
有下面几种方法, 用法和find()
系列方法相同, 返回string
型的数字结果, 如果没有统计到数据, 返回 string
'0'
count()
countByAttributes()
countBySql()
findAllBySql()
批量更新
以下这些方法都返回受影响的行数
// 调用格式
// 更新符合指定条件的行
Post::model()->updateAll($attributes,$condition,$params);
// 更新符合指定条件和主键的行
Post::model()->updateByPk($pk,$attributes,$condition,$params);
// 更新满足指定条件的行的计数列
Post::model()->updateCounters($counters,$condition,$params);
// 使用实例
// 使用 db 表达式
Post::model()->updateAll(['hits' => new CDbExpression('hits + 1')]);
Post::model()->updateAll(['title' => 'new title'], 'id=1');
Post::model()->updateByPk(10, ['title' => 'new title']);
// 更新计数列, $counters为一个数组, name=>(int)value, value >=0 执行增加操作
// value < 0 执行减法操作
// 只能更新计数的列,不能更新其他的列, 比如title=>new title, 这是不被允许的
Post::model()->updateCounters(['hits' => -1]); // set hits = hits - 1
Post::model()->updateCounters(['hits' => 2]); // set hits = hits + 2
批量删除
// 删除符合指定条件的行
Post::model()->deleteAll($condition, $params);
// 删除符合指定条件和主键的行
Post::model()->deleteByPk($pk, $condition, $params);
参数, $pk
, $condition
, $params
同上面说过的一样
使用 AR 处理事务
$model=Post::model();
$transaction=$model->dbConnection->beginTransaction();
try {
// 查找和保存是可能由另一个请求干预的两个步骤
// 这样我们使用一个事务以确保其一致性和完整性
$post=$model->findByPk(10);
$post->title='new post title';
$post->save();
$transaction->commit();
}
catch(Exception $e) {
$transaction->rollBack();
}
命名范围 (查询场景)
命名范围主要是在 CActiveRecord::scopes() 方法中以名字-规则对的方式声明。 如下代码在 Post 模型类中声明了两个命名范围, published 和 recently。
class Post extends CActiveRecord
{
......
public function scopes()
{
return array(
'published'=>array(
'condition'=>'status=1',
),
'recently'=>array(
'order'=>'create_time DESC',
'limit'=>5,
),
);
}
}
只要定义了命名范围, 就可以直接当做方法调用, 因为yii在内部已经帮我们处理了, 会自动指向scopes[name]
每个命名范围声明为一个可用于初始化 CDbCriteria 实例的数组。 例如,recently 命名范围指定 order 属性为 create_time DESC , limit 属性为 5。他们翻译为查询规则后就会返回最近的5篇帖子。
命名范围多用作 find 方法调用的修改器。 几个命名范围可以链到一起形成一个更有约束性的查询结果集。例如, 要找到最近发布的帖子, 我们可以使用如下代码:
$posts = Post::model()->published()->recently()->findAll();
为命名范围传递参数
需要传递参数的时候, 就需要定义一个和命名范围相同的方法, 方法中获取查询实例添加查询条件, 如:
public function recently($limit=5)
{
$this->getDbCriteria()->mergeWith(array(
'order'=>'create_time DESC',
'limit'=>$limit,
));
return $this;
}
这样就可以使用下面的语句进行查询了
$posts=Post::model()->recently(3)->findAll();
定义默认的查询条件
我们可以在模型中直接覆盖CActiveRecord::defaultScope
方法
class Content extends CActiveRecord
{
public function defaultScope()
{
return array(
'condition' => "status=1",
);
}
}
现在,如果下面的方法被调用,将会自动使用上面定义的查询规则:
$contents=Content::model()->findAll();
当调用 find
系列的方法和count
系列的方法时,defaultScope
都会生效, 即使在$condition
中重新赋值了condition
当想取消defaultScope
效果时, 可以在find方法前调用一次 resetScope
方法, 如
$post = Post::model()->resetScope()->count(['condition'=>'id=1']);
注意,默认的命名范围只会应用于 SELECT 查询。INSERT, UPDATE 和 DELETE 查询将被忽略。