laravel 克隆一个包含所有关系的 Eloquent 对象?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/23895126/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Clone an Eloquent object including all relationships?
提问by andrewtweber
Is there any way to easily clone an Eloquent object, including all of its relationships?
有什么方法可以轻松克隆 Eloquent 对象,包括它的所有关系?
For example, if I had these tables:
例如,如果我有这些表:
users ( id, name, email )
roles ( id, name )
user_roles ( user_id, role_id )
In addition to creating a new row in the users
table, with all columns being the same except id
, it should also create a new row in the user_roles
table, assigning the same role to the new user.
除了在users
表中创建一个新行,除了id
,所有列都相同 ,它还应该在user_roles
表中创建一个新行,为新用户分配相同的角色。
Something like this:
像这样的东西:
$user = User::find(1);
$new_user = $user->clone();
Where the User model has
用户模型在哪里
class User extends Eloquent {
public function roles() {
return $this->hasMany('Role', 'user_roles');
}
}
采纳答案by Sabrina Leggett
tested in laravel 4.2 for belongsToMany relationships
在laravel 4.2 中测试了belongsToMany 关系
if you're in the model:
如果您在模型中:
//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');
//re-sync everything
foreach ($this->relations as $relationName => $values){
$new->{$relationName}()->sync($values);
}
回答by Piotr Borek
You may also try the replicate function provided by eloquent:
你也可以试试eloquent提供的replicate功能:
http://laravel.com/api/4.2/Illuminate/Database/Eloquent/Model.html#method_replicate
http://laravel.com/api/4.2/Illuminate/Database/Eloquent/Model.html#method_replicate
$user = User::find(1);
$new_user = $user->replicate();
$new_user->push();
回答by The Alpha
You may try this (Object Cloning):
你可以试试这个(对象克隆):
$user = User::find(1);
$new_user = clone $user;
Since clone
doesn't deep copy so child objects won't be copied if there is any child object available and in this case you need to copy the child object using clone
manually. For example:
由于clone
不是深度复制,因此如果有任何可用的子对象,则不会复制子对象,在这种情况下,您需要clone
手动复制子对象。例如:
$user = User::with('role')->find(1);
$new_user = clone $user; // copy the $user
$new_user->role = clone $user->role; // copy the $user->role
In your case roles
will be a collection of Role
objects so each Role object
in the collection needs to be copied manually using clone
.
在您的情况下roles
将是一个Role
对象集合,因此集合中的每个都Role object
需要使用clone
.
Also, you need to be aware of that, if you don't load the roles
using with
then those will be not loaded or won't be available in the $user
and when you'll call $user->roles
then those objects will be loaded at run time after that call of $user->roles
and until this, those roles
are not loaded.
此外,您需要注意的是,如果您不加载roles
using,with
那么这些对象将不会加载或将不可用$user
,当您调用时,$user->roles
这些对象将在该调用之后的运行时加载在$user->roles
此之前,这些roles
都没有加载。
Update:
更新:
This answer was for Larave-4
and now Laravel offers replicate()
method, for example:
这个答案是为Larave-4
现在 Laravel 提供的replicate()
方法,例如:
$user = User::find(1);
$newUser = $user->replicate();
// ...
回答by JIM
For Laravel 5. Tested with hasMany relation.
对于 Laravel 5。使用 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());
}
}
回答by davidethell
Here is an updated version of the solution from @sabrina-gelbart that will clone all hasMany relationships instead of just the belongsToMany as she posted:
这是@sabrina-gelbart 解决方案的更新版本,它将克隆所有 hasMany 关系,而不仅仅是她发布的belongsToMany:
//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();
}
}
回答by Mihai Cr?i??
If you have a collection named $user, using the code bellow, it creates a new Collection identical from the old one, including all the relations:
如果您有一个名为 $user 的集合,使用下面的代码,它会创建一个与旧集合相同的新集合,包括所有关系:
$new_user = new \Illuminate\Database\Eloquent\Collection ( $user->all() );
this code is for laravel 5.
此代码适用于 Laravel 5。
回答by david valentino
This is in laravel 5.8, havent tried in older version
这是在laravel 5.8中,还没有在旧版本中尝试过
//# this will clone $eloquent and asign all $eloquent->$withoutProperties = null
$cloned = $eloquent->cloneWithout(Array $withoutProperties)
edit, just today 7 April 2019 laravel 5.8.10 launched
编辑,就在今天 2019 年 4 月 7 日laravel 5.8.10 发布
can use replicate now
现在可以使用复制
$post = Post::find(1);
$newPost = $post->replicate();
$newPost->save();
回答by elyas.m
When you fetch an object by any relation you want, and replicate after that, all relations you retrieved are also replicated. for example:
当您通过任何您想要的关系获取对象并在此之后进行复制时,您检索到的所有关系也会被复制。例如:
$oldUser = User::with('roles')->find(1);
$newUser = $oldUser->replicate();
回答by Sean Berce
Here is a trait that will recursively duplicate all the loadedrelationships on an object. You could easily expand this for other relationship types like Sabrina's example for belongsToMany.
这是一个特征,它将递归复制对象上的所有加载关系。您可以轻松地将其扩展到其他关系类型,例如 Sabrina 的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});
}
}
}
Usage:
用法:
//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);
回答by mpen
Here's another way to do it if the other solutions don't appease you:
如果其他解决方案不能让您满意,这是另一种方法:
<?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);
}
The trick is to wipe the id
and exists
properties so that Laravel will create a new record.
诀窍是擦除id
和exists
属性,以便 Laravel 将创建一个新记录。
Cloning self-relationships is a little tricky but I've included an example. You just have to create a mapping of old ids to new ids and then re-sync.
克隆自我关系有点棘手,但我已经包含了一个例子。您只需要创建旧 ID 到新 ID 的映射,然后重新同步。