4

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
Gwein
  • 109
  • 3
  • 14
  • Instead of typing dummy data in tinker you can use laravel [seed](https://laravel.com/docs/7.x/seeding) it will be helpful, answering your question if you created `for_id` foreign field you should create another relation in model so you can get by that field, each new relation = new declaration of it inside model – Arman Aug 30 '20 at 05:31
  • @arman Hello, I thank you for your time reading this silly question of mine, so I cannot only use posts() function to fetch all post that made by selected user and also all posts that inteded for selected user? – Gwein Aug 30 '20 at 05:36
  • You can check [pivot](https://laravel.com/docs/7.x/eloquent-relationships#many-to-many) that might help – Arman Aug 30 '20 at 05:42

0 Answers0