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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 02:49:31  来源:igfitidea点击:

Clone an Eloquent object including all relationships?

laravellaravel-4cloneeloquent

提问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 userstable, with all columns being the same except id, it should also create a new row in the user_rolestable, 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 clonedoesn'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 clonemanually. 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 roleswill be a collection of Roleobjects so each Role objectin 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 rolesusing withthen those will be not loaded or won't be available in the $userand when you'll call $user->rolesthen those objects will be loaded at run time after that call of $user->rolesand until this, those rolesare not loaded.

此外,您需要注意的是,如果您不加载rolesusing,with那么这些对象将不会加载或将不可用$user,当您调用时,$user->roles这些对象将在该调用之后的运行时加载在$user->roles此之前,这些roles都没有加载。

Update:

更新:

This answer was for Larave-4and 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 idand existsproperties so that Laravel will create a new record.

诀窍是擦除idexists属性,以便 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 的映射,然后重新同步。