goods_de Schwäif初级说实话,我见过下面的代码,无论是在公司还是在开源项目上。 $query = $db->query('select * from goods');
$result = $query->fetchAll();
// 方案一
foreach($result as $key => $item){
$query = $db->query('select * from goods_detail where goods_id=' . $item['id']);
$result[$key]['goods_detail'] = $query->fetch();
}
var_dump($result);
// 方案二
foreach($result as &$item){
$query = $db->query('select * from goods_detail where goods_id=' . $item['id']);
$item['goods_detail'] = $query->fetch();
}
unset($item);
var_dump($result);
// 方案三
$result = array_map(function($item){
$query = $db->query('select * from goods_detail where goods_id=' . $item['id']);
$item['goods_detail'] = $query->fetch();
return $item;
},$result);
var_dump($result);
这是最暴力的方式,也是立杆的影子,而且选项1 看起来代码还是很繁琐不是吗?学过引用这一节的朋友应该使用第二种方式,就是直接使用引用来操作源数据。当然,最好不要忘记最后删除$item。除了第二种方式之外,我们还可以使用第三种方式。 ,用array_map,诚然,这和第二种方法没有什么不同,但是有一个非常大的问题:数据库查询的N+1 。从执行中我们可以看到,除了查询列表中的SQL之外,每查询一条记录都需要执行一条SQL,从而产生了额外的查询。请记住请求是否没有 limit 限制。那会是什么样子? 进阶看到这里,可能有人会想到另一个解决方案,先查询列表,然后取出列表中的goods_id 然后❿❿❿,然后循环赋值给列表,参见代码。 $goods_id = array_column($result,'id');
$goods_id_str = implode(',',$goods_id);
$query = $db->query(sprintf('select * from goods_detail where goods_id in (%s)',$goods_id_str));
$goods_detail_list = $query->fetchAll();
foreach($result as &$item){
$item['goods_detail'] = array_first($goods_detail_list,function($item1){
return $item['id'] == $item1['goods_id'];
});
}
unset($item);
var_dump($result);
/**
* 来自 Laravel
*/
if (!function_exists('value')) {
function value($value)
{
return $value instanceof Closure ? $value() : $value;
}
}
/**
* 来自 Laravel
*/
if (!function_exists('array_first')) {
/**
* @param $array
* @param callable|null $callback
* @param null $default
* @return mixed
*/
function array_first($array, callable $callback = null, $default = null)
{
if (is_null($callback)) {
if (empty($array)) {
return value($default);
}
foreach ($array as $item) {
return $item;
}
}
foreach ($array as $key => $value) {
if (call_user_func($callback, $value, $key)) {
return $value;
}
}
return value($default);
}
}
在这段代码中,我们完美避免了N+1 的困境,在in 查询中使用,然后传入数组,搜索完之后再使用❝数组❝方法,就可以了将其传递给 goods_detail 索引。虽然这个效率比第一次高了很多,但也并不完美。我们来看看最终的解决方案。关于array_first,可以阅读我的另一篇文章《PHP多维数组中的array_find》。 最佳实践$goods_detail_list_by_keys = array_column($goods_detail_list,null,'goods_id');
foreach($result as &$item){
$item['goods_detail'] = array_key_exists($goods_detail_list_by_keys,$item['id']) ? $goods_detail_list_by_keys[$item['id']] : null ;
// php 7.1+
// $item['goods_detail'] = $goods_detail_list_by_keys[$item['id']] ?? null;
}
unset($item);
var_dump($result);
这次我们使用另外两个函数。 array_column、array_key_exists,我们一一来说。其实在array_column 的官方手册中,我们可以在示例#2中介绍我们想要的方法。这里的应用是将单个商品下goods_detail_list 中商品的key重置为goods_id 。后面我们直接用array_key_exists 判断是否存在,然后进行相应处理。这里我们还可以再做一个操作,就是默认值,因为有时候数据可能不匹配。如果找到,则立即返回到前端。如果前端没有预料到这种情况,没有进行容错处理,就会导致前端页面崩溃,我们来编码 // 在 「进阶」 板块中,我们用到了 「array_first」 函数,该函数第三个参数可以直接设置默认值,我们就不多讲了,主要讲讲最后一个
$goods_detail_default = [
'content' => '默认内容',
'id' => null,
'goods_id'=> null,
];
foreach($result as &$item){
$tmp = array_key_exists($goods_detail_list_by_keys,$item['id']) ? $goods_detail_list_by_keys[$item['id']] : [] ;
// php 7.1+
// $tmp = $goods_detail_list_by_keys[$item['id']] ?? [];
$item['goods_detail'] = array_merge($goods_detail_default,$tmp);
}
unset($item);
var_dump($result);
End看看这就完成了,但是有的朋友说为什么不使用leftJoin 来处理吗?事实上,在处理一对一关系 很多时候我们会选择innerJoin 或 。它可以通过单个 SQL 完成,并且很少使用。与这种方案类似,其实在主流框架中,标准方案几乎都是这样处理的,比如Laravel、ThinkPHP。有很多场景需要考虑。例如,有时我只需要根据要求参加。 ,或者我必须根据我后续的业务结果来决定是否进行一对一加载。然而,在这种情况下,join 似乎不合适。
|
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。