Я столкнулся с той же проблемой довольно давно, и поэтому внимательно следил за этой проблемой и провел много исследований.Я сталкивался с некоторыми решениями, которые вы также нашли, и с некоторыми другими, а также думал о других решениях, которые я суммировал здесь , в основном о том, как получить оба user_ids в одном столбце.Боюсь, что все они не будут работать хорошо.Я также боюсь, что использование любых пользовательских классов помешает вам использовать все удобные функции отношений Laravel (особенно нетерпеливую загрузку).Поэтому я все еще думал, что можно сделать, и, пока не появится функция hasMany для многих столбцов, я думаю, что вчера я нашел возможное решение.Сначала я покажу его, а затем применю к вашему проекту.
Мой проект
Первоначальное решение
В моем проекте один пользователь сотрудничает с другим (= партнерство) ипотом будет назначена комиссия.Итак, у меня были следующие таблицы:
USERS
id | name
---------|------
1 | foo
2 | bar
17 | baz
20 | Joe
48 | Jane
51 | Jim
PARTNERSHIPS
id | partner1 | partner2 | confirmed | other_columns
----|-----------|-----------|-----------|---------------
1 | 1 | 2 | 1 |
9 | 17 | 20 | 1 |
23 | 48 | 51 | 1 |
Поскольку у каждого пользователя всегда должно быть только одно активное партнерство, неактивное, удаляемое мягко, я мог бы помочь себе, просто дважды воспользовавшись функцией hasMany
:
//user.php
public function partnerships()
{
$r = $this->hasMany(Partnership::class, 'partner1');
if(! $r->count() ){
$r = $this->hasMany(Partnership::class, 'partner2');
}
return $r;
}
Но если бы я хотел найти все партнерские отношения пользователя, текущего и прошлого, это, конечно, не сработало бы.
Новое решение
Вчера я предложил близкое к вам решение об использовании сводной таблицы, но с небольшой разницей в использовании другой таблицы:
USERS
(same as above)
PARTNERSHIP_USER
user_id | partnership_id
--------|----------------
1 | 1
2 | 1
17 | 9
20 | 9
48 | 23
51 | 23
PARTNERSHIPS
id | confirmed | other_columns
----|-----------|---------------
1 | 1 |
9 | 1 |
23 | 1 |
// user.php
public function partnerships(){
return $this->belongsToMany(Partnership::class);
}
public function getPartners(){
return $this->partnerships()->with(['users' => function ($query){
$query->where('user_id', '<>', $this->id);
}])->get();
}
public function getCurrentPartner(){
return $this->partnerships()->latest()->with(['users' => function ($query){
$query->where('user_id', '<>', $this->id);
}])->get();
}
// partnership.php
public function users(){
return $this->belongsToMany(User::class);
}
Конечно, это связано с тем недостатком, что вам всегда нужно создавать и поддерживать два входа в сводной таблице, но я думаю, что это случайная дополнительная нагрузка для базы данных - как часто это будет изменяться в любом случае?- предпочтительнее иметь два запроса на выборку по двум столбцам каждый раз (и из вашего примера казалось, что вы все равно дублировали записи в таблице друзей).
Применительно к вашему проекту
В вашем примере таблицы могут быть структурированы следующим образом:
USERS
id | name
---------|------
1 | foo
2 | bar
3 | baz
FRIENDSHIP_USER
user_id | friendship_id
---------|------
1 | 1
2 | 1
3 | 2
1 | 2
FRIENDSHIPS
id |send_id* | receive_id* | is_blocked | [all the other nice stuff
--------|---------|-------------|------------|- you want to save]
1 | 1 | 2 | 0 |
2 | 3 | 1 | 0 |
[*send_id and receive_id are optional except
you really want to save who did what]
Редактировать: My $user->partners()
выглядит так:
// user.php
// PARTNERSHIPS
public function partnerships(){
// 'failed' is a custom fields in the pivot table, like the 'is_blocked' in your example
return $this->belongsToMany(Partnership::class)
->withPivot('failed');
}
// PARTNERS
public function partners(){
// this query goes forth to partnerships and then back to users.
// The subquery excludes the id of the querying user when going back
// (when I ask for "partners", I want only the second person to be returned)
return $this->partnerships()
->with(['users' => function ($query){
$query->where('user_id', '<>', $this->id);
}]);
}