Search code examples
laravellaravel-collection

Attach new key and value by matching collection value


Hello great people of SO!

First of all, I'm sorry if my english is not very good, but I'm gonna try my best here to describe my problem

I have 3 Models

  1. User
  2. Post
  3. Like

User.php

___________________________________________________________
| id  | name | email | password | created_at | updated_at |
| ... | ...  | ...   | ...      | ...        | ...        |
| ... | ...  | ...   | ...      | ...        | ...        |
| ... | ...  | ...   | ...      | ...        | ...        |

User model relationship:

public function posts() {
    return $this->hasMany(Post::class, 'user_id', 'id');
}

Post.php

__________________________________________________
| id  | user_id | body | created_at | updated_at |
| ... | ...     | ...  | ...        | ...        |
| ... | ...     | ...  | ...        | ...        |

Post model relationship:

public function user() {
    return $this->belongsTo(User::class, 'user_id', 'id');
}

public function likes() {
    return $this->morphMany('likeable');
}

Like.php

_________________________________________________________________________
| id  | likeable_type | likeable_id | user_id | created_at | updated_at |
| ... | ...           | ...         | ...     | ...        | ...        |
| ... | ...           | ...         | ...     | ...        | ...        |
| ... | ...           | ...         | ...     | ...        | ...        |

Like model relationship:

public function likeable() {
    return $this->morphTo('likeable');
}

Everything works fine, for simple C.R.U.D

The problem comes when I use Laravel Debugbar, I saw so many repetitive queries just to fetch few records:

Ex:
    // Let say that I have 5 users
    $users = User::all();

    foreach ($users as $user) {
        $user->load('posts');
    }

    return $users;

    // Result
    select * from `posts`.`user_id` 1 ...
    select * from `posts`.`user_id` 2 ...
    select * from `posts`.`user_id` 3 ...

So I decide to change the method

Ex:
    $users = User::all();

    $posts = Post::whereIn('user_id', $users->pluck('id')->toArray())->get();

    // Result:
    select * `posts`.`user_id` in (1, 2, 3, 4, 5)

I no longer see repetitive query, which is good, After solving this repetitive query, I fetch 'likes' from specific posts

Ex:
    $users = User::all();

    $posts = Post::whereIn('user_id', $users->pluck('id')->toArray())->get();

    $posts_likes = Like::where('likeable_type', 'App\Post') // morphMany
                ->whereIn('likeable_id', $posts->pluck('id')->toArray())->get();
    

Now here's the problems, I do not know how to pair posts_likes to to it's post

Ex:
    $posts = [
        {
            'id': 1,
            'user_id': 2,
            'body': 'Lorem ipsum...',
            ...
        },
        {
            'id': 2,
            'user_id': 2,
            'body': 'Sit amet...',
            ...
        },
        {
            'id': 3,
            'user_id': 3,
            'body': 'abcde...',
            ...    
        },
        ... etc
    ];

    $posts_likes = [
        {
            'id': 1,
            'likeable_type': 'App\Post',
            'likeable_id': 2,
            'user_id': 3,
            ...
        },
            'id': 2,
            'likeable_type': 'App\Post',
            'likeable_id': 2,
            'user_id': 2,
            ...
        {
            'id': 3,
            'likeable_type': 'App\Post',
            'likeable_id': 1,
            'user_id': 5,
            ...
        },
        {
            'id': 4,
            'likeable_type': 'App\Post',
            'likeable_id': 3,
            'user_id': 1,
            ...
        },
        ... etc
    ];

My question:

How to insert likes inside $posts collection by matching exact id? (post id == like likeable_id)

So I can access them in loop, like: $post->likes = [...]

Ex:
    $posts = [
        {
            'id': 1,
            'user_id': 2,
            'body': 'Lorem ipsum...',
            'likes': [
                // All likes for post with this id (1)
            ],
            ...
        },
        {
            'id': 2,
            'user_id': 2,
            'body': 'Sit amet...',
            'likes': [
                // All likes for post with this id (2)
            ],
            ...
        },
        ...
    ];

If there's any unclear explanation, I will edit it a.s.a.p Thanks in advance


Solution

  • you can eagar load all of them

    ref link https://laravel.com/docs/8.x/eloquent-relationships#nested-eager-loading

    $users = User::with('posts.likes')->get();
    return $users;
    

    this code will work if you set correct relationship

    //user model
    public function posts()
    {
        return $this->hasMany(Post::class, 'user_id', 'id');
    }
    
    //post model
    public function likes()
    {
        return $this->morphMany('likeable');
    }
    

    i suppose to generate json link

    [{
        "name": "user",
        "posts": [{
                "name": "postName",
                "likes": []
            },
            {
                "name": "postName",
                "likes": []
            }
        ]
    }]