सभी रिश्तों सहित एक स्पष्ट वस्तु क्लोन?


86

क्या किसी भी वस्तु को आसानी से अपने सभी रिश्तों सहित एक सुव्यवस्थित वस्तु पर क्लोन करने का कोई तरीका है?

उदाहरण के लिए, अगर मेरे पास ये टेबल थे:

users ( id, name, email )
roles ( id, name )
user_roles ( user_id, role_id )

usersतालिका में एक नई पंक्ति बनाने के अलावा , सभी कॉलम समान होने के अलावा id, इसे user_rolesतालिका में एक नई पंक्ति भी बनानी चाहिए , नए उपयोगकर्ता को एक ही भूमिका सौंपे।

कुछ इस तरह:

$user = User::find(1);
$new_user = $user->clone();

जहां यूजर मॉडल है

class User extends Eloquent {
    public function roles() {
        return $this->hasMany('Role', 'user_roles');
    }
}

जवाबों:


74

रिलेटोमनी संबंधों के लिए लार्वा 4.2 में परीक्षण किया गया

यदि आप मॉडल में हैं:

    //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);
    }


यह पिछले संस्करण Laravel 6 पर भी काम करता है (मुझे लगता है कि पिछली टिप्पणी के आधार पर अपेक्षित है :)) धन्यवाद!
mmmearearte

लारवेल 7.28.4 में काम किया। मैंने देखा है कि यदि आप मॉडल के बाहर इसे चलाने का प्रयास कर रहे हैं तो कोड अलग होना चाहिए। धन्यवाद
रोमन Grinev

56

आप वाक्पटु द्वारा प्रदान किए गए प्रतिकृति फ़ंक्शन को भी आज़मा सकते हैं:

http://laravel.com/api/4.2/Illuminate/Database/Eloquent/Model.html#method_replicate

$user = User::find(1);
$new_user = $user->replicate();
$new_user->push();

7
वास्तव में आपको उन रिश्तों को लोड करना होगा जिन्हें आप दोहराना चाहते हैं। दिए गए कोड केवल अपने संबंधों के बिना आधार मॉडल को दोहराएंगे। साथ ही रिश्तों को क्लोन करने के लिए, आप या तो अपने संबंधों के साथ उपयोगकर्ता को प्राप्त कर सकते हैं: $user = User::with('roles')->find(1);या आपके पास उन्हें लोड करने के बाद आपके पास मॉडल होगा: इसलिए पहली दो पंक्तियां होंगी$user = User::find(1); $user->load('roles');
अलेक्जेंडर टूबेनकोर्ब

2
लोड हो रहा है रिश्तों को भी रिश्तों को दोहराने के लिए प्रकट नहीं होता है, कम से कम 4.1 में नहीं। मुझे माता-पिता की प्रतिकृति तैयार करनी थी, फिर मूल के बच्चों के माध्यम से लूप दिया और उन्हें नए माता-पिता को इंगित करने के लिए एक बार में अपडेट किया।
रेक्स श्रेडर

replicate()संबंधों को सेट करेगा और संबंधों में push()पुनरावृत्ति करेगा और उन्हें बचाता है।
मैट के

इसके अलावा 5.2 में आपको बच्चों के माध्यम से लूप करने की जरूरत है और एक बार में एक की नकल करने के बाद उन्हें बचाएं; एक अंदर की ओर:$new_user->roles()->save($oldRole->replicate)
d.grassi84 17

28

आप इसे आज़मा सकते हैं ( ऑब्जेक्ट क्लोनिंग ):

$user = User::find(1);
$new_user = clone $user;

चूँकि 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

आपके मामले rolesमें Roleवस्तुओं का एक संग्रह होगा ताकि संग्रह में प्रत्येक Role objectको मैन्युअल रूप से उपयोग करके कॉपी करने की आवश्यकता हो clone

इसके अलावा, आपको इसके बारे में पता होना चाहिए, यदि आप rolesउपयोग को लोड नहीं करते हैं, withतो वे लोड नहीं होंगे या उपलब्ध नहीं होंगे $userऔर जब आप कॉल करेंगे $user->rolesतो उस कॉल के बाद रन टाइम पर उन वस्तुओं को लोड किया जाएगा। की $user->rolesहै और इस तक, उन rolesलोड नहीं होतीं।

अपडेट करें:

यह उत्तर के लिए था Larave-4और अब लारवेल replicate()विधि प्रदान करता है, उदाहरण के लिए:

$user = User::find(1);
$newUser = $user->replicate();
// ...

2
सावधान रहें, केवल एक उथली प्रतिलिपि, न कि उप / बाल वस्तुओं :-)
अल्फा

1
@ TheShiftExchange, आपको यह दिलचस्प लग सकता है , मैंने एक प्रयोग बहुत पहले किया था। अंगूठे के लिए धन्यवाद :-)
अल्फा

1
क्या यह वस्तु की आईडी की नकल भी नहीं करता है? बचत के लिए इसे बेकार बना रहे हैं?
तोश

@ संतोष, हाँ, ठीक है और इसीलिए आपको null
द अल्फा

1
php के रहस्य को प्रकट करने के लिए plus1:
मेटाबॉलिक

21

लारवेल के लिए 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());
    }
}

7

यहाँ @ सबरीना-गेलबार्ट से समाधान का एक अद्यतन संस्करण दिया गया है, जो सभी के साथ संबंध रखता है, जैसे कि वह केवल पोस्टमार्टम के बजाय किसी भी संबंध में है:

    //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();
        }
    }

मुश्किल अगर some_parent_idसभी रिश्तों के लिए समान नहीं है। हालांकि यह उपयोगी है, धन्यवाद।
डस्टिन ग्राहम

4

यह लार्वा 5.8 में है, पुराने संस्करण में havent की कोशिश की गई है

//# this will clone $eloquent and asign all $eloquent->$withoutProperties = null
$cloned = $eloquent->cloneWithout(Array $withoutProperties)

संपादित करें, आज ही के दिन 7 अप्रैल 2019 को लार्वा 5.8.10 लॉन्च किया गया था

अब प्रतिकृति का उपयोग कर सकते हैं

$post = Post::find(1);
$newPost = $post->replicate();
$newPost->save();

2

यदि आपके पास $ b नाम का एक संग्रह है, तो कोड bellow का उपयोग करते हुए, यह सभी संबंधों सहित पुराने से एक नया संग्रह बनाता है:

$new_user = new \Illuminate\Database\Eloquent\Collection ( $user->all() );

यह कोड लार्वा 5 के लिए है।


1
क्या आप बस नहीं कर सकते थे $new = $old->slice(0)?
फ़ुबर

2

जब आप अपने इच्छित किसी भी संबंध के द्वारा एक वस्तु प्राप्त करते हैं, और उसके बाद प्रतिकृति करते हैं, तो आपके द्वारा पुनः प्राप्त सभी संबंध भी दोहराया जाता है। उदाहरण के लिए:

$oldUser = User::with('roles')->find(1);
$newUser = $oldUser->replicate();

मैंने
लारवेल

2

यहां एक विशेषता है जो किसी वस्तु पर सभी लोड किए गए रिश्तों की पुनरावृत्ति करेगा । आप अन्य संबंधों के प्रकारों के लिए इसका विस्तार आसानी से कर सकते हैं जैसे कि सबरीना का उदाहरण ,TTMMany के लिए।

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});
        }
    }
}

उपयोग:

//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);

0

यदि अन्य समाधान आपको खुश नहीं करते हैं, तो यह करने का एक और तरीका है:

<?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);
}

चाल idऔर existsगुणों को मिटा देना है ताकि लारवेल एक नया रिकॉर्ड बनाएंगे।

स्व-संबंधों को क्लोन करना थोड़ा मुश्किल है लेकिन मैंने एक उदाहरण शामिल किया है। आपको बस नई आईडी में पुरानी आईडी की मैपिंग बनानी होगी और फिर से सिंक करना होगा।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.