Laravel one to many get data - php

So I'm very bad with these relationships in Laravel and I cant get my one to many relationship to work. Lets say I have a friend system like this:
Table Users:
id | username | password
Table Friends:
id | person1(int) | person2(int)
person1 and person 2 are the ID's of a user in the table Users
Now I have these 2 models:
class Friend extends Eloquent {
protected $table = 'friends';
public function friend_relations(){
return $this->belongsTo('User');
}
}
and:
class User extends Eloquent {
protected $table = 'users';
}
Now what I am doing is, after logging in, I want to show friends on the home page with the ID of the person logging in. So to get this I'd have it run like this:
$friends = Friend::where('person1', $id)->get();
Now this will give me the ID's of both of the persons, now I want to get the username that belongs to that user without running unnecessary extra queries so I would use a one to many relationship. However I cant seem to get the query to instantly get the name of the friends of the user logged in.
I tried something like:
$friends = Friend::where('person1', $id)->friend_relations;
and:
$friends = Friend::all()->friend_relations()->where('person1',$id)->get();
gives me: Call to undefined method friend_relations()
but this will give me the error: Undefined property friend_relations.
What am I doing wrong?

Your relationships are a little off because you may be thinking about this one a little wrong.
Even though it may not look like it, this is a belongs to many relationship and your friends table is actually a pivot table relating your users table to itself.
To further simplify this, you should rename the columns in your friends table so it's more clear what is what. person1 and person2 are incredibly vague and will only serve to confuse. With that said, I'm going to assume the two columns are user_id and friend_id. If you wish to not do this, just assume when I say user_id, I mean person1 and friend_id would map to your person2.
With all that said, this becomes a fairly simple problem. Add the following to your User model.
public function friends()
{
return $this->belongsToMany(User::class, 'friends', 'user_id', 'friend_id');
}
Now in order to retrieve a user with their friends, you can do the following.
$user = User::with('friends')->find($id);
$friends = $user->friends; // This will return a collectino of friends.
Additionally, your Friend model becomes unnecessary so you can feel free to delete that.
Edit:
Now $friends is going to be a Collection (you can think of this as a sort of array) of User objects. There are going to be 0, 1 or many User objects inside the Collection so it doesn't make any sense to try to use $friends->date because it doesn't know which User's date within that collection to grab from.
You will need to loop through it to access each User's properties.
foreach($friends as $friend) {
echo 'Name: ' . $friend->name;
echo 'Date: ' . $friend->date;
}

Related

retrieve child object by evaluating grandchildren object in laravel

I have 2 models, User and Conversation related to each other with a many-to-many relationship, many users to many conversations.
On my models I have:
User:
public function conversation() {
return $this->belongsToMany('App\Models\Conversation');
}
Conversation:
public function users() {
return $this->belongsToMany('App\Models\User');
}
So I could get the conversations a user has by : $user->conversation; and vice versa, retrieve the users a conversation has by $conversation->users, works like a charm. The problem is that I want a way to retrieve all users a certain user has made contact before, in few words, something like:
$user = User::find(1);
$talkedUsers = $user->conversation->user.
And also a way to check all the conversation user 'x' has made with user 'y' ('if any')
$userX = User::find(1);
$userY = 2;
$talkedUser = $userX->conversation->where('user.id', '=', $userY);
Obviously, the code above won't work. I wish to know if something like this is possible to accomplish without adding a complex raw query, I hope is possible only using only QueryBuilder.
The problem is that I want a way to retrieve all users a certain user
has made contact before,
$user->conversation is actually a collection of conversations, it is better to use plural i.e. $user->conversations. Then you can use higher order messages to deal with multiple objects at once. See https://laravel.com/docs/8.x/collections#higher-order-messages for more information
$users = $user->conversations->flatMap->users->unique('id');
flatMap combines multiple User collections retrieved from multiple Conversation objects into one collection of User objects then to remove duplicate users, you can use unique('id')
And also a way to check all the conversation user 'x' has made with
user 'y' ('if any')
First you should query all conversations from x then filter only conversations that have relationships with y.
$userX = User::find($x);
$conversations = $userX->conversations()
->whereHas('users', function($query) use ($y) {
$query->where('users.id', $y);
})
->get();

Laravel eloquent relationship and pivot tables

Im experimenting with an Laravel application where I have users and teams. The tables looks a little bit like this (simplified):
users
id*
...
teams
id*
...
team_user
team_id*
user_id*
isLeader
confirmed
As you can see, a user can be part of a number of teams, and *he can also be appointed leader of a given team. A team can have multiple leaders.
The user model has the following relationships:
// Returns all the teams connected to the user and where the confirmed timestamp is set
public function teams()
{
return $this->belongsToMany(Team::class)->wherePivot('confirmed', '!=', null);
}
// Returns all the teams where the user is appointed team leader
public function teamleaderTeams()
{
return $this->belongsToMany(Team::class)->wherePivot('isLeader', '=', 1);
}
The team has:
public function confirmedUsers()
{
return $this->belongsToMany(User::class)->where('confirmed', '!=', null);
}
I need something that returns all the users that the user is team leader for. So if you are not a team leader the result would off course be empty, but if you are it should return all the users from all the teams where you are appointed leader.
Ive tried asking around, and have gotten some suggestions, but not really arrived at a solution.
I do kindof understand what I want (I think). Sooo... since you can tell which teams a user is team leader for through the teamleaderTeams() relation, I can loop through each and then ask to get all the confirmed users through the confirmedUsers() relation. But I've only managed to accomplish this in the controller and it just seems messy.
I.e. this only crashes the browser (it seems to be in an infinite loop or something, and I dont really understand why).
public function getLeaderForAttribute()
{
$users = collect();
foreach($this->teamleaderTeams as $team)
{
foreach ($team->confirmedUsers as $user) {
$users->add($user);
}
}
return $users->unique('id');
}
Anyway, anyone got a nice solution for a teamleaderUsers() relation (not really a good name for it), that returns all the users from all the teams that a given user is team leader for (thats a mouth full)?
I think this is a nice time to use Pivot models. You can define a pivot model by extending the Pivot class. Furturemore, you can define relationships in the pivot model. So, if you have users relationship in your pivot model, you can make a query like this:
TeamUser::with('users')->where('isLeader', 1); // If the pivot model is called TeamUser
Of course you can exclude a specific user as usual:
TeamUser::with(['users' => function($query) {
$query->where('id', '<>', 1); // If we want to exclude user with id 1
}])
->where('isLeader', 1);
Of course, you can also make an additional where clause in the relatonship:
public function teamLeaders()
{
return $this->hasMany('users')->where('isLeader', 1);
}
Please read more about it here and here is the API
Good luck!
First, you have a typo here:
$users->add($user);
It should be Collection::push:
$users->push($user);
Second, I think your approach is okay. However, if performance becomes your problem, you might want to write a custom query for optimization, rather than depending on Laravel ORM.
Third, you can name relations like this: leadingTeams instead of teamleaderTeams and leadingUsers instead of teamleaderUsers.

Laravel: get a model through another table

I have gotten a model called User. in this model i have a function called UserActivity where i return this:
return $this->hasMany('App\UserActivity', 'userid');
After this i have a function that gets the activity name from the Activity table, but i do not know how to do this, i have currently gotten this:
public function Activity() {
return $this->hasManyThrough('App\UserActivity', 'App\Activity', 'userid', 'activityid');
}
And in my view i want to use this like this:
$activiteiten = \App\User::find(Auth::user()->id);
dd($activiteiten->UserActivity()->Activity());
But then i get an error saying this:
Call to undefined method Illuminate\Database\Query\Builder::Activity() (View: /var/www/vhosts/cpned.nl/intranet.cpned.nl/laravel/resources/views/dashboard.blade.php)
I can do it using inner joins but i am really wondering if i can do this with Laravel models. I do not know how currently, and because i don't know what the name is for the function in laravel i can't find it either, so I am sorry if this will be a duplicate.
My tables have the following keys and foreign keys:
Users: pk: id
user_activities: pk: userid, activityid
activities: pk: id
Thank you in advance!
You can try using eager loading.
So something like:
$activiteiten = \App\User::with('UserActivity.Activity')->find(Auth::user()->id);
dd($activiteiten->UserActivity->Activity);
Edit: So then you can do something like this:
foreach($activiteiten->UserActivity as $user_activity) {
foreach($user_activity->Activity as $activity) {
print_r($activity);
}
}
Shows how you can loop through the relationships.
The Activity function should be in the User model. So then you can call it this way:
$activiteiten = \App\User::find(Auth::user()->id);
dd($activiteiten->Activity); //gives you a collection of activities
However, you need belongsToMany function rather than hasManyThrough since the relationship between users and activities is many-to-many.
This mean you cut down going through the Intermediate model. You can check the doc on belongsToMany to understand what to do better.
A side note: Why are your functions name starting with capital letter?
Also using hasMany relations should affect the function name as well, so that 'activity' becomes 'activities' (just to keep the functionality and interpretation in sync).
Update:
//User model
public function activities() {
return $this->belongsToMany('App\Activity', 'user_activity', 'userid', 'activityid');
}
This uses the 'user_activity' table, to find the relationship between users and activities, so that you may now access by User::first()->activities for example.

How to chain eloquent relations in laravel?

So far I was extracting the relation objects as arrays and then doing something like:
App\Model::find($id)
But however is there a way to do something like:
Auth::user()->group()->members()
It works until Auth::user()->group but no further chaining. Please help if you've done something. Or I'm just newbie.
You could use eager loading to load the user's group and then load all of the members of that group.
$user = User::with(['group', 'group.members'])->find(1);
// OR if you already have a user object (Like when using the Auth facade)
$user = Auth::user()->load(['group', 'group.members']);
foreach ($user->group->members as $member) {
// Do something with a member
}
However, if you essentially want to jump down the structure a level, and get all the members related to a user, you could use the hasManyThrough relationship, in that a user has many members, through a group.
// In your User model
public function members()
{
return $this->hasManyThrough(Member::class, Group::class);
}
That way you can simply access the members directly through the user:
$members = Auth::user()->members;
Instead of doing a query to access the user's group and then doing another query to access that group's members, Laravel would use a single query with a join to get you the members.
Take a look at the hasManyThrough relationship here
Try this
Auth::user()->group->members

Eloquent where owner equals follow

I am trying to make a twitter like feed in an application, I have a database called connections where inside there's user and follow and another database called feed containing owner which would equal to the follow column in connections.
What I could do if had every id of a follower statically is to use where('owner', '=' $follow) on each follower and return it.
I tried this approach but it wasn't ideal:
Get each follower inside connections;
foreach(follower) {
Get 10 of the latest posts orderBy "created_at";
Push into array;
}
shuffle array;
limit array to 15;
return array;
That also ended with the returned array not being ordered by created date.
How would I use eloquent to get the feed item only if the user follows the owner in the best/simplest way?
Are there any specific Laravel tools that can be used?
Also the database layout isn't fixed as it is, it can be altered if needed to better suite this.
You need to create a many-to-many relationship between the user and itself. See the laravel eloquent documentation http://laravel.com/docs/eloquent#relationships. I didn't test this code but it should be enough to get you started down the right track.
Create a pivot table called "user_following" with:
(int) id, (int) user_id, (int) following_id
Then do something like this:
<?php
// Model
class User extends Eloquent {
public function following()
{
return $this->belongsToMany('User', 'user_following', 'user_id', 'following_id');
}
public function tweets()
{
return $this->hasMany('Tweet')->orderBy('created_at', 'desc');
}
}
// controller
$tweetsOfWhoImFollowing = User::find($id)->following->tweets;

Categories