Laravel 5.* - Eloquent Many to Many Relationships - php

I have the following tables
USERS = username | email | name
FOLLOWERS = user_id | follower_id
When a logged-in user clicks on "follow" my code saves his id inside followers.follower_id, and the id of user who he wants to follow is saved inside followers.user_id.
To see how many followers a user has and how many users a user is following I use:
$followers = Follower::where('user_id', $user->id)->count();
$following = Follower::where('follower_id', $user->id)->count();
This works well, but I would like to show information about the followers of one user. I've tried the following:
$first_follower = $followers[0]->user->username;
But it return the user followed not the follower.
I am wondering how I can get information about the follower
User Model
protected $fillable = ['username','email','name'];
public function follow() {
return $this->hasMany('Shop\Follower');
}
Follower Model
protected $fillable = ['user_id','follower_id'];
public function user() {
return $this->belongsTo('Shop\User');
}

An example how you should implement relations, is above. It's many users to many users thru followers table. You probably dont need a follower model, cause you already have a user model. Your code will work after some analyze and enhancements, but i will highly recomend you to make something like this inuser model instead:
public function followers()
{
return $this->belongsToMany('App\User','followers','user_id','follower_id');
}
public function following_users()
{
return $this->belongsToMany('App\User','followers','follower_id','user_id');
}
Than you can access a followers $user->followers (this will return eloquent collection and you will be able to do whatever you want with this according to laravel docs collection api) and a certain one like $user->followers[0]
Hope i get your answer rigth.

If I get this right the followers are instances of the User class/model, so you don't need a Follower model. You can just define a Many To Many Relationship
In your User model you can add:
public function followers()
{
return $this->belongsToMany('Shop\User', 'followers', 'user_id ', 'follower_id');
}
public function following()
{
return $this->belongsToMany('Shop\User', 'followers', 'follower_id', 'user_id');
}
Than you can access the user followers just by $user->followers which will return a Laravel Collection and with $user->following you can access the ones that the user is following.
//Get the count of all followers for $user
$count = $user->followers()->count();
//Get the count of all followed by $user
$count = $user->following()->count();
//Get the username of the first follower
$follower = $user->followers()->first();
echo $follower->username;
//Loop trough all followers
foreach ($user->followers as $follower) {
echo $follower->username;
}
Defining this relation can help you save/delete followers just by using the attach() and detach() methods
// The $user will be followed by an user with $followerId
// A record in the `followers` table will be created with
// user_id = $user->id and follower_id = $followerId
$user->followers()->attach($followerId);
// The $user will stop be followed by an user with $followerId
$user->followers()->detach($followerId);
A side note:
There is a difference between calling the followers() method and calling the followers property. The first will return BelongsToMany relation and you can call all the Eloquent query builder method on it and the later will return a Collection
/** #var Illuminate\Support\Collection */
$user->followers;
/** #var Illuminate\Database\Eloquent\Relations\BelongsToMany */
$user->followers();

Related

Reverse contains() in laravel?

I'm new to laravel,
I'm trying to get a row from a table according to it's Many-to-Many relationship with another table.
You know when you pass multiple ids in contain() to check if they have relationship with that row or not, like this:
$row->contains([4,6,9]);
i want to reverse this, where i can use the ids [4,6,9] to get any row from the other table linked with them.
Let's say you have this two models: User and Role that makes many-to-many relationship.
If you defined your relationships properly:
/** User.php */
public function roles()
{
return $this->belongsToMany(Role::class);
}
-
/** Role.php */
public function users()
{
return $this->belongsToMany(User::class);
}
Then you could use the relationship to accomplish what you are trying to do.
From the related object:
$user = User::find(1);
// getting all the roles attached with a user:
$roles = $user->roles;
// getting all the roles attached with a user that has certain ids:
$roles = $user->roles()->whereIn('id', [2, 4, 6])->get();
Also, you could find your desired related model using the collection instance that return the relationship:
$user = User::find(1);
// getting all the roles attached with a user:
$roles = $user->roles;
// getting a specific role attached to a user:
$specific_role = $roles->where('id', 6)->first();

Laravel - How to chain Eloquent relationship - Eager Loading

I'm using Laravel 5.4, Laravel Roles from here and Eloquent relationships.
I'm trying to retrieve the users of a company along with their roles.
User::find(1)->roles gets me the user's roles whose id =1
Company::find(1)->users gets me all the users that belongs to the company whose id =1 (without the roles of users).
Company::find(1)->users->roles returns an error Property [roles] does not exist on this collection instance.
Questions
Is it possible to do what I want to do ?
If so, how should I do it ?
User.php
class User extends Authenticatable
{
use HasRoleAndPermission;
public function company()
{
return $this->belongsTo('App\Company');
}
public function user()
{
return $this->belongsTo(User::class);
}
}
HasRoleAndPermission.php
trait HasRoleAndPermission
{
public function roles()
{
return $this->belongsToMany(config('roles.models.role'));
}
}
Company.php
class Company extends Model
{
public function users() {
return $this->hasMany('App\User');
}
}
$company = Company::with('users.roles')->find(1);
Will load all the users, and all the roles for each user.
Update
According to your comment, you don't want the company data. Just users and roles Using eager loading and relationship existence.
$users = User::with('roles')
->whereHas('company' => function($query){
$query->where('name', '=', 'company'); //If you don't have the company ID
})->get();
Without relationship existence
$users = User::where('company_id', 1)->with('roles')->get();
1 company has many users.
1 users has many roles.
You are trying to get the roles of a collection of users (the property only exists for one user) thus, the property doesn't exists for the collection.
If you want to get all the roles of all users in the company, you might try the above code:
$roles = [];
Company::find(1)->users->foreach(function($user) {
$roles = array_merge($roles, $user->roles);
});
--------- edit ---------
For eager loading the roles of users, you must use with, as suggested by #Ohgodwhy, but I'd refactor a little:
$users = User::with('roles')->where('company_id', $companyId)->get();
Now you have the array of users eager loading their roles. You still can't access directly $users->roles, you must first get a user, only then get its roles.

Laravel relationship query optimization

First of all I apologise for the title, I could not find anything better.
In my project I have Users and Groups. Users can join a group and create a group. The relationships are defined as follows.
User Model
/** Get all the groups the user is administrator of
* #return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function groupsAdmin()
{
return $this->hasMany('App\Group','group_admin_id','id');
}
Group Model
/** Get the users in a group
* #return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function users()
{
return $this->belongsToMany(\App\User::class,'groups_users', 'group_id','user_id');
}
What I am trying to do is get all the users who have joined the groups created by an user. For that I have written a method in my User model:
/**
* Returns all the users who have attended groups created by this user
*/
public function getPastGroupAttendees()
{
// first verify if the user is admin of any group
if(!$this->groupsAdmin)
{
return false;
}
$attendees = array();
foreach($this->groupsAdmin as $group)
{
if(count($group->users) > 0) $attendees[] = $group->users;
}
return $attendees;
}
But the problem with this method is its slow and will get slower with new data. And also as a user can join multiple groups, I would get duplicate users from this method.
So if anyone can show me some directions to optimize and correct this it would be very helpful.
You can setup two relations in User model:
public function groupsAdmin()
{
return $this->hasMany('App\Group', 'group_admin_id', 'id');
}
public function groups()
{
return $this->belongsToMany('App\Group');
}
It's one-to-many for admin and many-to-many for groups and users (you'll need pivot table here).
To load the data, use eager loading:
$groupWithUsers = Group::where('group_admin_id', $adminId)->with('users')->first();
$groupsOfUsers = User::with('groups')->get();
To remove duplicates you can iterate over groups and merge() all users collections into one and then use unique() method to remove duplicates.
Another way to do it is to create model for pivot table and get all users of the group with simple and readable code:
$groups = Group::where('group_admin_id', $adminId)->pluck('id'); // Get IDs of groups.
UserGroup::whereIn('group_id', $groups)->get()->unique(); // Get unique users from these groups.

Friendship system with Laravel : Many to Many relationship

I'm trying to create a Friendship system with Laravel (I'm starting with it) but I'm blocked with relationships. Here's the thing : there is one table Users and one table Friends which contains the following columns :
friends: id, user_id, friend_id, accepted.
It looks like a Many to Many so here's what I set on User class :
class User extends Eloquent {
function friends()
{
return $this->belongsToMany('User');
}
}
But when I try a :
$friends = User::find($id)->friends()->get()
I have this error :
Base table or view not found: 1146 Table 'base.user_user' doesn't exist
I would like to get a list of the Friends of a user, no matters if the user sent the invitation or received it. So the user can ba on user_id or on friend_id and then I retrieve the data of the other user depending of that column.
Any idea? Thank's!
EDIT : Here's the code I use :
$usersWithFriends = User::with('friendsOfMine', 'friendOf')->get();
$user = User::find(Auth::id())->friends;
foreach($user as $item) {
echo $item->first()->pivot->accepted;
}
tldr; you need 2 inverted relationships to make it work, check SETUP and USAGE below
First off the error - this is how your relation should look like:
function friends()
{
return $this->belongsToMany('User', 'friends', 'user_id', 'friend_id')
// if you want to rely on accepted field, then add this:
->wherePivot('accepted', '=', 1);
}
Then it will work without errors:
$user->friends; // collection of User models, returns the same as:
$user->friends()->get();
SETUP
However you would like the relation to work in both ways. Eloquent doesn't provide a relation of that kind, so you can instead use 2 inverted relationships and merge the results:
// friendship that I started
function friendsOfMine()
{
return $this->belongsToMany('User', 'friends', 'user_id', 'friend_id')
->wherePivot('accepted', '=', 1) // to filter only accepted
->withPivot('accepted'); // or to fetch accepted value
}
// friendship that I was invited to
function friendOf()
{
return $this->belongsToMany('User', 'friends', 'friend_id', 'user_id')
->wherePivot('accepted', '=', 1)
->withPivot('accepted');
}
// accessor allowing you call $user->friends
public function getFriendsAttribute()
{
if ( ! array_key_exists('friends', $this->relations)) $this->loadFriends();
return $this->getRelation('friends');
}
protected function loadFriends()
{
if ( ! array_key_exists('friends', $this->relations))
{
$friends = $this->mergeFriends();
$this->setRelation('friends', $friends);
}
}
protected function mergeFriends()
{
return $this->friendsOfMine->merge($this->friendOf);
}
USAGE
With such setup you can do this:
// access all friends
$user->friends; // collection of unique User model instances
// access friends a user invited
$user->friendsOfMine; // collection
// access friends that a user was invited by
$user->friendOf; // collection
// and eager load all friends with 2 queries
$usersWithFriends = User::with('friendsOfMine', 'friendOf')->get();
// then
$users->first()->friends; // collection
// Check the accepted value:
$user->friends->first()->pivot->accepted;
It's oviously a problem in your DB and also definition of the relation. Many-to-Many relation type expects you to use and intermediate table. Here's what you have to do :
Create a user_friend (id, user_id, friend_id) table in your schema.
Remove unnecessary fields from user and friend tables.
Create proper foreign keys . user.id-> user_friend.user_id , friend.id -> user_friend.friend_id
Better define full relation on the User and Friend models,
for example :
class User extends Eloquent {
function friends()
{
return $this->belongsToMany('User', 'user_friend', 'user_id', 'friend_id');
}
}
You can read much more in Laravel docs, HERE

Laravel Object queries - 3 tables

I have three tables like this:
**Users**
id
**Posts**
id
user_id
**Favorites**
id
user_id
post_id
Currently, I made it so when I query my posts for display, it pulls all the related user data who created the post with that row which is great! But what I'm trying to do now is also add to see if the user Authorized (Logged in) has favorited the post (row) so I can display to that they already favorited it. I don't want to re-query for every post (i think its called the N+1 problem?). I'm using Laravel4
Post model
class Post extends Eloquent{
public function user(){
return $this->belongsTo('User');
}
User model
public function posts(){
return $this->hasMany('Post');
}
PostsController
public function index()
{
$posts = Post::with('user')->paginate(25);
return View::make('index', compact('posts'));
}
Step 1. Add favorites relationship in Post model.
public function favorites() {
return $this->hasMany('Favorite');
}
When querying the Model.
$auth_user_id = Auth::user()->id;
$posts = Post::with(array('user', 'favorites' => function($query) use ($auth_user_id){
$query->where('user_id', '=', $auth_user_id);
}))->get();
For more information refer to the eager load constraints,
http://laravel.com/docs/eloquent#eager-loading
Adding a many-to-many relationship using the favorites table as pivot would be one approach.
Add favorites relationship in User model:
public function favorites() {
return $this->belongsToMany('Post', 'favorites');
}
You should then be able to get all favorites by simply accessing
Auth::user()->favorites
To find whether the current post is a favorite, use
$isFavorite = Auth::user()->favorites->has($post->id);

Categories