Reputation:
I'm trying to make a simple "friends" system, by storing each friend id in a single column as a string separated with a comma.
I want to get the users by id and store them in an array:
public function getFriends() {
$friendids = explode(',', $this->friends);
$friends = [];
foreach ($friendids as $id) {
$friends[] = User::findOrFail($id);
}
return $friends;
}
So I can do this in my view:
@foreach ($user->getFriends() as $friend)
<p>Friend ID: {{ $friend->id }}</p>
<p>Friend Username: {{ $friend->username }}</p>
@endforeach
But I think I can't use findOrFail in the user model or what am I doing wrong?
Upvotes: 8
Views: 44742
Reputation: 374
To use the findOrFail($id)
method, you should invoke the object type you want to get. Example:
foreach ($friendids as $id) {
$friends[] = User::findOrFail($id);
}
Upvotes: 15
Reputation:
I made 2 simple mistakes:
1) I removed User
object from findOrFail()
when looking for the mistake I made
2) I had specified user id's (friends) that did not exist in the databse, causing an error
I also improved the code a bit, by adding an optional parameter to get only x amount of users:
// App/User.php
public function getFriends($friends_to_get = null) {
$friendids = explode(',', $this->friends);
$friends = [];
$i = 0;
foreach ($friendids as $id) {
if (is_numeric($id)) {
$friends[] = User::findOrFail($id);
}
if (isset($friends_to_get) && $i < $friends_to_get) {
++$i;
}
if (isset($friends_to_get) && $i == $friends_to_get) {
break;
}
}
return $friends;
}
Now you can do something like this in your view:
@foreach ($user->getFriends() as $friend)
<p>Friend ID: {{ $friend->id }}</p>
<p>Friend Username: {{ $friend->username }}
@endforeach
Or if you want to get, for example, 6 friends only, you can do: $user->getFriends(6)
In my case, $user is the user of who's profile I'm currently viewing. If you want, you could also get your friends only, by doing Auth::user()->getFriends()
/Edit: Added is_numeric check in the foreach loop. This way you don't have to worry about first and last element in the column having an extra comma. Since explode adds an extra value to the array, if there's a comma in the end of the friends column. Now you can just add or remove x,
from/to the column every time and not worry about fetching an object, that does not exist.
Upvotes: 1
Reputation: 2944
Although there's an existing answer, I wanted to provide an alternative solution that I personally feel is a better long-term solution.
Firstly, saving friends as an comma separated list isn't going to scale very well. It also massively limits the ability for you to do things such as 'friends of friends', and other more complex queries.
Really, you should have two tables, users
and friends
.
public function acceptedFriends()
{
return $this->hasMany('App\Friend')->where('accepted', true);
}
public function pendingFriends()
{
return $this->hasMany('App\Friend')->where('accepted', false);
}
Now, when a friend request is rejected, you can simply delete the record in the Friends
table.
$user = User::find($id)->with('acceptedFriends')->firstOrFail();
$users = Friends::where('accepted', false)->where('target_user', $userId)->get();
You might want to also check out https://stackoverflow.com/a/25057320/972370. It's written by someone who is very well known in the Laravel community for advanced Eloquent usage.
Upvotes: 4