国庆刷 GitHub 发现有人在 issue 提了一个 append 的操作,可以直接减少输出。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public function index () { $banner = Banner::field('id, cid, images, href') ->where('status', 1) ->find();
$data = $banner->append([ 'category' => [ 'name', ], ])->toArray();
return json($data); }
|
1 2 3 4 5 6 7 8 9 10 11
| { "id": 1, "cid": 1, "images": "https://cdn.hongfs.cn/tmp/douyin_20211015/dynamic_cover/184.gif", "href": "https://www.hongfs.cn/?f=mini_banner_top_1", "category": { "id": 1, "name": "首页-顶部", "create_at": "2022-10-04 16:32:19" } }
|
可以看见目前因为有 bug,所以 category 这个一对一关联是直接显示全部数据的。
对代码进行跟踪后,append
方法添加的数据最终会在 toArray
完成处理。
vendor/topthink/think-orm/src/model/concern/Conversion.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
|
protected $append = [];
public function append(array $append = [], bool $merge = false) { if ($merge) { $this->append = array_merge($this->append, $append); } else { $this->append = $append; }
return $this; }
public function toArray(): array { ... foreach ($this->append as $key => $name) { $this->appendAttrToArray($item, $key, $name); } ... }
protected function appendAttrToArray(array &$item, $key, $name) { if (is_array($name)) { $relation = $this->getRelation($key, true); $item[$key] = $relation ? $relation->append($name) ->toArray() : []; } elseif (strpos($name, '.')) { [$key, $attr] = explode('.', $name); $relation = $this->getRelation($key, true); $item[$key] = $relation ? $relation->append([$attr]) ->toArray() : []; } else { $value = $this->getAttr($name); $item[$name] = $value;
$this->getBindAttrValue($name, $value, $item); } }
|
$this->append
循环后给到 appendAttrToArray
处理。 appendAttrToArray
的入参应该是:
1
| $this->appendAttrToArray(&$item, 'category', ['name']);
|
所以我们会进入第一个 if 的判断。因为之前看过一部分源码,所以直接就能知道问题了,$relation->append($name)
这里变成了追加数据表不存在的额外字段了。
最方便就是通过循环然后一个一个把我们需要的字段添加上去。
1 2 3 4 5 6 7 8
| if(is_array($name)) { $relation = $this->getRelation($key, true);
foreach ($name as $attr) { $item[$key][$attr] = $relation->getAttr($attr); } }
|
1 2 3 4 5 6 7 8 9
| { "id": 1, "cid": 1, "images": "https://cdn.hongfs.cn/tmp/douyin_20211015/dynamic_cover/184.gif", "href": "https://www.hongfs.cn/?f=mini_banner_top_1", "category": { "name": "首页-顶部" } }
|
接着我们要研究下中间隔着 .
的方式。
1 2 3 4 5 6 7 8 9 10 11 12
| public function index () { $banner = Banner::field('id, cid, images, href') ->where('status', 1) ->find();
$data = $banner->append([ 'category.name', ])->toArray();
return json($data); }
|
和上面的处理方式是差不多的。要注意应该只有一个 .
,不然会出问题的。
1 2 3 4 5 6 7
| if(strpos($name, '.')) { [$key, $attr] = explode('.', $name); $relation = $this->getRelation($key, true);
$item[$key][$attr] = $relation->getAttr($attr); }
|
1 2 3 4 5 6 7 8 9
| { "id": 1, "cid": 1, "images": "https://cdn.hongfs.cn/tmp/douyin_20211015/dynamic_cover/184.gif", "href": "https://www.hongfs.cn/?f=mini_banner_top_1", "category": { "name": "首页-顶部" } }
|
接着在使用过程中还会遇到其他一些问题。
比如点的,如果我的数量有多少条,就有多少条记录。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public function index () { $banner = Banner::field('id, cid, images, href') ->where('status', 1) ->find();
$data = $banner->append([ 'category.id', 'category.name', 'category.create_at', ])->toArray();
return json($data); }
|
1 2 3
| [SQL] SELECT * FROM `fa_banner_category` WHERE `id` = 1 LIMIT 1 [ RunTime:0.046582s ] [SQL] SELECT * FROM `fa_banner_category` WHERE `id` = 1 LIMIT 1 [ RunTime:0.046457s ] [SQL] SELECT * FROM `fa_banner_category` WHERE `id` = 1 LIMIT 1 [ RunTime:0.043727s ]
|
我想是不是在 append
方法优化下会比较好呢,把 .
的进行拆分以便可以进第一个 if 判断。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
|
public function append(array $append = [], bool $merge = false) { $tmp = $merge ? $this->append : [];
foreach($append as $k => $name) { if(is_array($name)) { if(!isset($tmp[$k])) { $tmp[$k] = $name; } else { $tmp[$k] = array_merge($tmp[$k], $name); }
$index = array_search($k, $tmp);
if($index !== false) { array_splice($tmp, $index, 1); }
continue; } elseif(strpos($name, '.')) { [$key, $attr] = explode('.', $name);
if(!isset($tmp[$key])) { $tmp[$key] = []; }
if(in_array($attr, $tmp[$key])) { continue; }
$tmp[$key][] = $attr; } else { if(in_array($name, $tmp)) { continue; } else if(isset($tmp[$name])) { continue; }
$tmp[] = $name; } }
$append = $tmp;
return $this; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| public function index () { $banner = Banner::field('id, cid, images, href') ->where('status', 1) ->find();
$data = $banner->append([ 'category.id', 'category.name', 'category.create_at', ])->toArray();
return json($data); }
|
现在我们的 SQL 就又回归到了一条。
下面这种情况肯定是不生效的,比较你早已经把关联进行引入了,这时你应该用 hidden
隐藏掉不想的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public function index () { $banner = Banner::field('id, cid, images, href') ->where('status', 1) ->find();
$banner->category;
$data = $banner->append([ 'category' => [ 'name', ], ])->toArray();
return json($data); }
|
目前看,append
适合那种不需要额外 where 的,当然你也可以写在关联后面。append
查询的语句目前是 SELECT *
暂时不能直接改,如果有需要限制具体字段还是 field
。减少无关字段的返回可以提升网络速度,业务规模小就没必要了。