¿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 users
tabla, con todas las columnas iguales excepto id
, también debe crear una nueva fila en la user_roles
tabla, 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
clone
que 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 secundarioclone
manualmente. Por ejemplo:$user = User::with('role')->find(1); $new_user = clone $user; // copy the $user $new_user->role = clone $user->role; // copy the $user->role
En su caso
roles
, habrá una colección deRole
objetos, por lo que cada unoRole object
en la colección debe copiarse manualmente usandoclone
.Además, debe tener en cuenta que, si no carga el
roles
uso, esos objetoswith
no se cargarán o no estarán disponibles en el$user
y cuando llame$user->roles
, esos objetos se cargarán en el tiempo de ejecución después de esa llamada. de$user->roles
y hasta este, esosroles
no se cargan.Actualizar:
Esta respuesta fue para
Larave-4
y 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_id
no 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
id
yexists
para 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