Encontramos el hallazgo a esta obstáculo, al menos eso deseamos. Si tienes dudas dínoslo, que para nosotros será un placer responderte
Solución:
Desafortunadamente no hay sync
método para relaciones de uno a varios. Es bastante sencillo hacerlo tú mismo. Al menos si no tienes ningún extranjero key referenciando links
. Porque entonces puede simplemente eliminar las filas e insertarlas todas de nuevo.
$links = array(
new Link(),
new Link()
);
$post->links()->delete();
$post->links()->saveMany($links);
Si realmente necesita actualizar uno existente (por cualquier motivo), debe hacer exactamente lo que describió en su pregunta.
El problema de eliminar y leer las entidades relacionadas es que romperá cualquier key restricciones que pueda tener en esas entidades secundarias.
Una mejor solución es modificar Laravel’s HasMany
relación para incluir un sync
método:
[], 'deleted' => [], 'updated' => [],
];
$relatedKeyName = $this->related->getKeyName();
// First we need to attach any of the associated models that are not currently
// in the child entity table. We'll spin through the given IDs, checking to see
// if they exist in the array of current ones, and if not we will insert.
$current = $this->newQuery()->pluck(
$relatedKeyName
)->all();
// Separate the submitted data into "update" and "new"
$updateRows = [];
$newRows = [];
foreach ($data as $row)
// We determine "updateable" rows as those whose $relatedKeyName (usually 'id') is set, not empty, and
// match a related row in the database.
if (isset($row[$relatedKeyName]) && !empty($row[$relatedKeyName]) && in_array($row[$relatedKeyName], $current))
$id = $row[$relatedKeyName];
$updateRows[$id] = $row;
else
$newRows[] = $row;
// Next, we'll determine the rows in the database that aren't in the "update" list.
// These rows will be scheduled for deletion. Again, we determine based on the relatedKeyName (typically 'id').
$updateIds = array_keys($updateRows);
$deleteIds = [];
foreach ($current as $currentId)
if (!in_array($currentId, $updateIds))
$deleteIds[] = $currentId;
// Delete any non-matching rows
if ($deleting && count($deleteIds) > 0)
$this->getRelated()->destroy($deleteIds);
$changes['deleted'] = $this->castKeys($deleteIds);
// Update the updatable rows
foreach ($updateRows as $id => $row)
$this->getRelated()->where($relatedKeyName, $id)
->update($row);
$changes['updated'] = $this->castKeys($updateIds);
// Insert the new rows
$newIds = [];
foreach ($newRows as $row)
$newModel = $this->create($row);
$newIds[] = $newModel->$relatedKeyName;
$changes['created'] = $this->castKeys($newIds);
return $changes;
/**
* Cast the given keys to integers if they are numeric and string otherwise.
*
* @param array $keys
* @return array
*/
protected function castKeys(array $keys)
return (array) array_map(function ($v)
return $this->castKey($v);
, $keys);
/**
* Cast the given key to an integer if it is numeric.
*
* @param mixed $key
* @return mixed
*/
protected function castKey($key)
return is_numeric($key) ? (int) $key : (string) $key;
Puede anular el de Eloquent Model
clase para usar HasManySyncable
en lugar del estándar HasMany
relación:
newRelatedInstance($related);
$foreignKey = $foreignKey ?: $this->getForeignKey();
$localKey = $localKey ?: $this->getKeyName();
return new HasManySyncable(
$instance->newQuery(), $this, $instance->getTable().'.'.$foreignKey, $localKey
);
Suponiendo que tu Post
el modelo se extiende MyBaseModel
y tiene un links()
hasMany
relación, puedes hacer algo como:
$post->links()->sync([
[
'id' => 21,
'name' => "LinkedIn profile"
],
[
'id' => null,
'label' => "Personal website"
]
]);
Cualquier registro en este multidimensional array que tienen un id
que coincide con la tabla de la entidad secundaria (links
) será actualizado. Registros en la tabla que no están presentes en este array será eliminado. Registros en el array que no están presentes en la tabla (tienen un id
, o un id
de null) se considerarán registros “nuevos” y se insertarán en la base de datos.