数据库+模型 - AR模型关系定义

  • 作者:KK

  • 发表日期:2016.12.22

  • 修改日期:2017.01.24 补充跨数据库的关系声明


获取一对多关联表的数据

AR模型提供了一种面向对象的操作方法让我们获取关联表,这回我拿用户和发表文章来说说

如果在Yii里查用户发表的文章,这样的代码是很菜的行为,我并不建议这样写:

$user = User::findOne($userId);
$articles = Article::findAll(['user_id' => $user->id]);
foreach($articles as $article){
	echo $article->title . '<br/>';
}

通过getter定义关系

基于本文章的例子,官方推荐我们在User模型里建立一个getter方法来获取所有文章(在Object 基类的特性里有介绍过getter嘛,怕你忘了我提醒一下)

这个getter是这样写的:

public function getArticles(){
	$关联表的类名表的类名::className();
	$关联表的字段表的字段'user_id';
	$本表的字段'id';
	return $this->hasMany($关联表的类名表的类名=> $本表的字段

这样的话就可以这样获取用户的所有文章了:

$user = User::findOne($userId); //查询一个用户的记录
foreach($user->articles as $article){ //扫描这个用户的文章
	echo $article->title . '<br/>';
}

这样的代码看上去是不是更加有可读性?至少让调用代码变得更短了

你可能会觉得这样有点装饰花哨,但根据一个用户的ID去查他的各种关联数据的场景是经常有的,如果定义了getter就能在调用时简写一点了,而且代码也非常有可读性,语义简练

我解析一下这个hasMany方法,它是用在一对多的两张关联表场景里的,用户表有id字段,article表有user_id字段来关联用户,每条user表的记录里的用户都可以在article表里有多条文章记录

最终hasMany方法返回的是一个AR查询器,当使用这个查询器时底层和Article::findAll一样也实现了获取所有文章

hasMany的第一个参数就是要关联的表名;第2个参数就是关联关系(关联数组),这个关系中,本表的字段要放在$value部分才可以


一对一关系

上手一对多关系表的模型查询后,咱们还要把一对一也给收拾一下其实已经很简单了,也是在Article模型里定义getUser这样的一个getter:

public function getUser(){
	//下面把hasMany换成hasOne hasOne hasOne 好了我说三遍了
	$本表的字段'user_id';
	$关联表的字段表的字段'id'; //注意下面,把关联表的字段放在key那里
	return $this->hasOne(User::className(), [$关联表的字段表的字段=> $本表的字段//这样获取文章所属的用户就方便了:
echo $article->user->name;

只要将之前学会的hasMany方法换成hasOne就行了,参数是一样的


延迟加载

$user = User::findOne(1);
$articles = $user->articles; //触发getArticles

其实此时还没有发生SELECT * FROM article WHERE user_id = $用户ID这样的查询,只是getArticles方法返回了一个AR查询器

当你真正要用起这个查询器相关的数据时,它才会查询给你,比如count($articles)的话它就会去查了,所以这叫延迟加载

那既然它还没查询,其实我们还能做我们喜欢的事情,由于它是一个AR查询器,而AR查询器又跟Query查询器类似,所以可以这样玩:

$user = User::findOne(1);
$articles = $user->articles->andWhere(['>', 'click', 5])->limit(10)->all();

通过andWhere方法追加条件,再加个limit 10篇文章最后才进行查询也可以


跨数据库关系声明

这个会很少用吧,但比较容易明白所以顺便讲讲

我们目前讲到的AR模型默认就是指yii\db\ActiveRecord,这是基于主流SQL关系数据库层面上定义的AR模型,其实还有一种AR模型就是yii\mongodb\ActiveRecord(需要安装yii-mongodb扩展),下面代码中的两个模型可以实现互相定义关系获取对方的数据(基于官方示例代码稍作了修改):

//Customer表对应的AR模型,假设在MySql里
class Customer extends \yii\db\ActiveRecord
{
    public static function tableName(){
        return 'customer';
    }

    public function getComments(){
        //声明与comment的一对多关系
        return $this->hasMany(Comment::className(), ['customer_id' => 'id']);
    }
}

// Comment是在mongoDb里的comment集合
class Comment extends \yii\mongodb\ActiveRecord //注意继承{
    public static function collectionName(){
        return 'comment';
    }

    public function getCustomer(){
        //声明与customer的一对一关系
        return $this->hasOne(Customer::className(), ['id' => 'customer_id']);
    }
}

$customer = Customer::findOne(1);
echo count($customer->comments); //输出评论数,底层会构造与mongoDb之间的查询处理

$comment = Comment::findOne(99);
echo $comment->customer->name; //输出客户名称

关于Yii里用mongoDb的AR模型,有需要的请去翻一下官方资料,我暂时未计划写这部分,至少我对mongoDb也不是很熟悉