Laravel & Eloquent - Query 'Friends' Relationship - laravel

Lets say you have User and Friend Schemas
user:
- id
- username
friends:
- id
- user_id
- friend_id
A 'friendship' is determined by having two reciprocal records. And lets says if it's just a one way relationship, this is a pending request. So in the friends table:
user_id: 5 | friend_id: 6
user_id: 6 | friend_id 5
In the User Model, you would then have:
public function friends()
{
return $this->belongsToMany(self::class,'friends','user_id','friend_id')->withTimestamps();
}
However, this isn't the whole story because this doesn't check for the presence of the mutual relationship.
How would you set this up? Should the friends() method be responsible for for querying the data further? Or is this method appropriate, and instances where you want to get a mutual friendship, you should append a query scope? For example
$this->user->friends->mutual()
OR
$this->user->friends->pending()

Ended up figuring this out based on this answer. However, i changed the merge() functionality to intersect() because I'm not use the approved flag to determine weather the friendship is mutual. I'm only using the presence of the two relationships.
So in my case. the relations are:
public function myFriends()
{
return $this->belongsToMany(self::class,'friends','user_id','friend_id')->withTimestamps();
}
public function friendsOf()
{
return $this->belongsToMany(self::class,'friends','friend_id','user_id')->withTimestamps();
}
And the other logic is:
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)) $this->setRelation('friends', $this->mergeFriends());
}
protected function mergeFriends()
{
return $this->myFriends->intersect($this->friendsOf);
}

Related

hasOneThrough and hasManyThrough not working in Eloquent

I have the following tables
users userdatas workdays
--------- ----------- --------
id id id
user_id
workday_id
I would like to get a user's workdays (and later get all the users that have a specified workday, but one step at a time). I attempted several variations of this however it's not working. I believe the issue could be that workdays doesn't reference userdatas.
In the User model:
public function workday() {
return $this->hasOneThrough('App\Workday', 'App\Userdata');
}
I've also defined the following:
In Userdata
public function workday() {
return $this->hasOne('App\Workday');
}
In Workday
public function userdatas() {
return $this->belongsToMany('App\Userdata');
}
Does the reverse through relationship have to be defined in Workday? Any tips to get this to work?
Thank you!
You're following wrong relationship. As per your Database table design it should be Many to many relationship(belongsToMany).
In User Model pass the relationship as below.
public function workdays() {
return $this->belongsToMany('App\Workday', 'userdatas','user_id','workday_id');
}

Laravel returns a Collection with duplicates of the first model

I'm developing a Laravel 5.7 (API) application with a PostgreSQL database behind it. The relevant Models are: User (customers and employees), Car, and Request.
An employee User creates a Request for a Car, that belongs to a customer User.
The relationships are:
Car (as customer) : User = n:m
Car : Request = 1:n
User : Request (as employee) = 1:n
(The data design is suboptimal, to put it mildly, but anyway, it's the given reality for now.)
Now to the actual issue. I want to display all Requests of a customer User:
Request::query()
->join('user_car', 'user_car.car_id', '=', 'request.car_id')
->join('user', 'user.id', '=', 'user_car.user_id')
->where('user.id', '=', $customer->id)
->select()
->get();
The customer with the given $customer->id has n Requests. And the length of the result Collection of the call above is correct. But all these n entries are duplicates of the first one. Means: I'm getting a list with n instances of Request#1.
Why does the first call return a list of references to the same Model object? Is it a (known) bug?
ADDITIONAL INFORMATION
Relationships:
class User extends \Illuminate\Foundation\Auth\User
{
// ...
public function cars()
{
return $this->belongsToMany('App\Car', 'user_car')->withTimestamps();
}
public function requests()
{
return $this->hasMany(Request::class, 'user_id');
}
}
class Car extends Model
{
// ...
public function users()
{
return $this->belongsToMany('App\User', 'user_car')->withTimestamps();
}
public function requests()
{
return $this->hasMany(Request::class);
}
}
class Request extends Model
{
// ...
public function car()
{
return $this->belongsTo(Car::class);
}
public function user()
{
return $this->belongsTo(User::class);
}
}
The query is correct.
I logged the database requests, got the generated statement
SELECT *
FROM "request"
INNER JOIN "user_car" ON "user_car"."car_id" = "request"."car_id"
INNER JOIN "user" ON "user"."id" = "user_car"."user_id"
WHERE "user"."id" = 1;
..., and executed it manually. The result table contains as expected n different entries.
NOT just references
The result Collection's entries instances references to the different objects:
$test1 = $resultCollection->first();
$test2 = $resultCollection->last();
$test3 = spl_object_hash($test1);
$test4 = spl_object_hash($test2);
Xdebug output:
$test3 = "0000000077505ccd000000007964e0a8" <-- ccd0
$test4 = "0000000077505c33000000007964e0a8" <-- c330
Workaround
I found a workaround. This call
Request::whereIn('car_id', $customer->cars()->pluck('id')->toArray())->get();
... retrieves the correct/expected set of model.
First, note that your object hashes are not actually identical, and you're likely dealing with two separate instances.
What you're likely experiencing is an issue with ambiguous column names. When you JOIN together multiple tables, any matching/duplicate column names will contain the value of the last matching column. Your SQL GUI/client usually separates these. Unfortunately Laravel doesn't have a prefixing mechanism, and just uses an associative array.
Assuming all of your tables have a primary key column of id, every Request object in your result set will likely have the same ID - the User's ID you pass in the WHERE condition.
You can fix this in your existing query by explicitly selecting the columns you need to prevent ambiguity. Use ->select(['request.*']) to limit the returned info to the Request object data.

Laravel ORM relationship - manager-employee over the same table of users

I have a user_managers pivot table that gets both keys from the users table:
employer_user_id
employee_user_id
I believe users would have a many to many relationship as a user can be managed by more than 1 manager there will be users who manage 1 or more users, while other users (excluding those under them) would manage them while there will be users who don't manage at all and a User can have only 1 manager.
My first try at defining this was to build another model named Manager representing the user_managers pivot table, so in User model I wrote the following 2 functions:
public function managedBy()
{
return $this->belongsToMany('Manager', 'employer_user_id');
}
public function manages()
{
return $this->hasMany('Manager', 'employee_user_id', 'employer_user_id');
}
Does this make sense or do you know of a better way to implement this kind of structure?
If a user can have only 1 manager then you can define you relationship as one to many like
//User model
public function managedBy()
{
return $this->belongsTo(User::class, 'manager_id');
}
public function managees()
{
return $this->hasMany(User::class, 'manager_id');
}
you don't need to pass $id to your relationship definition.
For multiple managers, Yes you would need a many to many relationship by adding a junction/pivot table which i guess you already have user_managers, Now you need to define your relationships using belongsToMany for managers and mangees like
public function managers()
{
$this->belongsToMany(User::class, 'user_managers', 'employer_user_id')
}
public function managees()
{
$this->belongsToMany(User::class, 'user_managers', 'employee_user_id')
}

How to setup conditional relationship on Eloquent

I have this (simplified) table structure:
users
- id
- type (institutions or agents)
institutions_profile
- id
- user_id
- name
agents_profile
- id
- user_id
- name
And I need to create a profile relationship on the Users model, but the following doesn't work:
class User extends Model
{
public function profile()
{
if ($this->$type === 'agents')
return $this->hasOne('AgentProfile');
else
return $this->hasOne('InstitutionProfile');
}
}
How could I achieve something like that?
Lets take a different approach in solving your problem. First lets setup relationship for the various models respectively.
class User extends Model
{
public function agentProfile()
{
return $this->hasOne(AgentProfile::class);
}
public function institutionProfile()
{
return $this->hasOne(InstitutionProfile::class);
}
public function schoolProfile()
{
return $this->hasOne(SchoolProfile::class);
}
public function academyProfile()
{
return $this->hasOne(AcademyProfile::class);
}
// create scope to select the profile that you want
// you can even pass the type as a second argument to the
// scope if you want
public function scopeProfile($query)
{
return $query
->when($this->type === 'agents',function($q){
return $q->with('agentProfile');
})
->when($this->type === 'school',function($q){
return $q->with('schoolProfile');
})
->when($this->type === 'academy',function($q){
return $q->with('academyProfile');
},function($q){
return $q->with('institutionProfile');
});
}
}
Now you can access your profile like this
User::profile()->first();
This should give you the right profile. Hope it helps.
you can do this by use another method please check this:
a blog Post and Video model could share a polymorphic relation to a
Tag model. Using a many-to-many polymorphic relation allows you to
have a single list of unique tags that are shared across blog posts
and videos. First, let's examine the table structure:
https://laravel.com/docs/5.4/eloquent-relationships#many-to-many-polymorphic-relations
Looks like that should be $this->type rather than $this->$type - since type is a property, not a variable.

Correct relationship in Laravel

I have four tables in database: groups, specialties, lessons, group_lesson. It's structures:
groups
id
specialty_id
name
specialties
id
name
lessons
id
specialty_id
group_lesson (UNIQUE INDEX lesson_id, group_id)
lesson_id
group_id
date
My models look like that for now:
class Group extends Eloquent {
public function specialty() {
return $this->belongsTo('Specialty');
}
}
class Lesson extends Eloquent {
public function specialty() {
return $this->belongsTo('Specialty');
}
}
class Specialty extends Eloquent {
public function lessons() {
return $this->hasMany('Lesson');
}
public function groups() {
return $this->hasMany('Group');
}
}
I need get additional fields in Group model look like that
Group - Eloquent model
name - string
lessons - collection of Lesson models of Group Specialty
date - date from group_lesson table
I've tried different relationships and combinations, but it's doesn't work. Please help me to write correct relationships.
You can use eager-loading to access relational data through relationships, and can even chain relationships further. As a rule of thumb, if you can draw a path to from 1 model to another through a relationship, you can eagerload all the relevant and relational data for that with chained eager-loads.
Laravel Eager Loading
As an example
$speciality_group = Speciality::with('group','lessons')->find($id);
Even though you are only getting a single instance of the speciality model, the related data is hasMany, meaning multiple records. You need to loop through these records using a foreach loop to access the relevant data for them, or alternitavely add additional closures in your initial query to load only a single related model.
foreach($speciality_group->group as $group)
{
echo $group->name;
}
You will need to do this for both instances where you want to display related information.

Resources