徐善通的随笔

千里之行, 始于足下



yii1.1活动记录ActiveRecord使用方法总结


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/CDbCriteria
  • array, 当为数组时, 一种替代 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 查询将被忽略。


作者: 徐善通
地址: https://www.xstnet.com/article-113.html
声明: 除非本文有注明出处,否则转载请注明本文地址


我有话说



最新回复


正在加载中....

Copyrights © 2016-2019 醉丶春风 , All rights reserved. 皖ICP备15015582号-1