Laravel salva / atualiza relacionamento muitos para muitos

89

Alguém pode me ajudar em como salvar muitos relacionamentos? Eu tenho tarefas, o usuário pode ter muitas tarefas e a tarefa pode ter muitos usuários (muitos para muitos). O que eu quero alcançar é que no formulário de atualização o administrador possa atribuir vários usuários a uma tarefa específica. Isso é feito por meio de entrada de seleção múltipla html

name="taskParticipants[]"

O problema aqui é que através do mesmo formulário (entrada) você pode adicionar / remover usuários, por isso tenho que usar o sync (). Talvez eu deva começar do início, mas não sei por onde começar ...

Este é o meu modelo de usuário:

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

Modelo de tarefa

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

Esta é a estrutura das tarefas de tabelas id | título | prazo

user_tasks
id|task_id|user_id
SuperManSL
fonte
Eu atualizei meu código. link
SuperManSL
4
$workers = Input::get('taskParticipants'); $task->taskParticipants()->sync($workers);e isso é tudo que você precisa, desde que passe daquele formulário todos os usuários atribuídos à tarefa.
Jarek Tkaczyk
@JarekTkaczyk Obrigado, Isso foi mágico.
Ryu_hayabusa

Respostas:

200

tldr; Use synccom o 2º parâmetrofalse


O relacionamento muitos para muitos ocorre belongsToManyem ambos os modelos:

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

Para adicionar uma nova relação, use attachousync .

A diferença entre os dois é:

1 attach adicionará uma nova linha na tabela dinâmica sem verificar se ela já está lá. É bom quando você tem dados adicionais vinculados a essa relação, por exemplo:

User e Exam vinculado à tabela dinâmicaattempts: id, user_id, exam_id, score

Suponho que não seja isso que você precisa na sua situação:

$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 por outro lado, irá remover todas as relações e configurá-las novamente:

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

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

ou irá configurar novas relações sem separar o E anterior sem adicionar duplicatas:

$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
fonte
8
Seria bom se isso estivesse documentado nos documentos principais, em vez dos documentos da API! Rock on. +1.
ceejayoz
Eu realmente gosto da segunda solução com sincronização e segundo parâmetro. Como disse no comentário abaixo, não posso deixar de usar o detach. A história é que o administrador pode atribuir tarefas aos usuários. Ele seleciona usuários na lista suspensa (vários), o campo é os participantes []. Então ...: Etapa 1: o administrador atribui a tarefa A a três usuários (seu método funciona, temos 3 registros no banco de dados) Etapa 2: o administrador atualiza a tarefa A e adiciona dois usuários (seu método funciona, temos 5 registros no banco de dados) Etapa 3: o administrador atualiza a tarefa A e remove 1 usuário (seu método falha, ainda temos 5 usuários em vez de 4) meu método de atualização meu código
SuperManSL
1
Você pode simplificar sua consulta de relacionamento apenas $this->belongsToMany('User')se usar o nome da tabela em ordem alfabética e singular (em task_uservez de user_tasks)
Kousha
@Rok se você sempre passar um array de todos os usuários relacionados, use synccom desanexação, não se preocupe. Eu sugiro usar o segundo parâmetro definido como falso sempre que quiser 'adicionar nova tarefa para um usuário' ou 'atribuir usuário a uma tarefa', quando você passar um iddos modelos relacionados.
Jarek Tkaczyk
1
@FabioAntunes syncretorna uma matriz com detached, attachede updatedlistas. attachnão retorna nada, mas em ambos os casos você obteria uma exceção se algo inesperado acontecesse nas chamadas de banco de dados.
Jarek Tkaczyk
109

Aqui estão minhas notas sobre como salvar e atualizar todos os relacionamentos do Eloquent.

em um para um :

Você deve usar HasOne no primeiro modelo e BelongsTo no segundo modelo

para adicionar registro no primeiro modelo ( HasOne ) use a  função salvar

exemplo:    $post->comments()->save($comment);

para adicionar registro no segundo modelo ( BelongsTo ) use o  associado função de

exemplo:    $user->account()->associate($account);    $user->save();


em um para muitos :

Você tem que usar HasMany no primeiro modelo e BelongsTo no segundo modelo

para adicionar registro na primeira tabela ( HasMany ) use o save  ou saveMany funções

exemplo:    $post->comments()->saveMany($comments);

para adicionar registro no segundo modelo ( BelongsTo ) use o  associado função de

exemplo:    $user->account()->associate($account);    $user->save();


de muitos para muitos :

Você deve usar  BelongsToMany no primeiro modelo e BelongsToMany no segundo modelo

para adicionar registros na tabela dinâmica, use as funções de anexar ou sincronizar

  • ambas as funções aceitam ID único ou matriz de IDs 

  • a diferença é o anexo verifica se o registro já existe na tabela dinâmica enquanto a sincronização não

exemplo: $user->roles()->attach($roleId);


em polimórfico um para muitos :

Você tem que usar  MorphMany no modelo principal e  MorphTo em todos os modelos (*** capazes)

para adicionar registros em todos os outros modelos, use o  botão salvar

exemplo:    $course->tags()->save($tag);

a tabela dinâmica deve ter as seguintes colunas:

. ID do modelo principal

. (*** capaz) ID

. (*** capaz) Tipo


em polimórfico muitos para muitos :

Você deve usar  MorphByMany no modelo principal e  MorphToMany em todos os modelos (*** aptos)

para adicionar registros em todos os outros modelos, use o save ou saveMany

exemplo:    $course->tags()->save($tag);

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

a tabela dinâmica deve ter as seguintes colunas:

. ID do modelo principal

. (*** capaz) ID

. (*** capaz) Tipo


em Has Many Through (atalho):

Você tem que usar HasManyThrough na primeira tabela e ter as relações normais nas outras 2 tabelas

isso não funciona para relacionamentos ManyToMany (onde há uma tabela dinâmica)

no entanto, há uma solução fácil e agradável apenas para isso.


Aqui está um artigo que escrevi, inspirado por esta resposta. Importante verificar: https://hackernoon.com/eloquent-relationships-cheat-sheet-5155498c209

Mahmoud Zalt
fonte
Na verdade, coloquei esta resposta quando eles já tinham mais de 40 curtidas na resposta correta, mas sim, eu sei como isso é útil para mim, que bom que você gostou :)
Mahmoud Zalt
1
Eu gostaria que você soubesse que você é um salvador
Chay22
1
esta é uma ótima resposta.
caro
1
como atualizar um para a relação de maio. u explicou como adicionar. você poderia explicar a atualização também? existe um método para atualizar registros como updateManyalgo parecido? obrigado
Hamidreza
4

syncWithoutDetaching([$id_one, $id_two, $id_three]);é o que você está procurando. Na verdade, ele faz exatamente a coisa que [ synccom o segundo parâmetro false] faz!

Deris muçulmana
fonte
0

A syncfunção oblitera os relacionamentos existentes e torna seu array a lista inteira de relacionamentos. Em attachvez disso, você deseja adicionar relações sem remover outras.

ceejayoz
fonte
Não posso usar anexar porque estou usando esse código dentro do método de atualização. A história é que o administrador pode atualizar a tarefa e preencher os participantes de entrada [] com os usuários que participarão da tarefa. Então preciso verificar se ele existe e deletar (ou não adicionar novo registro) ou se não existir adicionar.
SuperManSL