Existe alguma maneira de clonar facilmente um objeto Eloquent, incluindo todos os seus relacionamentos?
Por exemplo, se eu tivesse essas tabelas:
users ( id, name, email )
roles ( id, name )
user_roles ( user_id, role_id )
Além de criar uma nova linha na users
tabela, com todas as colunas iguais id
, exceto , também deve criar uma nova linha na user_roles
tabela, atribuindo a mesma função ao novo usuário.
Algo assim:
$user = User::find(1);
$new_user = $user->clone();
Onde o modelo de usuário tem
class User extends Eloquent {
public function roles() {
return $this->hasMany('Role', 'user_roles');
}
}
Você também pode tentar a função replicar fornecida 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();
fonte
$user = User::with('roles')->find(1);
ou carregá-los depois de ter o modelo: portanto, as duas primeiras linhas seriam$user = User::find(1); $user->load('roles');
replicate()
definirá as relações epush()
recursará nas relações e as salvará.$new_user->roles()->save($oldRole->replicate)
Você pode tentar isso ( Clonagem de objeto ):
$user = User::find(1); $new_user = clone $user;
Como
clone
não copia profundamente, os objetos filho não serão copiados se houver algum objeto filho disponível e, neste caso, você precisa copiar o objeto filhoclone
manualmente. Por exemplo:$user = User::with('role')->find(1); $new_user = clone $user; // copy the $user $new_user->role = clone $user->role; // copy the $user->role
No seu caso,
roles
haverá uma coleção deRole
objetos, portanto, cada umRole object
na coleção precisa ser copiado manualmente usandoclone
.Além disso, você precisa estar ciente de que, se você não carregar o
roles
usingwith
, eles não serão carregados ou não estarão disponíveis no$user
e quando você chamar$user->roles
, esses objetos serão carregados em tempo de execução após essa chamada de$user->roles
e até isso, aquelesroles
não são carregados.Atualizar:
Essa resposta era para
Larave-4
e agora o Laravel oferece oreplicate()
método, por exemplo:$user = User::find(1); $newUser = $user->replicate(); // ...
fonte
null
:-)Para Laravel 5. Testado com relação 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()); } }
fonte
Aqui está uma versão atualizada da solução de @ sabrina-gelbart que clonará todos os relacionamentos hasMany em vez de apenas o belongsToMany como ela postou:
//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(); } }
fonte
some_parent_id
não for o mesmo para todos os relacionamentos. Porém, isso é útil, obrigado.Este é o laravel 5.8, não tentei na versão anterior
//# this will clone $eloquent and asign all $eloquent->$withoutProperties = null $cloned = $eloquent->cloneWithout(Array $withoutProperties)
editar, apenas hoje, 7 de abril de 2019, laravel 5.8.10 lançado
pode usar replicar agora
$post = Post::find(1); $newPost = $post->replicate(); $newPost->save();
fonte
Se você tem uma coleção chamada $ user, usando o código abaixo, ela cria uma nova coleção idêntica à antiga, incluindo todas as relações:
$new_user = new \Illuminate\Database\Eloquent\Collection ( $user->all() );
este código é para laravel 5.
fonte
$new = $old->slice(0)
?Quando você busca um objeto por qualquer relação desejada e replica depois disso, todas as relações recuperadas também são replicadas. por exemplo:
$oldUser = User::with('roles')->find(1); $newUser = $oldUser->replicate();
fonte
Aqui está uma característica que irá duplicar recursivamente todos os relacionamentos carregados em um objeto. Você pode facilmente expandir isso para outros tipos de relacionamento, como o exemplo de Sabrina para belongsToMany.
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);
fonte
Esta é outra maneira de fazer isso se as outras soluções não o agradarem:
<?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); }
O truque é limpar as propriedades
id
eexists
para que o Laravel crie um novo registro.Clonar relacionamentos pessoais é um pouco complicado, mas incluí um exemplo. Você só precisa criar um mapeamento de ids antigos para novos ids e, em seguida, sincronizar novamente.
fonte