Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Laravel: Nesting query join results in a sub array

NOTE Please do not suggest using Eloquent, this is specifically for the Laravel query builder.

For performance reasons we are using Query Builder to retrieve results from a table:

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

If we then want to join a relation onto that query:

DB:table('posts')
    ->leftJoin('comments', 'posts.id', '=', 'comments.post_id')
    ->get();

The results are merged into the array of each post:

[
    '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',
]

How can we have the joined results placed into a nested array? Such as:

[
    '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',            
    ],
]
like image 967
AndrewMcLagan Avatar asked Oct 17 '22 18:10

AndrewMcLagan


2 Answers

Dont think its doable out of the box without Eloquent.

You can go the primitive route:

$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']);
}
like image 71
Paras Avatar answered Nov 02 '22 08:11

Paras


Since you work with DB facade and not Eloquent, and cannot use built-in with() method, you have to implement it yourself:

$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;

If you want to get rid of post_id for comments entries, you can do:

$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;

(I guess the runtime would be similar to with(), since after-all MySql does not provide this functionality out-of-the-box).

like image 40
guyaloni Avatar answered Nov 02 '22 09:11

guyaloni