16
votes

I have set up the relationship and the models as below:

pivot table schema

Schema::create('friend_user', function(Blueprint $table) {
    $table->increments('id');
    $table->integer('user_id')->unsigned();
    $table->integer('friend_id')->unsigned();
    $table->timestamps();
});

pivot table seeder (this defines two 'friendships' to which user '1' belongs one where user 1 is specified in the user_id and the second where user 1 is listed in the friend id):

    $friend_user = array(
        array(
            'id' => 1,
            'user_id' => 1,
            'friend_id' => 3,
        ),
        array(
            'id' => 2,
            'user_id' => 4,
            'friend_id' => 1,
        ),

    );

User model

public function friends()
{
 return $this->belongsToMany('User', 'friend_user', 'user_id', 'friend_id');
}

This is as suggested by Taylor Otwell here: https://github.com/laravel/framework/issues/441

This all works but when I run the following command I get only one result:

foreach(Auth::user()->friends as $i) {
    var_dump($i->id);
}

This returns the value '3' but not 4 as expected. I understand why this is happening (as the user_id is not the friend_id) but how can I get this to return a collection of all friends belonging to a user (ie all friendships) regardless of which end of the connection (user_id or friend_id) the user is?

2
you onlu have 2 relations in your seeder, one for two different users. So your function is only going to return one result for each of those users? - Laurence
Yes, but the friend_id on the second record is the id of user 1 so both entries define a friendship, it just happens that one was initiated by one user and the other by the second user. I'm trying to work out how to return friendships regardless of the order they are entered into the system. - Al_
oh - i get it now... perhaps insert two records when you make a friend? one for 'each way' of the friendship? that would be easiest. - Laurence
That's my backup plan, I was hoping there was a cleaner way of doing it though - Al_
How did you handle this in the end @Al_? - Mike

2 Answers

23
votes

Instead of creating two records use a new function.

public function friends()
{
  return $this->belongsToMany('User', 'friend_user', 'user_id', 'friend_id');
}

// Same table, self referencing, but change the key order
public function theFriends()
{
  return $this->belongsToMany('User', 'friend_user', 'friend_id', 'user_id');
}

//You can then call opposite record(s) using:
foreach( Auth::user()->theFriends as $theFriends )

I used this approach in my project so I can have better separation for organizing the results.

-2
votes

use this

public function friends()
{
 return $this->belongsToMany('User', 'friend_user', 'user_id', 'friend_id')->orWhere('friend_id', $this->id);;
}

in this case you get one query and one record for each relation