[TOC]
# 获取一对多关联表的数据
AR模型提供了一种面向对象的操作方法让我们获取关联表,这回我拿用户和发表文章来说说
如果在Yii里查用户发表的文章,这样的代码是很菜的行为,我并不建议这样写:
~~~php
$user = User::findOne($userId);
$articles = Article::findAll(['user_id' => $user->id]);
foreach($articles as $article){
echo $article->title . '<br/>';
}
~~~
# 通过getter定义关系
基于本文章的例子,官方推荐我们在`User`模型里建立一个getter方法来获取所有文章(在[Object 基类的特性](http://www.kkh86.com/it/yii2/guide-base-object.html)里有介绍过getter嘛,怕你忘了我提醒一下)
这个getter是这样写的:
~~~php
public function getArticles(){
$关联表的类名 = Article::className();
$关联表的字段 = 'user_id';
$本表的字段 = 'id';
return $this->hasMany($关联表的类名, [$关联表的字段 => $本表的字段]);
}
~~~
这样的话就可以这样获取用户的所有文章了:
~~~php
$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部分`才可以
# 跨数据库关系声明
这个会很少用吧,但比较容易明白所以顺便讲讲
我们目前讲到的AR模型默认就是指[yii\\db\\ActiveRecord](http://www.yiichina.com/doc/api/2.0/yii-db-activerecord),这是基于主流SQL关系数据库层面上定义的AR模型,其实还有一种AR模型就是[yii\\mongodb\\ActiveRecord](http://www.yiiframework.com/doc-2.0/yii-mongodb-activerecord.html)(需要安装[yii-mongodb扩展](http://www.yiiframework.com/doc-2.0/ext-mongodb-index.html)),下面代码中的两个模型可以实现互相定义关系获取对方的数据(基于官方示例代码稍作了修改):
~~~php
//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也不是很熟悉
# 查询指定字段时的with注意事项
~~~php
$orders = Order::find()->select(['id', 'amount'])->with('customer')->all();
echo $orders[0]->customer; // 空的
~~~
不是with一起查出来了吗?为什么是空的呢?
因为with的附加查询里需要关联字段才可以,但是select指定的id和amount两个字段都不是与customer表发生关联的字段,所以在底层进行with附加的查询时,根本找不到关联字段,所以没查到相关数据
所以这里要注意的是,select要附加上关联字段,以下才是正确姿势:
~~~php
$orders = Order::find()->select(['id', 'amount', 'customer_id'])->with('customer')->all();
~~~
那如果突然想要在in的同时又加上其它条件,就要在with里换一下参数的写法了:
~~~php
// SELECT * FROM customer LIMIT 100;
// SELECT * FROM orders WHERE customer_id IN (1,2,...) AND status = 1
$customers = Customer::find()->limit(100)->with([
'orders' => function($arQuery){
//传来的参数是一个AR查询器,通过查询器提供的API来调整with查询的条件,比如andWhere
$query->andWhere(['status' => 1]);
}
])->all();
~~~
- 目录
- 配置
- 简介
- 别名
- gii
- 配置项
- 模型
- 简介
- 增删改查
- AR和model
- 模型事件
- 场景
- query查询
- 增删改
- AR查询器
- 模型关系定义
- AR模型连表查询
- fields
- where拼接
- 模块
- 创建模块
- 控制器
- 表单
- 跳转
- 响应
- 验证器
- Action
- 组件
- url
- 分页
- 验证码
- 缓存
- 文件上传
- 预启动组件
- 事件
- 自定义组件
- redis
- 日志
- 行为
- cookie和session
- 基础知识
- 创建一个类
- 配置一个类
- object基类
- component组件类特性
- phpstorm无法更改php等级
- url地址美化
- 过滤器
- 请求处理
- 请求组件
- 响应组件
- header
- 用户登录
- 实现IdentityInterface接口
- 登录
- 自动检测登录
- 获取用户信息
- 访问行为追踪
- phpstorm+postman断点调试