直接举例:
现在我们需要查询一个文章列表,这个文章列表中需要包含文章所属的分类(多个)
通常我们的做法是在文章中加入一个分类ids的字段,以逗号形式隔开,这里不采用这种方法。
我们手里有两张表
1.文章(article):字段有id,name
2.分类(category):字段有id,name
现在我们需要创建一张关联映射表来体现他们之间的关系
3.映射表(article_category_map):字段有article_id,category_id
在TP5.1中,我们可以通过关联预载入with()很方便的就可以把数据查出来
$list = $model->with(['category'])->select();
// Aritcle模型的category方法
public function category(){
return $this->belongsToMany('Category', 'ArticleCategoryMap');
}
// 映射表模型定义
<?php
namespace app\common\model;
use think\model\Pivot;
class InstitutionCategoryMap extends Pivot
{
}
但这样是会把category所有的字段都查出来,根据文档得知可以传递一个匿名函数来限制查询的字段
$list = $model->with(['category' => function ($query) {
$query->field('id,name');
}])->select();
// Aritcle模型的category方法
public function category(){
return $this->belongsToMany('Category', 'ArticleCategoryMap');
}
如果项目中有很多地方关联到了这个分类信息,那么每一个都需要这样限制一下,感觉有点麻烦
灵机一动直接在定义关联关系的地方限制了不就好了么
$list = $model->with(['category'])->select();
// Aritcle模型的category方法
public function category(){
return $this->belongsToMany('Category', 'ArticleCategoryMap')->field('id,name');
}
这种方法官网在文档中也有提过,但是也说明了不能滥用
所以果然就出现了问题:在一条数据时,数据是正常的,但当出现一条数据以上时,只有最后一条数据会有关联数据(图为示例)
一开始我一直在排查自身问题,比如数据库数据错误之类的,但百思不得姐,最后只好试着把关联方法定义的field()去掉,数据果然就正常了。
哦...原来是这里的问题,但我怎么能轻易放弃灵机一动才发现的点子呢?
这里就要引入标题中说的append()方法-模型关联输出,这个方法常用于向查询的模型中追加字段,比如一些状态的中文值。经过测试发现,append()中传入关联模型的方法名也可以实现with()的效果,查询出来的数据结构没有任何区别,而且它还没有上面说到的不显示分类的bug。
$list = $model->append(['category'])->select();
// Aritcle模型的category方法
public function category(){
return $this->belongsToMany('Category', 'ArticleCategoryMap')->field('id,name');
}
果真有这么好的事情?答案是否定的。不得不说,当你使用习惯了框架中模型带给你的各种便利之后,很容易就会忽视掉一些性能方面的问题,其实严格来说,使用模型本身就会损失一定的性能,本质上是用便利来换取性能的。
通过查询执行sql的日志,可以发现,使用with()方法查询列表只会有一条查询关联分类的sql,但使用append()则会有N个。这是由于with()使用的是批量IN查询,而append()则是循环查询,显然后者在多数情况下的查询速度会更慢些。
框架文档中也说明了这一点
多数情况下,我们自然希望程序在性能上更好,但有时候又还需要考虑维护性。比如所有分类中需要增加一个颜色标签,如果是使用append()统一管理,那修改成本就很小很小。如果使用了with(),你就只能批量搜索来替换,若开发风格不统一,就非常容易有遗漏的情况。
两种方法各有利弊,但我个人更推荐使用with()。