企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
[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(); ~~~