Yii2中使用join、joinwith多表关联查询


Posted in PHP onJune 30, 2016

表结构

现在有客户表、订单表、图书表、作者表,

客户表Customer (id customer_name)
订单表Order (id order_name customer_id book_id)
图书表 (id book_name author_id)
作者表 (id author_name)

模型定义

下面是这4个个模型的定义,只写出其中的关联

Customer

class Customer extends \yii\db\ActiveRecord
{
// 这是获取客户的订单,由上面我们知道这个是一对多的关联,一个客户有多个订单
public function getOrders()
{
// 第一个参数为要关联的子表模型类名,
// 第二个参数指定 通过子表的customer_id,关联主表的id字段
return $this->hasMany(Order::className(), ['customer_id' => 'id']);
}
}

Order

class Order extends \yii\db\ActiveRecord
{
// 获取订单所属用户
public function getCustomer()
{
//同样第一个参数指定关联的子表模型类名
//
return $this->hasOne(Customer::className(), ['id' => 'customer_id']);
}
// 获取订单中所有图书
public function getBooks()
{
//同样第一个参数指定关联的子表模型类名
//
return $this->hasMany(Book::className(), ['id' => 'book_id']);
}
}

Book

class Book extends \yii\db\ActiveRecord
{
// 获取图书的作者
public function getAuthor()
{
//同样第一个参数指定关联的子表模型类名
return $this->hasOne(Author::className(), ['id' => 'author_id']);
}
}

Author

class Autor extends \yii\db\ActiveRecord
{
}

hasMany、hasOne使用

Yii2中的表之间的关联有2种,它们用来指定两个模型之间的关联。

一对多:hasMany

一对一:hasOne

返回结果:这两个方法的返回结果都为yii\db\ActiveQuery对象

第一个参数:所关联的模型的类名称。

第二个参数:是一个数组,其中键为所关联的模型中的属性,值为当前模型中的属性。

关联的使用

现在我们获取一个客户的所有的订单信息

// 获取一个客户信息
$customer = Customer::findOne(1);
$orders = $customer->orders; // 通过在Customer中定义的关联方法(getOrders())来获取这个客户的所有的订单。

上面的两行代码会生成如下sql语句

SELECT * FROM customer WHERE id=1;
SELECT * FROM order WHERE customer_id=1;

关联结果缓存

如果客户的订单改变了,我们再重新调用

$orders = $customer->orders;

再次得到订单的时候你会发现没有变化。原因是只会在第一次执行$customer->orders的时候才会去数据库里面查询,然后会把结果缓存起来,以后查询的时候都不会再执行sql。

那么如果我想再次执行sql如何做呢?可以执行

unset($customer->orders);
$customer->orders;

然后就可以从数据库里面取数据了。

定义多个关联

同样,我们还可以在Customer里面定义多个关联。
如返回总数大于100的订单。

class Customer extends \yii\db\ActiveRecord
{
public function getBigOrders($threshold = 100)
{
return $this->hasMany(Order::className(), ['customer_id' => 'id'])
->where('subtotal > :threshold', [':threshold' => $threshold])
->orderBy('id');
}
}

关联的两种访问方式

如上面的,如果使用

$customer->bigOrders

将会得到大于100的所有的订单。如果要返回大于200的订单可以这样写

$orders = $customer->getBigOrders(200)->all();

从上面可以看出访问一个关联的时候有两种方法

如果以函数的方式调用,会返回一个 ActiveQuery 对象($customer->getOrders()->all())

如果以属性的方式调用,会直接返回模型的结果($customer->orders)

with的使用看如下代码,是取一个客户的订单

// 执行sql语句: SELECT * FROM customer WHERE id=1
$customer = Customer::findOne(1);
//执行sql:SELECT * FROM order WHERE customer_id=1
$orders1 = $customer->orders;
//这个不会执行sql,直接使用上面的缓存结果
$orders2 = $customer->orders;

如果现在我们要取出100个用户,然后访问每个用户的订单,从上面的了解我们可能会写出如下代码

// 执行sql语句: SELECT * FROM customer LIMIT 100
$customers = Customer::find()->limit(100)->all();
foreach ($customers as $customer) {
// 执行sql: SELECT * FROM order WHERE customer_id=...
$orders = $customer->orders;
// 处理订单。。。
}

然而,如果真要这样写的话,会在foreach的每个循环里面都执行一次sql去数据库里面查询数据。因为每个$customer对象都是不一样的。

为了解决上面的问题 可以使用 yii\db\ActiveQuery::with()。

其中width的参数为关系的名称,也就在model里面定义的getOrders,getCustomer中的orders和customer

// 先执行sql: SELECT * FROM customer LIMIT 100;
// SELECT * FROM orders WHERE customer_id IN (1,2,...)
$customers = Customer::find()->limit(100)
->with('orders')->all();
foreach ($customers as $customer) {
// 在这个循环的时候就不会再执行sql了
$orders = $customer->orders;
// ...handle $orders...
}

如果使用了select来指定返回的列,一定要确保返回的列里面包含所关联的模型的关联字段,否则将不会返回关联的表的Model

$orders = Order::find()->select(['id', 'amount'])->with('customer')->all();
// $orders[0]->customer 的结果将会是null
// 因为上面的select中没有返回所关联的模型(customer)中的指定的关联字段。
// 如果加上customer_id,$orders[0]->customer就可以返回正确的结果
$orders = Order::find()->select(['id', 'amount', 'customer_id'])->with('customer')->all();

给with加过滤条件

查询一个客户大于100的订单

//首先执行sql: SELECT * FROM customer WHERE id=1
$customer = Customer::findOne(1);
// 再执行查询订单的sql语句:SELECT * FROM order WHERE customer_id=1 AND subtotal>100
$orders = $customer->getOrders()->where('subtotal>100')->all();

查询100个客户的,每个客户的总合大于100的订单

// 下面的代码会执行sql语句: 
// SELECT * FROM customer LIMIT 100
// SELECT * FROM order WHERE customer_id IN (1,2,...) AND subtotal>100
$customers = Customer::find()->limit(100)->with([
'orders' => function($query) {
$query->andWhere('subtotal>100');
},
])->all();

在这里width的参数为数组,键为关联的名称,值为回调函数。

也就是说 对orders这个关联返回的ActiveQuery,再执行一次$query->andWhere('subtotal>100');

使用joinWith进行表关联

我们都知道可以用join on来写多个表之间的关联。先看看yii2中joinWit的声明

joinWith( $with, $eagerLoading = true, $joinType = 'LEFT JOIN' )

$with 数据类型为字符串或数组,如果为字符串,则为模型中定义的关联的名称(可以为子关联)。

如果为数组,键为model中以getXXX格式定义的关联,值为对这个关联的进一步的回调操作。

$eagerLoading 是否加载在$with中关联的模型的数据。

$joinType 联接类型,可用值为:LEFT JOIN、INNER JOIN,默认值为LEFT JOIN

// 订单表和客户表以Left join的方式关联。
// 查找所有订单,并以客户 ID 和订单 ID 排序
$orders = Order::find()->joinWith('customer')->orderBy('customer.id, order.id')->all();
// 订单表和客户表以Inner join的方式关联
// 查找所有的订单和书
$orders = Order::find()->innerJoinWith('books')->all();
// 使用inner join 连接order中的 books关联和customer关联。
// 并对custmer关联再次进行回调过滤:找出24小时内注册客户包含书籍的订单
$orders = Order::find()->innerJoinWith([
'books',
'customer' => function ($query) {
$query->where('customer.created_at > ' . (time() - 24 * 3600));
}
])->all();
// 使用left join连接 books关联,books关联再用left join 连接 author关联
$orders = Order::find()->joinWith('books.author')->all();

在实现上,Yii 先执行满足JOIN查询条件的SQL语句,把结果填充到主模型中, 然后再为每个关联执行一条查询语句, 并填充相应的关联模型。

// Order和books关联 inner join ,但不获取books关联对应的数据
$orders = Order::find()->innerJoinWith('books', false)->all();

On条件

在定义关联的时候还可以指定on条件

class User extends ActiveRecord
{
public function getBooks()
{
return $this->hasMany(Item::className(), ['owner_id' => 'id'])->onCondition(['category_id' => 1]);
}
}

在joinWith中使用

//先查询主模型(User)的数据, SELECT user.* FROM user LEFT JOIN item ON item.owner_id=user.id AND category_id=1
// 然后再根据关联条件查询相关模型数据SELECT * FROM item WHERE owner_id IN (...) AND category_id=1
// 这两个在查询的过程中都使用了 on条件。
$users = User::find()->joinWith('books')->all();

如果没有使用join操作,即使用的是with 或者 直接以属性来访问关联。这个时候on条件会作为where 条件。

// SELECT * FROM user WHERE id=10
$user = User::findOne(10);

总结

首先需要在模型中定义好关联(如getOrders中的Orders为一个关联)

然后在with或者joinWith中使用在模型中定义的关联。

其中在使用关联的时候还可以指定回调方法。

再有就是可以给关联、with、joinWith指定where或者on条件

这一部分其实非常多,也有点乱,有些功能没说说完,如三个表关联、逆关联等。

最基本的操作也就大体是这些。如果还有哪个地方想了解的,可以回帖交流。

以上所述是小编给大家介绍的Yii2中多表关联查询(join、joinwith)的相关知识,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

PHP 相关文章推荐
什么是MVC,好东西啊
May 03 PHP
swfupload 多文件上传实现代码
Aug 27 PHP
php设计模式之观察者模式的应用详解
May 21 PHP
PHP的preg_match匹配字符串长度问题解决方法
May 03 PHP
php用户注册时常用的检验函数实例总结
Dec 22 PHP
让你的PHP7更快之Hugepage用法分析
May 31 PHP
PHP Ajax实现无刷新附件上传
Aug 17 PHP
PHP经典算法集锦【经典收藏】
Sep 14 PHP
PHP堆栈调试操作简单示例
Jun 15 PHP
PHP中常用的三种设计模式详解【单例模式、工厂模式、观察者模式】
Jun 14 PHP
laravel框架学习笔记之组件化开发实现方法
Feb 01 PHP
Yii框架小部件(Widgets)用法实例详解
May 15 PHP
Yii2 ActiveRecord多表关联及多表关联搜索的实现
Jun 30 #PHP
eclipse php wamp配置教程
Jun 30 #PHP
PHP上传图片类显示缩略图功能
Jun 30 #PHP
PHP使用php-resque库配合Redis实现MQ消息队列的教程
Jun 29 #PHP
Thinkphp批量更新数据的方法汇总
Jun 29 #PHP
ThinkPHP实现更新数据实例详解(demo)
Jun 29 #PHP
php结合mysql与mysqli扩展处理事务的方法
Jun 29 #PHP
You might like
php操作(删除,提取,增加)zip文件方法详解
2015/03/12 PHP
详解PHP的Laravel框架中Eloquent对象关系映射使用
2016/02/26 PHP
Ajax提交表单时验证码自动验证 php后端验证码检测
2016/07/20 PHP
php获取数据库中数据的实现方法
2017/06/01 PHP
PHP基于pdo的数据库操作类【可支持mysql、sqlserver及oracle】
2018/05/21 PHP
PHP5.6读写excel表格文件操作示例
2019/02/26 PHP
侧栏跟随滚动的简单实现代码
2013/03/18 Javascript
js同比例缩放图片的小例子
2013/10/30 Javascript
javascript 小数取整简单实现方式
2014/05/30 Javascript
基于jQuery+PHP+Mysql实现在线拍照和在线浏览照片
2015/09/06 Javascript
js实现图片缓慢放大缩小效果
2016/08/02 Javascript
jQuery.ajax实现根据不同的Content-Type做出不同的响应
2016/11/03 Javascript
Vue.js计算属性computed与watch(5)
2016/12/09 Javascript
React创建组件的三种方式及其区别
2017/01/12 Javascript
解决js ajax同步请求造成浏览器假死的问题
2018/01/18 Javascript
vue中element-ui表格缩略图悬浮放大功能的实例代码
2018/06/26 Javascript
vue-router beforeEach跳转路由验证用户登录状态
2018/12/26 Javascript
jQuery实现文本显示一段时间后隐藏的方法分析
2019/06/20 jQuery
vue跳转同一个组件,参数不同,页面接收值只接收一次的解决方法
2019/11/05 Javascript
编写v-for循环的技巧汇总
2020/12/01 Javascript
python中星号变量的几种特殊用法
2016/09/07 Python
视觉直观感受若干常用排序算法
2017/04/13 Python
python通过socket实现多个连接并实现ssh功能详解
2017/11/08 Python
python中for用来遍历range函数的方法
2018/06/08 Python
Python面向对象之类的定义与继承用法示例
2019/01/14 Python
PyQT实现菜单中的复制,全选和清空的功能的方法
2019/06/17 Python
解决Keras 自定义层时遇到版本的问题
2020/06/16 Python
伦敦一家非常流行的时尚精品店:Oxygen Boutique
2017/01/15 全球购物
英国最大的自有市场,比亚马逊便宜:Flubit
2019/03/19 全球购物
国际商务系学生个人的自我评价
2013/11/26 职场文书
法学院方阵解说词
2014/01/29 职场文书
优秀小学生家长评语
2014/01/30 职场文书
优秀教师主要事迹
2014/02/01 职场文书
股东授权委托书范本
2014/09/13 职场文书
新兵入伍决心书
2015/09/22 职场文书
python树莓派通过队列实现进程交互的程序分析
2021/07/04 Python