Laravel zapisuje / aktualizuje wiele do wielu relacji

85

Czy ktoś może mi pomóc, jak uratować związek wielu za wielu? Mam zadania, użytkownik może mieć wiele zadań, a zadanie może mieć wielu użytkowników (od wielu do wielu). Chcę tylko, aby w formularzu aktualizacji administrator mógł przypisać wielu użytkowników do określonego zadania. Odbywa się to za pomocą wielokrotnego wyboru html

name="taskParticipants[]"

Problem polega na tym, że za pomocą tego samego formularza (danych wejściowych) można dodawać / usuwać użytkowników, dlatego muszę używać sync (). Może powinienem zacząć od początku, ale nie wiem od czego zacząć ...

To jest mój model użytkownika:

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

Model zadań

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));
        }
    }
}

To jest struktura tabel id | tytuł | termin realizacji zadań

user_tasks
id|task_id|user_id
SuperManSL
źródło
Zaktualizowałem swój kod. link
SuperManSL,
4
$workers = Input::get('taskParticipants'); $task->taskParticipants()->sync($workers);i to wszystko, czego potrzebujesz, o ile przekażesz z tego formularza wszystkich użytkowników przydzielonych do zadania.
Jarek Tkaczyk
@JarekTkaczyk Dzięki, to było magiczne.
Ryu_hayabusa,

Odpowiedzi:

191

tldr; Używaj syncz drugim parametremfalse


Relacja wiele do wielu występuje belongsToManyw obu modelach:

// 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');
}

Aby dodać nową relację, użyj attachlub sync.

Różnica między nimi to:

1 attach doda nowy wiersz do tabeli przestawnej bez sprawdzania, czy już tam jest. Dobrze, gdy masz dodatkowe dane powiązane z tą relacją, na przykład:

Useri Exampołączone z tabelą przestawnąattempts: id, user_id, exam_id, score

Przypuszczam, że nie tego potrzebujesz w swojej sytuacji:

$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 z drugiej strony usunie wszystkie relacje i utworzy je od nowa:

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

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

lub utworzy nowe relacje bez odłączania poprzedniego AND bez dodawania duplikatów:

$user->tasks()->sync([5,6,7,8], false); // 2nd param = detach
// then
$user->tasks()->getRelatedIds(); // [1,2,3,4,5,6,7,8]
Jarek Tkaczyk
źródło
8
Byłoby miło, gdyby zostało to udokumentowane w głównej dokumentacji, a nie w dokumentacji API! Rock on. +1.
ceejayoz,
Bardzo podoba mi się drugie rozwiązanie z synchronizacją i drugim parametrem. Jak powiedziałem w komentarzu poniżej, nie mogę sobie pozwolić na to, aby nie używać odłączania. Historia polega na tym, że administrator może przydzielać zadania użytkownikom. Wybiera użytkowników z listy rozwijanej (wielu), pole to uczestnicy []. Więc ...: Krok 1: administrator przydziela zadanie A trzem użytkownikom (twoja metoda działa, mamy 3 rekordy w DB) Krok 2: administrator aktualizuje zadanie A i dodaje dwóch użytkowników (twoja metoda działa, mamy 5 rekordów w DB) Krok 3: Administrator aktualizuje zadanie A i usuwa 1 użytkownika (Twoja metoda nie powiodła się, nadal mamy 5 użytkowników zamiast 4) moja metoda aktualizacji mój kod
SuperManSL
1
Możesz uprościć swoje zapytanie dotyczące relacji, $this->belongsToMany('User')jeśli użyjesz nazwy tabeli alfabetycznie i liczby pojedynczej ( task_usera więc zamiast user_tasks)
Kousha
@Rok, jeśli zawsze przekazujesz tablicę wszystkich powiązanych użytkowników, użyj syncz odłączaniem, bez obaw. Sugeruję użycie drugiego parametru ustawionego na false, gdy chcesz „dodać nowe zadanie dla użytkownika” lub „przypisać użytkownika do zadania”, gdy przekazujesz jeden idz powiązanego modelu.
Jarek Tkaczyk
1
@FabioAntunes synczwraca tablicę detached, attachedi updatedlist. attachnic nie zwraca, ale w obu przypadkach wystąpiłby wyjątek, gdyby coś nieoczekiwanego wydarzyło się w wywołaniach bazy danych.
Jarek Tkaczyk
107

Oto moje notatki o tym, jak zapisywać i aktualizować wszystkie elokwentne relacje.

w jeden do jednego :

Musisz użyć HasOne na pierwszym modelu i BelongsTo na drugim modelu

aby dodać rekord na pierwszym modelu ( HasOne ) użyj  funkcji zapisu

przykład:    $post->comments()->save($comment);

aby dodać rekord w drugim modelu ( BelongsTo ), użyj  funkcji skojarzonej

przykład:    $user->account()->associate($account);    $user->save();


w jeden do wielu :

Musisz użyć HasMany na pierwszym modelu i BelongsTo na drugim modelu

aby dodać na pierwszej tabeli ( hasMany ) użyć Zapisz  lub saveMany funkcje

przykład:    $post->comments()->saveMany($comments);

aby dodać rekord w drugim modelu ( BelongsTo ), użyj  funkcji skojarzonej

przykład:    $user->account()->associate($account);    $user->save();


w Wiele do wielu :

Musisz użyć  BelongsToMany w pierwszym modelu i BelongsToMany w drugim modelu

aby dodać rekordy do tabeli przestawnej, użyj funkcji dołączania lub synchronizacji

  • obie funkcje akceptują pojedynczy identyfikator lub tablicę identyfikatorów 

  • różnica polega na tym, że dołączanie sprawdza, czy rekord już istnieje w tabeli przestawnej, a synchronizacja nie

przykład: $user->roles()->attach($roleId);


w Polimorficznym jeden do wielu :

Musisz użyć  MorphMany na głównym modelu i  MorphTo na wszystkich (*** w stanie) modelach

aby dodać rekordy we wszystkich innych modelach, użyj opcji  save

przykład:    $course->tags()->save($tag);

tabela przestawna powinna mieć następujące kolumny:

. główny identyfikator modelu

. (*** w stanie) ID

. (*** w stanie) Wpisz


w Polimorficznym Wiele do wielu :

Musisz użyć  MorphByMany na głównym modelu i  MorphToMany na wszystkich (*** w stanie) modelach

aby dodać rekordy we wszystkich innych modelach, użyj opcji save lub saveMany

przykład:    $course->tags()->save($tag);

przykład:    $course->tags()->saveMany([$tag_1, $tag_2, $tag_3]);

tabela przestawna powinna mieć następujące kolumny:

. główny identyfikator modelu

. (*** w stanie) ID

. (*** w stanie) Wpisz


w Ma wiele do przejścia (skrót):

Musisz użyć HasManyThrough na pierwszej tabeli i mieć normalne relacje na pozostałych 2 tabelach

to nie działa w przypadku relacji ManyToMany (gdzie jest tabela przestawna)

jednak jest na to ładne i łatwe rozwiązanie.


Oto artykuł, który napisałem, zainspirowany tą odpowiedzią. Ważne, aby to sprawdzić: https://hackernoon.com/eloquent-relationships-cheat-sheet-5155498c209

Mahmoud Zalt
źródło
Właściwie umieściłem tę odpowiedź, gdy mieli już ponad 40 polubień przy poprawnej odpowiedzi, ale tak, wiem, jak przydatne jest to dla mnie, cieszę się, że ci się podoba :)
Mahmoud Zalt
1
Chciałbym, żebyś wiedział, że jesteś zbawicielem
Chay22
1
to świetna odpowiedź.
caro
1
jak zaktualizować jeden do może relację. wyjaśniłeś, jak dodać. czy mógłbyś również wyjaśnić aktualizację? czy istnieje metoda aktualizacji rekordów, jak updateManycoś takiego? dzięki
Hamidreza
3

syncWithoutDetaching([$id_one, $id_two, $id_three]);jest tym, czego szukasz. Właściwie robi dokładnie to samo [ syncz drugim parametrem false]!

Muzułmańskie Deris
źródło
0

syncFunkcja likwidujący relacje wychodzące i czyni swoją tablicę całą listę stosunków. Zamiast attachtego chcesz dodać relacje bez usuwania innych.

ceejayoz
źródło
Nie mogę użyć załączania, ponieważ używam tego kodu w ramach metody aktualizacji. Historia polega na tym, że administrator może aktualizować zadanie i podawać uczestnikom dane wejściowe [] użytkownikom, którzy będą uczestniczyć w zadaniu. Muszę więc sprawdzić, czy istnieje i usunąć (lub nie dodawać nowego rekordu) lub jeśli nie istnieje, dodaj go.
SuperManSL,