Laravel:将查询联接结果嵌套在子数组中

6

注意 请不要建议使用 Eloquent,这是专门针对 Laravel 查询构造器的。

出于性能原因,我们使用查询构造器从表中检索结果:

DB::table('posts')->get();

如果我们想要在该查询中加入一个关系:
DB:table('posts')
    ->leftJoin('comments', 'posts.id', '=', 'comments.post_id')
    ->get();

结果合并到每个文章的数组中:
[
    'id'                => 1,
    'title'             => 'My Blog Post',
    'content'           => '<h1>This is a post</h1><p>hello world</p>',
    'post_author'       => 'Billy',
    'comment'           => 'This is a comment',
    'comment_author'    => 'Andrew',
]

我们如何将连接结果放入嵌套数组中?例如:
[
    'id'                => 1,
    'title'             => 'My Blog Post',
    'content'           => '<h1>This is a post</h1><p>hello world</p>',
    'post_author'       => 'Billy',
    'comment'           => [
        'id'                => 22,
        'comment'           => 'This is a comment',
        'comment_author'    => 'Andrew',            
    ],
]

关系只在Eloquent中使用。Laravel查询构建器不处理关系。@SougataBose - GiamPy
查询返回的格式有什么问题? - Mayank Pandeyz
你是否正在寻找类似于MySQL中的GROUP BY的东西,以便为每个帖子分组所有评论? - K Arun Singh
2
@SougataBose 这个问题明确说明不要使用Eloquent…… - AndrewMcLagan
在 Laravel 中,您无法使用查询构建器获取上述输出。为此,您需要编写自己的类和函数,然后返回所需格式的输出。 - Sandeep Garg
5个回答

3

没有使用Eloquent难以直接实现。

您可以选择基本方法:

$results = DB:table('posts')
    ->leftJoin('comments', 'posts.id', '=', 'comments.post_id')
    ->select('posts.*', 'comments.*', 'comments.id as comments_id')
    ->get(); 

foreach($results as &$result) 
{ 
    $result['comment'] = [
        'id' => $result['comment_id'], 
        'comment' => $result['comment'], 
        'comment_author' => $result['comment_author']
    ]; 
    unset($result['comment_author'], $result['comment_id']);
}

3
我希望Eloquent在处理结果时能够有更加微妙的方式,至少作为一种选项。 - Murilo
1
您的解决方案没有将每个帖子的所有评论分组为嵌套数组,实际上每个帖子将出现N次,其中N是其评论数。 - guyaloni
这会使查询变得非常缓慢,如果有超过10000篇文章,那么查询本身将需要几秒钟才能执行。 - Jagadish Meghval
@JagadishMeghval 这个问题是如何以嵌套的方式获取联接结果,与性能无关。 - Paras
优雅的意思和这个答案一样吗? - undefined

1

由于您使用的是DB facade而不是Eloquent,因此无法使用内置的with()方法,您需要自己实现:

$posts = DB::table('posts')->get()->toArray();
$comments = DB::table('comments')->get()->toArray();

foreach($posts as &$post)
{
    $post->comments = array_filter($comments, function($comment) use ($post) {
        return $comment->post_id === $post->id;
    });
}

return $posts;

如果您想在评论条目中摆脱post_id,可以执行以下操作:
$posts = DB::table('posts')->get()->toArray();
$comments = DB::table('comments')->get()->toArray();

foreach($posts as &$post)
{
    $comments = array_filter($comments, function($comment) use ($post) {
        return $comment->post_id === $post->id;
    });

    $post->comments = array_map(function ($comment) {
        unset($comment->id);
        return $comment;
    }, $comments);
}

return $posts;

我猜运行时类似于with(),因为毕竟MySql没有提供这个功能。


0

这里有一些新信息:

$data= $DB->select("select posts.*,comments from posts left join comments on posts.id = comments.post_id");

我不确定它是否有效,但你可以尝试一下


这并不是对问题的回答。一旦您获得足够的声望,您就可以在任何帖子上发表评论;相反,提供不需要问者澄清的答案。- [来自审查] (/review/late-answers/31393825) - Sayakiss

0

你可以试试这个

$data= $DB->select("select *,(select json_agg(datas) from (select * from comments where posts.id=comments.post_id) as datas) as comments from posts;");

但你可能也需要对注释进行解码


0
$products = Category::with('product')->get(array('category.qty','category.product_id','category.net_unit_price as price'));

    foreach ($products as $p){
        $pl['name'] = $p->product->name;
        $pl['image'] = $p->product->image;
        unset($pl->product);
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接