Alex
Alex

Reputation: 177

Laravel belongsToMany function

I'm using Laravel to create a basic site with the following function: A user can follow certain topics. I have a 'users' table, a 'topics' table, and as a pivot table, I have a 'following' table.

users
----
user_id (primary)
user_first_name
etc..


following
---------
follow_id
user_id (foreign)
topic_id (foreign)


topics
------
topic_id (primary)
topic_name
etc...

I'm trying to create a page that displays all of the topics, but for the topics that the current user is following, I need to show an overlay on the box.

I have a User Model with the following function:

public function follows() {
    return $this->belongsToMany('Topic', 'following', 'user_id', 'user_id');
}

However, I'm not too sure where to go from here (or whether this is right!)

Would be hugely grateful for any help.

Upvotes: 1

Views: 4211

Answers (4)

RobbieP
RobbieP

Reputation: 698

Almost there, the follows() relationship needs to be like this:

public function follows() {
    return $this->belongsToMany('Topic', 'following', 'user_id', 'topic_id');
}

Then you should be able to grab the topics associated to the current user by doing this:

$topicsUserIsFollowing = Auth::user()->follows;

To start following a topic, you can do this (assuming you have the topic's ID):

Auth::user()->follows()->attach($topic_id);

Edit If you want to see if a topic is followed by someone, then in your Topic model put a function like this:

public function isFollowedBy($user)
{
    return $user->follows->contains($this->topic_id);
}

So then you can do something like this:

$currentUser = Auth::user();

$topics = Topics::all();

foreach($topics as $topic) {
   echo $topic->topic_name;
   if( $topic->isFollowedBy($currentUser) ){
       echo ' - [Already following this topic]';
   }  else {
       echo ' - [Follow this topic]';
   }  
}

You'd want to put the loop in your view, this is just for illustration

Upvotes: 1

Jarek Tkaczyk
Jarek Tkaczyk

Reputation: 81187

1 In your setup you use non-default primary keys (topic_id / user_id / follow_id instead of id) so be sure to set:

protected $primaryKey = 'topic_id';

on each of your models accordingly.

2 I would suggest renaming the relation - follows is ambiguous, unless you have literally 3 models there.

3 Your relation for your current setup, like already suggested by @ChainList:

public function follows()
{
  return $this->belongsToMany('Topic', 'following', 'user_id', 'topic_id');
}

4 In order to check if a user already follows given topic, do this:

// I Assume logged in user
$user = Auth::user();
$topics = Topic::all();

// somewhere in your view
@foreach ($topics as $topic)
  {{ $topic->name }} 
  @if ($user->follows->contains($topic->id))
    Already following this topic
  @else
    Follow this topic (put a link here or whatever)
  @endif
@endforeach

With this you run just a single query for user's topics, when you call $user->follows for the first time.


My suggestion would be:

// leave id as primary key, it will make your life easier

// rename relation to
public function topics()
{
  // rename table name to something meaningful
  return $this->belongsToMany('Topic', 'topic_user'); // laravel convention

  // or if you like topics_followers
}

Upvotes: 1

Ramesh Kotkar
Ramesh Kotkar

Reputation: 766

You sustain the following function in User Model

public function follows() {
   return $this->belongsToMany('Topic', 'following');
}

and use below statement to retrieve the all topics of any user

$topics = User::find(1)->follows;

Where 1 is the user id for particular user.

Upvotes: 2

ChainList
ChainList

Reputation: 1208

First of all, you made a mistake on your follows method. You have the same variable name on local and foreign id 'user_id'.

Then, Did you already add a topic to an user ? If yes, it would be great if you do the same as in your User model on the Topic model

public function followedBy()
{
    return $this->belongsToMany('User', 'following', 'topic_id', 'user_'id');
}

From here, you can add a following topic to users by doing

$user->following()->attach($topic_id);

Or

$user->following()->attach([$first_topic, $second, $third, ...]);

You can use the sync method too, but that will delete all previous relationship between user and the topics which are not in the array.

To retrieve all information you can simply do the following:

foreach ($user->following as $topic) {};

/!\ Do not add parentheses to following otherwise you will get a QueryBuilder instead of a collection of the topics. /!\

If you want to add more filters (for example only active topics)

foreach ($user->following()->active()->get() as $topic) {}

Notice that here I added the parentheses which are necessaries because I do not directly want the topics but a QueryBuilder to filter the results. Call the ->get() method when you are done filtering.

(This suppose you have a method called scopeActive() in your model) See Laravel scope to do so : http://laravel.com/docs/eloquent#query-scopes

You can do the opposite on the topic side by doing :

foreach ($topic->followedBy as $user) {}

PS: sorry for my English, If you misunderstood something. Not my native language.

Upvotes: 4

Related Questions