¿Hay alguna forma de clonar fácilmente un objeto Eloquent, incluidas todas sus relaciones?
Por ejemplo, si tuviera estas tablas:
users ( id, name, email )
roles ( id, name )
user_roles ( user_id, role_id )
Además de crear una nueva fila en la userstabla, con todas las columnas iguales excepto id, también debe crear una nueva fila en la user_rolestabla, asignando el mismo rol al nuevo usuario.
Algo como esto:
$user = User::find(1);
$new_user = $user->clone();
Donde el modelo de usuario tiene
class User extends Eloquent {
public function roles() {
return $this->hasMany('Role', 'user_roles');
}
}

También puede probar la función de replicación proporcionada por eloquent:
http://laravel.com/api/4.2/Illuminate/Database/Eloquent/Model.html#method_replicate
$user = User::find(1); $new_user = $user->replicate(); $new_user->push();fuente
$user = User::with('roles')->find(1);o cargarlas después de tener el Modelo: por lo que las dos primeras líneas serían$user = User::find(1); $user->load('roles');replicate()establecerá las relaciones y lospush()recurrentes en las relaciones y las guardará.$new_user->roles()->save($oldRole->replicate)Puede probar esto ( Clonación de objetos ):
$user = User::find(1); $new_user = clone $user;Dado
cloneque no hace una copia en profundidad, los objetos secundarios no se copiarán si hay algún objeto secundario disponible y, en este caso, debe copiar el objeto secundarioclonemanualmente. Por ejemplo:$user = User::with('role')->find(1); $new_user = clone $user; // copy the $user $new_user->role = clone $user->role; // copy the $user->roleEn su caso
roles, habrá una colección deRoleobjetos, por lo que cada unoRole objecten la colección debe copiarse manualmente usandoclone.Además, debe tener en cuenta que, si no carga el
rolesuso, esos objetoswithno se cargarán o no estarán disponibles en el$usery cuando llame$user->roles, esos objetos se cargarán en el tiempo de ejecución después de esa llamada. de$user->rolesy hasta este, esosrolesno se cargan.Actualizar:
Esta respuesta fue para
Larave-4y ahora Laravel ofrece unreplicate()método, por ejemplo:$user = User::find(1); $newUser = $user->replicate(); // ...fuente
null:-)Para Laravel 5. Probado con la relación hasMany.
$model = User::find($id); $model->load('invoices'); $newModel = $model->replicate(); $newModel->push(); foreach($model->getRelations() as $relation => $items){ foreach($items as $item){ unset($item->id); $newModel->{$relation}()->create($item->toArray()); } }fuente
Aquí hay una versión actualizada de la solución de @ sabrina-gelbart que clonará todas las relaciones de hasMany en lugar de solo las pertenece a Many como ella publicó:
//copy attributes from original model $newRecord = $original->replicate(); // Reset any fields needed to connect to another parent, etc $newRecord->some_id = $otherParent->id; //save model before you recreate relations (so it has an id) $newRecord->push(); //reset relations on EXISTING MODEL (this way you can control which ones will be loaded $original->relations = []; //load relations on EXISTING MODEL $original->load('somerelationship', 'anotherrelationship'); //re-sync the child relationships $relations = $original->getRelations(); foreach ($relations as $relation) { foreach ($relation as $relationRecord) { $newRelationship = $relationRecord->replicate(); $newRelationship->some_parent_id = $newRecord->id; $newRelationship->push(); } }fuente
some_parent_idno es lo mismo para todas las relaciones. Sin embargo, esto es útil, gracias.Esto está en laravel 5.8, no lo he probado en una versión anterior
//# this will clone $eloquent and asign all $eloquent->$withoutProperties = null $cloned = $eloquent->cloneWithout(Array $withoutProperties)editar, solo hoy 7 de abril de 2019 se lanzó laravel 5.8.10
puede usar replicar ahora
$post = Post::find(1); $newPost = $post->replicate(); $newPost->save();fuente
Si tiene una colección llamada $ user, usando el código a continuación, crea una nueva colección idéntica a la anterior, incluyendo todas las relaciones:
$new_user = new \Illuminate\Database\Eloquent\Collection ( $user->all() );este código es para laravel 5.
fuente
$new = $old->slice(0)?Cuando recupera un objeto por cualquier relación que desee, y luego lo replica, todas las relaciones que recupera también se replican. por ejemplo:
$oldUser = User::with('roles')->find(1); $newUser = $oldUser->replicate();fuente
Aquí hay un rasgo que duplicará de forma recursiva todas las relaciones cargadas en un objeto. Puede expandir esto fácilmente para otros tipos de relaciones como el ejemplo de Sabrina para pertenece a muchos.
trait DuplicateRelations { public static function duplicateRelations($from, $to) { foreach ($from->relations as $relationName => $object){ if($object !== null) { if ($object instanceof Collection) { foreach ($object as $relation) { self::replication($relationName, $relation, $to); } } else { self::replication($relationName, $object, $to); } } } } private static function replication($name, $relation, $to) { $newRelation = $relation->replicate(); $to->{$name}()->create($newRelation->toArray()); if($relation->relations !== null) { self::duplicateRelations($relation, $to->{$name}); } } }Uso:
//copy attributes $new = $this->replicate(); //save model before you recreate relations (so it has an id) $new->push(); //reset relations on EXISTING MODEL (this way you can control which ones will be loaded $this->relations = []; //load relations on EXISTING MODEL $this->load('relation1','relation2.nested_relation'); // duplication all LOADED relations including nested. self::duplicateRelations($this, $new);fuente
Aquí hay otra forma de hacerlo si las otras soluciones no lo apaciguan:
<?php /** @var \App\Models\Booking $booking */ $booking = Booking::query()->with('segments.stops','billingItems','invoiceItems.applyTo')->findOrFail($id); $booking->id = null; $booking->exists = false; $booking->number = null; $booking->confirmed_date_utc = null; $booking->save(); $now = CarbonDate::now($booking->company->timezone); foreach($booking->segments as $seg) { $seg->id = null; $seg->exists = false; $seg->booking_id = $booking->id; $seg->save(); foreach($seg->stops as $stop) { $stop->id = null; $stop->exists = false; $stop->segment_id = $seg->id; $stop->save(); } } foreach($booking->billingItems as $bi) { $bi->id = null; $bi->exists = false; $bi->booking_id = $booking->id; $bi->save(); } $iiMap = []; foreach($booking->invoiceItems as $ii) { $oldId = $ii->id; $ii->id = null; $ii->exists = false; $ii->booking_id = $booking->id; $ii->save(); $iiMap[$oldId] = $ii->id; } foreach($booking->invoiceItems as $ii) { $newIds = []; foreach($ii->applyTo as $at) { $newIds[] = $iiMap[$at->id]; } $ii->applyTo()->sync($newIds); }El truco consiste en borrar las propiedades
idyexistspara que Laravel cree un nuevo registro.Clonar las relaciones con uno mismo es un poco complicado, pero he incluido un ejemplo. Solo tiene que crear un mapeo de identificadores antiguos a identificadores nuevos y luego volver a sincronizar.
fuente