#laravel #many-to-many
#laravel #многие ко многим
Вопрос:
Я работаю над реализацией дружбы в своем проекте, но в дополнение к добавлению друзей я хотел бы иметь функцию приглашения, как в Facebook, я использую подход «многие ко многим», ссылающийся на себя,
схема сводной таблицы:
Schema::create('user_friends', function (Blueprint $table) {
$table->primary(['user_id', 'friend_id']);
$table->unsignedInteger('user_id');
$table->unsignedInteger('friend_id');
$table->timestamps();
$table->timestamp('accepted_at')->nullable()->default(null);
$table->timestamp('refused_at')->nullable()->default(null);
$table->foreign('user_id')
->references('id')->on('users')
->onDelete('cascade');
$table->foreign('friend_id')
->references('id')->on('users')
->onDelete('cascade');
});
Я делю друзей / будущих друзей на 3 группы:
- Подтвержденные друзья, и в эту категорию входят друзья, которых я приглашаю, и друзья, которые приглашают меня;
- Приглашенные: те, кого я приглашаю, но не одобрил;
- Приглашающие: те, кто приглашает меня, но я еще не говорю «да».
Ниже приведен их соответствующий код:
Группа 1: друзья:
class User {
//
/**
* Friendships that started by the user
*/
public function friendsOfMine()
{
return $this->belongsToMany('AppUser', 'user_friends', 'user_id', 'friend_id')
->withPivot(['accepted_at', 'refused_at'])
->wherePivot('accepted_at', '<>', null)
->withTimestamps();
}
/**
* Friendships that started by others
*/
public function friendOfOthers()
{
return $this->belongsToMany('AppUser', 'user_friends', 'friend_id', 'user_id')
->withPivot(['accepted_at', 'refused_at'])
->wherePivot('accepted_at', '<>', null)
->withTimestamps();
}
// 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->friendOfOthers);
}
Группа 2: Приглашенные:
/**
* Users that are invited as friends by this user.
*/
public function invitees()
{
return $this->belongsToMany('AppUser', 'user_friends', 'user_id', 'friend_id')
->withPivot(['accepted_at', 'refused_at'])
->wherePivot('accepted_at', null)
->withTimestamps();
}
Группа 3: инвайтеры:
/**
* Users that invite this user as friend.
*/
public function invitors()
{
return $this->belongsToMany('AppUser', 'user_friends', 'friend_id', 'user_id')
->withPivot(['accepted_at', 'refused_at'])
->wherePivot('accepted_at', null)
->withTimestamps();
}
и затем, когда я хочу пригласить нового пользователя, я хочу убедиться, что этот новый пользователь не принадлежит к этим 3 группам:
public function inviteFriend(User $user)
{
if (!$this->is($user) amp;amp;
!$this->friends->contains($user) amp;amp;
!$this->invitees->contains($user) amp;amp;
!$this->invitors->contains($user)) {
$this->invitees()->attach($user);
}
}
Затем возникает странная проблема, я использовал dd() для проверки хода выполнения и подтверждения того, что условие выполнено и
$this->invitees()->attach($user);
был выполнен, но, однако, $user не был добавлен правильно,
но если я удалю эту самую проверку одного условия (а не другого)
!$this->invitees->contains($user)
и функция становится
public function inviteFriend(User $user)
{
if (!$this->is($user) amp;amp;
!$this->friends->contains($user) amp;amp;
// !$this->invitees->contains($user) amp;amp;
!$this->invitors->contains($user)) {
$this->invitees()->attach($user);
}
}
Тогда это будет работать нормально, но, тем не менее, тогда можно будет повторно приглашать одного и того же человека, а затем вызывать нарушение ограничений SQL.
Кто-нибудь может мне помочь в этом? Заранее благодарю вас!!!
Ответ №1:
Вы можете использовать syncWithoutDetaching вместо функции attach :
public function inviteFriend(User $user)
{
if (!$this->is($user) amp;amp;
!$this->friends->contains($user) amp;amp;
!$this->invitors->contains($user)) {
$this->invitees()->syncWithoutDetaching([$user->id]);
}
}
эта функция прикрепляет идентификатор пользователя, если он не существует
Комментарии:
1. Спасибо за ваш ответ, я попробовал ваше предложение, но оно дает тот же результат, что и мой исходный код.
Ответ №2:
После дополнительных поисков я, наконец, нашел обходной путь для решения моей проблемы, и я думаю, что этот подход может быть лучше, чем мой первоначальный, с точки зрения эффективности, поскольку collection->contains()
кажется излишним для такого рода ситуаций.
if (!$this->is($user) amp;amp;
!$this->friendsOfMine()->wherePivot('friend_id', $user->id)->exists() amp;amp;
!$this->friendOfOthers()->wherePivot('user_id', $user->id)->exists() amp;amp;
!$this->invitees()->wherePivot('friend_id', $user->id)->exists() amp;amp;
!$this->invitors()->wherePivot('user_id', $user->id)->exists()) {
$this->invitees()->attach($user);
}