Laravel сохраняет / обновляет отношения многие ко многим


89

Может ли кто-нибудь помочь мне спасти отношения "многие-ко-многим"? У меня есть задачи, у пользователя может быть много задач, а у задачи может быть много пользователей (от многих ко многим). Я хочу добиться того, чтобы в форме обновления администратор мог назначить нескольких пользователей для конкретной задачи. Это делается с помощью ввода множественного выбора html

name="taskParticipants[]"

Уловка здесь в том, что через ту же форму (ввод) вы можете добавлять / удалять пользователей, поэтому я должен использовать sync (). Может, мне стоит начать с самого начала, но я не знаю, с чего начать ...

Это моя модель пользователя:

public function tasks()
{
    return $this->belongsToMany('Task','user_tasks');
}

Модель задачи

public function taskParticipants()
{
    return $this->belongsToMany('User','user_tasks');
}

TaskController

public function update($task_id)
{
    if (Input::has('taskParticipants'))
    {
        foreach(Input::get('taskParticipants') as $worker)
        {
            $task2 = $task->taskParticipants->toArray();
            $task2 = array_add($task2,$task_id,$worker);
            $task->taskParticipants()->sync(array($task2));
        }
    }
}

Это структура таблиц id | title | deadline

user_tasks
id|task_id|user_id

Я обновил свой код. ссылка
SuperManSL

4
$workers = Input::get('taskParticipants'); $task->taskParticipants()->sync($workers);и это все, что вам нужно, если вы перейдете из этой формы всех пользователей, назначенных задаче.
Jarek Tkaczyk

@JarekTkaczyk Спасибо, это было волшебно.
Ryu_hayabusa

Ответы:


200

tldr; Используйте syncсо вторым параметромfalse


Отношения многие-ко-многим есть belongsToManyв обеих моделях:

// Task model
public function users()
{
  return $this->belongsToMany('User', 'user_tasks'); // assuming user_id and task_id as fk
}

// User model
public function tasks()
{
  return $this->belongsToMany('Task', 'user_tasks');
}

Чтобы добавить новое отношение, используйте attachили sync.

Разница между ними:

1 attach добавит новую строку в сводную таблицу, не проверяя, есть ли она там. Хорошо, когда у вас есть дополнительные данные, связанные с этим отношением, например:

Userи Examсвязан со сводной таблицейattempts: id, user_id, exam_id, score

Полагаю, это не то, что вам нужно в вашей ситуации:

$user->tasks()->getRelatedIds(); // [1,2,3,4,5,6]

$user->tasks()->attach([5,6,7]);
// then
$user->tasks()->getRelatedIds(); // [1,2,3,4,5,6,5,6,7]

2, sync с другой стороны, либо удалит все отношения, либо установит их заново:

$user->tasks()->getRelatedIds(); // [1,2,3,4,5,6]

$user->tasks()->sync([1,2,3]);
// then
$user->tasks()->getRelatedIds(); // [1,2,3]

или он установит новые отношения без отсоединения предыдущего И без добавления дубликатов:

$user->tasks()->sync([5,6,7,8], false); // 2nd param = detach
// then
$user->tasks()->getRelatedIds(); // [1,2,3,4,5,6,7,8]

8
Было бы неплохо, если бы это было задокументировано в основной документации, а не в документации по API! Раскачать. +1.
ceejayoz

Мне очень нравится второе решение с синхронизацией и вторым параметром. Как я уже сказал в комментарии ниже, я не могу позволить себе не использовать отсоединение. История в том, что администратор может назначать задачи пользователям. Он выбирает пользователей из раскрывающегося списка (несколько), поле - участники []. Итак ...: Шаг 1: администратор назначает задачу A трем пользователям (ваш метод работает, у нас есть 3 записи в БД) Шаг 2: администратор обновляет задачу A и добавляет двух пользователей (ваш метод работает, у нас 5 записей в БД) Шаг 3: администратор обновляет задачу A и удаляет 1 пользователя (ваш метод не работает, у нас все еще есть 5 пользователей вместо 4) мой метод обновления мой код
SuperManSL

1
Вы можете упростить свой запрос о взаимоотношениях, просто $this->belongsToMany('User')используя имя таблицы в алфавитном порядке и в единственном числе (то есть task_userвместо user_tasks)
Куша

@Rok, если вы всегда передаете массив всех связанных пользователей, используйте syncс отсоединением, не беспокойтесь. Я предлагаю использовать второй параметр со значением false, когда вы хотите «добавить новую задачу для пользователя» или «назначить пользователя для задачи», когда вы передаете одну idиз связанных моделей.
Jarek Tkaczyk

1
@FabioAntunes syncвозвращает массив detached, attachedи updatedсписки. attachничего не возвращает, но в обоих случаях вы получите исключение, если что-то неожиданное случится в вызовах db.
Jarek Tkaczyk

109

Вот мои заметки о том, как сохранять и обновлять все отношения Eloquent.

в один к одному :

Вы должны использовать HasOne на первой модели и BelongsTo на второй модели.

для добавления записи по первой модели ( HasOne ) используйте  функцию сохранения

пример:    $post->comments()->save($comment);

чтобы добавить запись во вторую модель ( BelongsTo ), используйте  ассоциированную функцию

пример:    $user->account()->associate($account);    $user->save();


от одного ко многим :

Вы должны использовать HasMany на первой модели и BelongsTo на второй модели.

чтобы добавить запись на первые таблицы ( HasMany ) использовать сохранить  или saveMany функции

пример:    $post->comments()->saveMany($comments);

чтобы добавить запись во вторую модель ( BelongsTo ), используйте  ассоциированную функцию

пример:    $user->account()->associate($account);    $user->save();


во многих для многих :

Вы должны использовать  BelongsToMany на первой модели и BelongsToMany на второй модели.

добавлять записи об использовании сводной таблицы присоединять или синхронизации функции

  • обе функции принимают один идентификатор или массив идентификаторов 

  • разница в том, что прикрепление проверяет, существует ли уже запись в сводной таблице, а синхронизация - нет.

пример: $user->roles()->attach($roleId);


в полиморфном от одного ко многим :

Вы должны использовать  MorphMany на основной модели и  MorphTo на всех (*** способных) моделях.

для добавления записей по всем остальным моделям используйте  сохранение

пример:    $course->tags()->save($tag);

сводная таблица должна иметь следующие столбцы:

. ID основной модели

. (*** в состоянии) ID

. (*** в состоянии) Тип


в полиморфном Многие ко многим :

Вы должны использовать  MorphByMany на основной модели и  MorphToMany на всех (*** способных) моделях

для добавления записей по всем остальным моделям используйте save или saveMany

пример:    $course->tags()->save($tag);

пример:    $course->tags()->saveMany([$tag_1, $tag_2, $tag_3]);

сводная таблица должна иметь следующие столбцы:

. ID основной модели

. (*** в состоянии) ID

. (*** в состоянии) Тип


in Has Many Through (ярлык):

Вы должны использовать HasManyThrough в первой таблице и иметь нормальные отношения в двух других таблицах

это не работает для отношений ManyToMany (где есть сводная таблица)

однако для этого есть хорошее и простое решение.


Вот статья, которую я написал, вдохновленный этим ответом. Важно это проверить: https://hackernoon.com/eloquent-relationships-cheat-sheet-5155498c209


Я на самом деле разместил этот ответ, когда у них уже было более 40 лайков на правильный ответ, но да, я знаю, насколько это полезно для меня, рад, что вам это нравится :)
Махмуд Залт

1
Я хочу, чтобы вы знали, что вы спаситель
Chay22 09

1
это отличный ответ.
caro

1
как обновить отношение к маю. Вы объяснили, как добавить. не могли бы вы также объяснить обновление? есть ли способ обновить такие записи updateMany? спасибо
Hamidreza

4

syncWithoutDetaching([$id_one, $id_two, $id_three]);это то, что вы ищете. На самом деле он делает то, что делает [ syncсо вторым параметром false]!


0

syncФункция стирает покидающих отношения и делает ваш массив весь список отношений. attachВместо этого вы хотите добавить отношения, не удаляя другие.


Я не могу использовать прикрепление, потому что я использую этот код внутри метода обновления. Суть в том, что администратор может обновить задачу и заполнить участников ввода [] пользователями, которые будут участвовать в задаче. Поэтому мне нужно проверить, существует ли он, и удалить (или не добавлять новую запись) его, или, если он не существует, добавить его.
SuperManSL
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.