原文链接:http://keshion.iteye.com/blog/1607994

一、多表关联的配置

在我们使用 AR 执行关联查询之前,我们需要让 AR 知道一个 AR 类是怎样关联到另一个的。

两个 AR 类之间的关系直接通过 AR 类所代表的数据表之间的关系相关联。 从数据库的角度来说,表 A 和 B 之间有三种关系:一对多(one-to-many,例如 tbl_user 和 tbl_post),一对一( one-to-one 例如 tbl_user 和 tbl_profile)和 多对多(many-to-many 例如 tbl_category 和 tbl_post)。 在 AR 中,有四种关系:

  • BELONGS_TO(属于): 如果表 A 和 B 之间的关系是一对多,则 表 B 属于 表 A (例如 Post 属于 User);

  • HAS_MANY(有多个): 如果表 A 和 B 之间的关系是一对多,则 A 有多个 B (例如 User 有多个 Post);

  • HAS_ONE(有一个): 这是 HAS_MANY 的一个特例,A 最多有一个 B (例如 User 最多有一个 Profile);

  • MANY_MANY: 这个对应于数据库中的 多对多 关系。 由于多数 DBMS 不直接支持 多对多 关系,因此需要有一个关联表将 多对多 关系分割为 一对多 关系。 在我们的示例数据结构中,tbl_post_category 就是用于此目的的。在 AR 术语中,我们可以解释MANY_MANY 为 BELONGS_TO 和 HAS_MANY 的组合。 例如,Post 属于多个(belongs to many) Category ,Category 有多个(has many) Post.

AR 中定义关系需要覆盖 CActiveRecord 中的 relations() 方法。此方法返回一个关系配置数组。每个数组元素通过如下格式表示一个单一的关系。

  1. 'VarName'=>array('RelationType', 'ClassName', 'ForeignKey', ...additional options)

其中 VarName 是关系的名字;RelationType 指定关系类型,可以是一下四个常量之一: self::BELONGS_TOself::HAS_ONE,self::HAS_MANY and self::MANY_MANYClassName 是此 AR 类所关联的 AR 类的名字; ForeignKey 指定关系中使用的外键(一个或多个)。

需要弄清楚的几点: 
(1),VarName指什么?  详见下面例2。 
(2),RelationType。一共有4种,分别为

  1. self::HAS_MANY, self::BELONGS_TO, self::MANY_MANY, self::HAS_ONE。

(3),ClassName。即关联的另一个../model/类名.php。 
(4),ForeignKey。谁是谁的外键? 
(5),附加条件

例1,一对多与多对一关系(post和user之间的关系)

1)models/Post.php

  1. class Post extends CActiveRecord
  2. {
  3. ......
  4. public function relations()
  5. {
  6. return array(
  7. 'author'=>array(self::BELONGS_TO, 'User', 'author_id'),
  8. );
  9. }
  10. }

其中Post与User的关系是BELONGS_TO(多对一)关系,并通过Post的author_id与User关联。 
Post中author_id是外键,关联到User中。 
注:此处的VarName是author,一个对象。

(2)models/User.php

  1. class User extends CActiveRecord
  2. {
  3. ......
  4. public function relations()
  5. {
  6. return array(
  7. 'posts'=>array(self::HAS_MANY, 'Post', 'author_id'),
  8. 'profile'=>array(self::HAS_ONE, 'Profile', 'owner_id'),
  9. );
  10. }
  11. }

对于User,与Post的关系是属于HAS_MANY(一对多)关系。并通过Post的author_id与Post关联。

例2,多对多关系 
在FailParts.php中

  1. 'Users' => array(self::MANY_MANY, 'User', 'fail_parts_user(fail_parts_id, user_id)'),

在User.php中

  1. 'FailParts' => array(self::MANY_MANY, 'FailParts', 'fail_parts_user(user_id, fail_parts_id)'),

由于两者是多对多关系,所以要用Users,而不是User;要用FailParts,而不是FailPart。

此处的Users和FailParts,即为前面的VarName。

例3,一对一关系 
比较简单,暂略。

2,关于VarName。 
对于类A.php,'VarName'=>array('RelationType', 'B', 'ForeignKey', ...additional options) 
其中VarName与B基本相同。但未必完全一样。此时就可以在A的views/A/xx.php中通过VarName来访问B及其属性值了。

如果是一对一:A->VarName 
如果是多对一:author_name = $post->Author->name; 
如果是一对多:$posts  =  $author->Post; 
如果是多对多:$posts  =  $author->Post;//本质是拆成一对多和多对一

  1. foreach($posts as $u){
  2. $_tmp_titles[] = $u -> title;
  3. }
  4. titleStr = implode(', ', $_tmp_titles);

二、多表关联的使用 
常常在controllers里 
1,延时加载 
(1)多对一 
$post = Post::model()->findByPk(10); 
$author = $post->author; 
批注:此处本质是一对一。

(2)一对多 
$user = User::model()->findByPk(10); 
$posts = $user->posts;

(3)多对多 
需要重点注意:两个id有先后关系。 
站在$repairInfo实例的角度,关联关系必须是

  1. 'FailParts' => array(self::MANY_MANY, 'FailParts', 'repair_mapping(repair_info_id,fail_parts_id)'),

而站在$failParts实例的角度,则关联关系变为

  1. 'RepairInfos' => array(self::MANY_MANY, 'RepairInfo', 'repair_mapping(fail_parts_id, repair_info_id)'),

而前面也已经指出,不需要双方都配置,只需需要的一方设置即可。

之前曾使用过的笨方法:

  1. /*方法一:使用表关系(多对多)*/
  2. $fails = $repairInfo->FailParts;//在$repairInfo中使用
  3. /*方法二:使用原始方法*/
  4. $id = $repairInfo->id;
  5. $maps = RepairMapping::model()->findAll("repair_info_id = $id");
  6. $f_ids = array();
  7. foreach($maps as $map){
  8. array_push($f_ids, $maps[0]->fail_parts_id);
  9. }
  10. $f_idsStr = implode(',',$f_ids);
  11. $fails = FailParts::model()->findAll("id IN ($f_idsStr)");

2,主动加载——with 
(1)一对多 
(2)多对多 
$posts = Post::model()->('author')->findAll();

例子: 
User.php

  1. //查询一个机房$idc_id的所有用户
  2. function getAdminedUsersByIdc($idc_id){
  3. $c = new CDbCriteria();
  4. $c->join = "JOIN idc_user on t.id=idc_user.user_id";
  5. $c->condition = "idc_user.idc_id=$idc_id";
  6. return User::model()->with('Idcs')->findAll($c);
  7. }
  8. //规则中配置
  9. 'Idcs' => array(self::MANY_MANY, 'Idc', 'idc_user(user_id, idc_id)'),

批注:没有with('Idcs'),执行后的结果也一样。只不过不再是eager loading。

三、带参数的关联配置

常见的条件有 
1,condition      按某个表的某个字段加过滤条件

例如:

  1. //在User的model里定义,如下关联关系
  2. 'doingOutsources' => array(self::MANY_MANY, 'Outsource', 'outsource_user(user_id, outsource_id)',
  3. 'condition' => "doingOutsources.status_id IN(" . Status::ASSIGNED . "," . Status::STARTED ."," . Status::REJECTED .")"),
  4. //结论:condition是array里指定model的一个字段。

显然,doingOutsources是真实数据表Outsource的别名,所以在condition中可以使用doingOutsources.status_id,当然也可以使用Outsource.status_id。另本表名user的默认别名是t。

2,order             按某个表的某个字段升序或降序

  1. //在RepairInfo的model里定义,如下关联关系
  2. 'WorkSheet'  => array(self::HAS_MANY, 'WorkSheet', 'repair_info_id', order => 'created_at desc'),
  3. //调用
  4. $worksheets = $repair_info->WorkSheet; //此时$worksheets是按降序排列
  5. //结论:order是array里指定model的一个字段。

with 
joinType 
select 
params 
on 
alias 
together 
group 
having 
index

还有用于lazy loading的 
limit        只取5个或10个 
offset 
through 
官方手册 
'posts'=>array(self::HAS_MANY, 'post', 'author_id',     'order'=>'posts.create_time DESC', 'with'=>'categories'),

四、静态查询(仅用于HAS_MANY和MANY_MANY) 
关键字:self:STAT 
1,基本用法。例如, 
class Post extends CActiveRecord 

    ......

public function relations() 
    { 
        return array( 
            'commentCount'=>array(self::STAT, 'Comment', 'post_id'), 
            'categoryCount'=>array(self::STAT,'Category','post_category(post_id, category_id)');

); 
    } 

2,静态查询也支持上面的各种条件查询 

  1. 'doingOutsourceCount' => array(self::STAT, 'Outsource', 'outsource_user(user_id, outsource_id)',
  2. 'condition' => "outsource.status_id IN(" . Status::ASSIGNED . "," . Status::STARTED ."," . Status::REJECTED .")"),

其他查询还包括 
condition  使用较多 
order 
select 
defaultValue 
params 
group 
having

3,静态查询的加载方式 
可以使用lazy loading方式 
$post->commentCount. 
也可以使用eager loading方式 
$posts = Post::model()->with('commentCount','categoryCount')->findAll(); 
注with中字符串一定是别名。

两者的性能比较: 
如果需要取所有post的所有comment,前者需要2N+1次查询,而后者只有一次。两者的选择视情况而定。

Yii关联查询(转载)的更多相关文章

  1. YII关联查询

    原文链接:http://keshion.iteye.com/blog/1607994 一.多表关联的配置 在我们使用 AR 执行关联查询之前,我们需要让 AR 知道一个 AR 类是怎样关联到另一个的. ...

  2. YII 关联查询

    通过外键自己关联自己

  3. yii 之数据库关联查询

    <?php namespace app\controllers; use yii\web\Controller; use app\models\Customer; class CustomerC ...

  4. Mybatis关联查询(转载)

    原文地址: http://www.cnblogs.com/xiaolang8762400/p/7399892.html   mybatis 提供了高级的关联查询功能,可以很方便地将数据库获取的结果集映 ...

  5. YII关联字段并带搜索排序功能

    1.简介 从接触yii框架到现在已经快有两个月了,但是自己对yii框架的了解程度并不是很深,并没有系统地去学习,仅仅只是在做项目的时候遇到不懂得知识才去翻手册. 在上一个项目中因为需要将关联的表的字段 ...

  6. Atitit.Hibernate中Criteria 使用总结and 关联查询 and 按照子对象查询 o9o

    Atitit.Hibernate中Criteria 使用总结and 关联查询 and 按照子对象查询 o9o 1. Criteria,,Criterion ,, 1 <2. 主要的对象黑头配置磊 ...

  7. Spring+SpringMVC+MyBatis深入学习及搭建(六)——MyBatis关联查询

    转载请注明出处:http://www.cnblogs.com/Joanna-Yan/p/6923464.html 前面有将到:Spring+SpringMVC+MyBatis深入学习及搭建(五)--动 ...

  8. MongoDB 集合间关联查询后通过$filter进行筛选

    在前面的分享中,有讲解 “详解MongoDB中的多表关联查询($lookup)” 一节,其内容涵盖了常见的集合管理的需求.我们知道文档的选择都是通过$match进行匹配刷选.但这是文档间的匹配筛选,并 ...

  9. JPQL的关联查询

    一般情况下,直接使用mysql语句写关联语句,是join on 的形式,如下: select * from tablea as a left join tableb as b on b.tablea_ ...

随机推荐

  1. Vue表单输入绑定

    <h3>基础用法</h3> <p>你可以用<strong>v-model</strong>指令在表单input,textarea以及sele ...

  2. 【mysql】【转发】[Err]1267 - Illegal mix of collations(utf8_general_ci,IMPLICIT) and (utf8_unicode_ci,I

    [Err]1267 - Illegal mix of collations(utf8_general_ci,IMPLICIT) and (utf8_unicode_ci,IMPLICIT) for o ...

  3. Ubuntu 16.04中安装谷歌Chrome浏览器

    1.进入 Ubuntu 16.04 桌面,按下 Ctrl + Alt + t 键盘组合键,启动终端. 2.在终端中,输入以下命令: sudo wget https://repo.fdzh.org/ch ...

  4. java做http接口

    问题描述 我要对外提供一个http接口给别人调用...但是我不知道用java怎么做这个接口.请大家详细给我讲讲.从开发到如何发布到服务器.谢谢了 解决方案 如果你这个很简单的话,而且数量也很少,建议直 ...

  5. leetcode-27-exercise_bit maniputation

    461. Hamming Distance 解题思路: 把两个数的每一位和1比较,如果结果不同说明这两位不同.要比较32次. int hammingDistance(int x, int y) { i ...

  6. LeetCode(117) Populating Next Right Pointers in Each Node II

    题目 Follow up for problem "Populating Next Right Pointers in Each Node". What if the given ...

  7. Linux交换分区swap

    一.SWAP 说明 1.1 SWAP 概述 当系统的物理内存不够用的时候,就需要将物理内存中的一部分空间释放出来,以供当前运行的程序使用.那些被释放的空间可能来自一些很长时间没有什么操作的程序,这些被 ...

  8. Linux学习-systemctl 针对 timer 的配置文件

    如何使用 systemd 内建的 time 来处理各种任务呢? systemd.timer 的优势 在 archlinux 的官网 wiki 上面有提到,为啥要使用 systemd.timer 呢? ...

  9. CF 219 D:Choosing Capital for Treeland(树形dp)

    D. Choosing Capital for Treeland 链接:http://codeforces.com/problemset/problem/219/D   The country Tre ...

  10. kruskal - 倍增 - 并查集 - Luogu 1967 货车运输

    P1967 货车运输 题目描述 A 国有 n 座城市,编号从 1 到 n,城市之间有 m 条双向道路.每一条道路对车辆都有重量限制,简称限重.现在有 q 辆货车在运输货物, 司机们想知道每辆车在不超过 ...