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

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]

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


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!


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.


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
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.