Hello wonderful people of StackOverflow, I hope you all have a good day ( ˘⌣˘)♡(˘⌣˘ )
I'm new to Laravel and currently learning about eloquent relationships (hasMany)
I'm sorry this post was too long to read, but I wanted to clarify every step I did until I ran into problems, and also sorry if my english is bad (⁄ ⁄•⁄ω⁄•⁄ ⁄)
So here we go
In my User model
public function posts() {
// A user can have many posts
return $this->hasMany('App\Post');
}
In my Post model
public function owner()
{
// A post belongs to a user
return $this->belongsTo('App\User', 'user_id');
}
In my Post Table, I have 3 simple records
|----------------------------------------------------------|
| id | user_id | body | ... | ... |
| 1 | 1 | My first post | ... | ... |
| 2 | 1 | Second post | ... | ... |
| 3 | 1 | Another post | ... | ... |
And then let's say, we want to see the user with id = 1 with all posts they created, so I use the code below:
// AccountController
public function profile($id)
{
$user = App\User::with('posts')->findOrFail($id);
return $user;
}
// Will return data:
{
"id": 1,
"name": "Prof. Angela Runte Jr.",
"email": "mborer@example.org",
...
"posts": [
{
"id": 1,
"user_id": 1,
"body": "My first post",
...
},
{
"id": 1,
"user_id": 1,
"body": "Second post",
...
},
{
"id": 1,
"user_id": 1,
"body": "Another post",
...
}
}
And in Blade view, I can simply get data like below:
// in profile.blade.php
$user->name
$user->...
@foreach($user->posts as $post)
// Show each post data
@endforeach
It works perfectly the way I want, Thanks to Taylor Otwell for creating an amazing framework (´• ω •`) ♡
And then I had an idea in my mind, let's make a feature where a user can post to other users, why not? Let's do it! (✧ω✧)
So in my Post migration, I change the table schema:
From:
// Before
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
$table->unsignBigInteger('user_id');
$table->text('body');
$table->timestamps();
To:
// After
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
$table->unsignedBigInteger('user_id');
// New column: nullable()
$table->foreign('for_id')->references('id')->on('users')->onDelete('cascade');
$table->unsignedBigInteger('for_id')->nullable();
$table->text('body');
$table->timestamps();
As you can see, I put nullable() in column 'for_id'
In words, if for_id is empty then this post belongs only to the user who made the post
Otherwise the post belongs to the user who made the post, as well as the intended user (for_id).
Then after that, I run
// I only use UserSeeder (wanna keep it simple tho)
php artisan migrate:fresh --seed
And now we have 2 users with id = 1 and 2 (Yay!)
|----------------------------------------------------------------------------------------|
| id | name | email | ... | ... |
| 1 | Camden Kulas | lemke.fabian@example.net | ... | ... |
| 2 | Mrs. Roberta Stroman | ybartoletti@example.com | ... | ... |
Let's use tinker for creating dummy data
php artisan tinker
>> $user = App\User::first();
=> App\User {#3910
id: 1,
name: "Camden Kulas",
email: "lemke.fabian@example.net",
...
}
>> $user->posts()->create(['body' => 'New first post']);
=> App\Post {#4120
body: "New first post",
user_id: 1,
updated_at: "2020-08-30 03:42:43",
created_at: "2020-08-30 03:42:43",
id: 1,
}
// we create one for Mrs. Roberta Stroman (user with id #2)
>> $user->posts()->create(['for_id' => 2, 'body' => 'Hello there']);
=> App\Post {#3912
for_id: 2,
body: "Hello there",
user_id: 1,
updated_at: "2020-08-30 03:44:18",
created_at: "2020-08-30 03:44:18",
id: 2,
}
So now we have 2 record in our posts table
|--------------------------------------------------------------------|
| id | user_id | for_id | body | ... |
| 1 | 1 | NULL | New first post | ... |
| 2 | 1 | 2 | Hello there | ... |
We're still in user id #1
>> $user->posts
=> Illuminate\Database\Eloquent\Collection {#4120
all: [
App\Post {#4125
id: 1,
user_id: 1,
for_id: null,
body: "New first post",
...
},
App\Post {#4128
id: 2,
user_id: 1,
for_id: 2,
body: "Hello there",
...
},
],
}
( ̄ω ̄) Ok 'till here, all works fine, let's try with user id #2
In my mind, I want to get user id #2, and with all posts made by id # 2, and all posts from others for id # 2
>> $user = App\User::find(2);
=> App\User {#4121
id: 2
name: "Mrs. Roberta Stroman",
email: "ybartoletti@example.com",
...
}
>> $user->posts
=> Illuminate\Database\Eloquent\Collection {#4067
all: [],
}
Σ(°ロ°) The posts is empty, so why the hell it's empty?
ఠ ͟ಠ Then I try to use with()
>> $user = App\User::with('posts')->find(2);
=> App\User {#4120
id: 2,
name: "Mrs. Roberta Stroman",
email: "ybartoletti@example.com",
...
posts: Illuminate\Database\Eloquent\Collection {#4123
all: [],
},
}
(ꐦ°᷄д°᷅) What?? Why The posts still empty? щ(ºДºщ)
So, after searching and reading for days, still I couldn't figured it out how to solve this (。╯︵╰。)
And then I tried to change in my User model: the posts() function
From:
public function posts()
{
// A post belongs to a user
return $this->hasMany('App\Post');
}
To:
public function posts()
{
// In my logic like:
// Hey I want to fetch all posts that made by this user, **or where all posts that created by others for this user**
// Make sense in human language right?
return $this->hasMany('App\Post', 'user_id')->orWhere('posts.for_id', $this->id);
}
I exited the tinker, then re-enter the tinker again
>> $user = App\User::first();
// return user id #1
>> $user->posts
// works fine, return all posts
(ಠ益ಠ) Now I am facing other problem
>> $user = App\User::with('posts')->first(); // notice that I'm using with()
// tinker return the desired user, and also return ONLY one post
>> App\User {#4123
id: 1,
name: "Camden Kulas",
email: "lemke.fabian@example.net",
....
posts: Illuminate\Database\Eloquent\Collection {#4130
all: [
App\Post {#4128
id: 1,
user_id: 1,
for_id: null,
body: "New first post",
created_at: "2020-08-30 03:42:43",
updated_at: "2020-08-30 03:42:43",
},
// where is the second post?
],
},
}
Then I try to change tinker session with user id #2
>> $user = App\User::find(2);
// return user id #2
>> $user = App\User::with('posts')->find(2);
// return user id #2, but no post (only array all)
So without writing further, the questions I want to ask
[#1] Why user #1 only fetch one post, meanwhile he created 2 posts?
One post without for_id (NULL), and second post with for_id
[#2] How to make user id #2 to fetch all their posts, and also posts that created for them?
Because IMHO posts() function in User model, perfectly make sense for me, but it doesn't work
If there's any unclear explanation, it will edit in time
Thanks in advance
Update
I figured it out that I can simply get all posts by user X and all posts for user X with this way:
$user = App\User::find(X);
$posts = $user->posts()->orWhere('for_id', X)->get();
return [
'user' => $user,
'posts => $posts
];
So now the question comes to my mind:
How to use with() for $posts?
// Like this
$user = App\User::with('allposts')->find(X);
// Return user X and all posts made by X and posted to user X
// So I can simply get data in view like this:
@foreach ($user->allposts as $post)
// Loop all posts by user X and for user X
@endforeach