Laravel guardar / actualizar la relación de muchos a muchos

85

¿Alguien puede ayudarme sobre cómo salvar la relación de muchos a muchos? Tengo tareas, el usuario puede tener muchas tareas y la tarea puede tener muchos usuarios (de muchos a muchos). Lo que quiero lograr es que en el formulario de actualización el administrador pueda asignar múltiples usuarios a una tarea específica. Esto se hace a través de la entrada de selección múltiple html

name="taskParticipants[]"

El problema aquí es que a través del mismo formulario (entrada) puede agregar / eliminar usuarios, por eso tengo que usar sync (). Tal vez debería empezar desde el principio pero no sé por dónde empezar ...

Este es mi modelo de usuario:

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

Modelo de tarea

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 es la estructura de las tablas id de tareas | título | fecha límite

user_tasks
id|task_id|user_id
SuperManSL
fuente
Actualicé mi código. enlace
SuperManSL
4
$workers = Input::get('taskParticipants'); $task->taskParticipants()->sync($workers);y eso es todo lo que necesita, siempre que pase de ese formulario a todos los usuarios, asignados a la tarea.
Jarek Tkaczyk
@JarekTkaczyk Gracias, eso fue mágico.
Ryu_hayabusa

Respuestas:

191

tldr; Usar synccon 2do parámetrofalse


La relación de varios a varios está belongsToManyen ambos 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 agregar una nueva relación, use attacho sync.

La diferencia entre los dos es:

1 attach agregará una nueva fila en la tabla dinámica sin verificar si ya está allí. Es bueno cuando tiene datos adicionales vinculados a esa relación, por ejemplo:

Usery Examvinculado con la tabla dinámicaattempts: id, user_id, exam_id, score

Supongo que esto no es lo que necesitas en tu situación:

$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 otro lado, eliminará todas las relaciones y las configurará de nuevo:

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

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

o configurará nuevas relaciones sin separar Y sin agregar duplicados:

$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
fuente
8
¡Sería bueno si eso estuviera documentado en los documentos principales en lugar de en los documentos de la API! Rock on. +1.
ceejayoz
Realmente me gusta la segunda solución con sincronización y segundo parámetro. Como dije en el comentario a continuación, no puedo permitirme no usar el desprendimiento. La historia es que el administrador puede asignar tareas a los usuarios. Selecciona usuarios del menú desplegable (múltiples), el campo es participantes []. Entonces ...: Paso 1: el administrador asigna la tarea A a tres usuarios (su método funciona, tenemos 3 registros en la base de datos) Paso 2: el administrador actualiza la tarea A y agrega dos usuarios (su método funciona, tenemos 5 registros en la base de datos) Paso 3: el administrador actualiza la tarea A y elimina 1 usuario (su método falla, todavía tenemos 5 usuarios en lugar de 4) mi método de actualización mi código
SuperManSL
1
Puede simplificar su consulta de relación solo $this->belongsToMany('User')si usa el nombre de la tabla alfabéticamente y en singular (en task_userlugar de user_tasks)
Kousha
@Rok si siempre pasa una matriz de todos los usuarios relacionados, entonces úselo con la separación sync, no se preocupe. Sugiero usar el segundo parámetro establecido en falso siempre que desee 'agregar una nueva tarea para un usuario' o 'asignar un usuario a una tarea', cuando pase solo uno idde los modelos relacionados.
Jarek Tkaczyk
1
@FabioAntunes syncdevuelve una matriz con detached, attachedy updatedlistas. attachno devuelve nada, pero en ambos casos obtendría una excepción si sucediera algo inesperado en las llamadas a la base de datos.
Jarek Tkaczyk
106

Aquí están mis notas sobre cómo guardar y actualizar todas las relaciones de Eloquent.

en uno a uno :

Tienes que usar HasOne en el primer modelo y BelongsTo en el segundo modelo

para agregar un registro en el primer modelo ( HasOne ) use la  función de guardar

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

para agregar un registro en el segundo modelo ( Pertenece a ) use la  función asociada

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


en uno a muchos :

Tienes que usar HasMany en el primer modelo y BelongsTo en el segundo modelo

para agregar un registro en la primera tabla ( HasMany ) use las funciones save  o saveMany

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

para agregar un registro en el segundo modelo ( Pertenece a ) use la  función asociada

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


de muchos a muchos :

Tienes que usar  BelongsToMany en el primer modelo y BelongsToMany en el segundo modelo

para agregar registros en la tabla dinámica, use las funciones adjuntar o sincronizar

  • Ambas funciones aceptan un solo ID o una matriz de ID 

  • la diferencia es adjuntar verificaciones si el registro ya existe en la tabla dinámica mientras que la sincronización no

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


en polimórfico uno a muchos :

Tienes que usar  MorphMany en el modelo principal y  MorphTo en todos los modelos (*** capaces)

para agregar registros en todos los demás modelos, use el  comando guardar

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

la tabla dinámica debe tener las siguientes columnas:

. ID del modelo principal

. (*** capaz) ID

. (*** capaz) Tipo


en polimórficos muchos a muchos :

Tienes que usar  MorphByMany en el modelo principal y  MorphToMany en todos los modelos (*** capaces)

para agregar registros en todos los demás modelos, use save o saveMany

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

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

la tabla dinámica debe tener las siguientes columnas:

. ID del modelo principal

. (*** capaz) ID

. (*** capaz) Tipo


en Has Many Through (atajo):

Tienes que usar HasManyThrough en la primera tabla y tener las relaciones normales en las otras 2 tablas

esto no funciona para las relaciones ManyToMany (donde hay una tabla dinámica)

sin embargo, existe una solución sencilla y agradable para eso.


Aquí hay un artículo que escribí, inspirado en esta respuesta. Importante comprobarlo: https://hackernoon.com/eloquent-relationships-cheat-sheet-5155498c209

Mahmoud Zalt
fuente
De hecho, coloqué esta respuesta cuando ya tenían más de 40 me gusta en la respuesta correcta, pero sí, sé lo útil que es para mí, me alegro de que te guste :)
Mahmoud Zalt
1
Deseo que sepas que eres un salvador
Chay22
1
Esta es una respuesta genial.
caro
1
cómo actualizar una relación de mayo. u explicó cómo agregar. ¿podría explicar la actualización también? ¿Existe un método para actualizar registros updateManycomo ese? gracias
Hamidreza
3

syncWithoutDetaching([$id_one, $id_two, $id_three]);es lo que estas buscando. ¡Realmente hace exactamente lo que hace [ synccon el segundo parámetro false]!

Deris musulmana
fuente
0

La syncfunción borra las relaciones existentes y convierte su matriz en la lista completa de relaciones. En su attachlugar, desea agregar relaciones sin eliminar otras.

ceejayoz
fuente
No puedo usar adjuntar porque estoy usando este código dentro del método de actualización. La historia es que el administrador puede actualizar la tarea y completar los participantes de entrada [] con los usuarios que participarán en la tarea. Así que necesito verificar si existe y eliminarlo (o no agregar un nuevo registro) o si no existe agregarlo.
SuperManSL