সমস্ত সম্পর্ক সহ একটি স্বতন্ত্র অবজেক্টকে ক্লোন করুন?


87

কোনও স্পষ্টত অবজেক্টের এর সমস্ত সম্পর্ক সহ সহজেই ক্লোন করার কোনও উপায় আছে কি?

উদাহরণস্বরূপ, যদি আমার এই টেবিলগুলি থাকে:

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

উত্তর:


77

T.২ টি সম্পর্কের জন্য ল্যারাভেল ৪.২ এ পরীক্ষিত

আপনি যদি মডেলটিতে থাকেন:

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


এটি পূর্ববর্তী সংস্করণ লারাভেল on এও কাজ করে ((আমার ধারণা পূর্ববর্তী মন্তব্যের ভিত্তিতে প্রত্যাশিত :) :) ধন্যবাদ!
mmmdearte

লারাভেল 7.28.4 এ কাজ করেছেন। আমি লক্ষ্য করেছি যে আপনি যদি মডেলটির বাইরে চালানোর চেষ্টা করছেন তবে কোডটি আলাদা হওয়া উচিত। ধন্যবাদ
রোমান গ্রিনিভ

56

আপনি স্বতন্ত্র দ্বারা সরবরাহিত প্রতিরূপ ফাংশন চেষ্টা করতে পারেন:

http://laravel.com/api/4.2/ ইলুমিনিট / ডেটাবেস / এলওভারেন্ট / মডেল এইচটিএমএল# মৈথুন_প্রেরণিকা

$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');
আলেকজান্ডার তৌবেনকর্ব

4
সম্পর্কগুলি লোড করা সম্পর্কেরও প্রতিলিপি হিসাবে উপস্থিত হয় না, অন্তত 4.1-এ নয়। আমাকে পিতামাতার প্রতিলিপি তৈরি করতে হয়েছিল, তারপরে মূল বাচ্চাদের প্রতি লুপটি প্রতিলিপি করা হয়েছিল এবং নতুন পিতামাতার প্রতি নির্দেশ করার জন্য তাদের একযোগে আপডেট করে।
রেক্স শ্র্রেডার

replicate()সম্পর্ক স্থাপন করবে এবং সম্পর্কের push()পুনরাবৃত্তি করবে এবং তাদের রক্ষা করবে।
ম্যাট কে

এছাড়াও 5.2 এ আপনাকে বাচ্চাদের মধ্য থেকে লুপ করতে হবে এবং একবারে প্রতিলিপি দেওয়ার পরে তাদের সংরক্ষণ করতে হবে; একটি $new_user->roles()->save($oldRole->replicate)
পূর্বাভাস

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

4
সাবধান, শুধুমাত্র একটি অগভীর অনুলিপি, সাব / শিশু অবজেক্টগুলি নয় :-)
আলফা

4
@ দ্য শিফটএক্সচেঞ্জ, আপনি এটি আকর্ষণীয় মনে করতে পারেন , আমি অনেক আগে একটি পরীক্ষা করেছি। থাম্বস আপ করার জন্য ধন্যবাদ :-)
আলফা

4
এটি কি বস্তুর আইডিটিও অনুলিপি করে না? এটি সংরক্ষণের জন্য অকেজো করা?
তোশ

@ তোশ, হ্যাঁ, ঠিক তাই এবং এজন্য আপনাকে অন্য আইডি সেট করতে হবে বা null:-)
দ্য আলফা

4
পিএইচপি গোপনীয়তা প্রকাশের জন্য প্লাস 1: পি
বিপাকীয়

23

লারাভেল 5 এর জন্য হ্যাশ মেল রিলেশন সহ পরীক্ষিত।

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

পুরোপুরি লারাভেল 5.6 কাজ করে অনেক অনেক ধন্যবাদ
আলী আব্বাস

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সমস্ত সম্পর্কের ক্ষেত্রে একই না হয়। ধন্যবাদ, যদিও এটি দরকারী।
ডাস্টিন গ্রাহাম

6

এটি লারাভেল ৫.৮-এ রয়েছে, পুরানো সংস্করণে চেষ্টা করে না

//# 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

কোড বেলো ব্যবহার করে যদি আপনার $ ব্যবহারকারীর নামের একটি সংগ্রহ থাকে তবে এটি সমস্ত সম্পর্ক সহ পুরানোটির কাছ থেকে একটি নতুন সংগ্রহ তৈরি করে:

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

এই কোডটি লারাভেল 5 এর জন্য।


4
আপনি কি শুধু করতে পারবেন না $new = $old->slice(0)?
fubar

2

আপনি যে কোনও সম্পর্কের মাধ্যমে যখন কোনও জিনিস আনেন এবং এর পরে প্রতিলিপি করেন, আপনি পুনরুদ্ধার করা সমস্ত সম্পর্কও প্রতিলিপি হয়ে যায়। উদাহরণ স্বরূপ:

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


2

এখানে এমন একটি বৈশিষ্ট্য যা প্রতারণামূলকভাবে কোনও বস্তুর সমস্ত লোড সম্পর্কের সদৃশ হবে । আপনি অন্যান্য সম্পর্কের ধরণের মতো এটিকে সহজেই প্রসারিত করতে পারেন যেমন সাবটিনার নিজস্ব টোম্যানির উদাহরণ।

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বৈশিষ্ট্যগুলি মুছতে যাতে লারাভেল একটি নতুন রেকর্ড তৈরি করে।

স্ব-সম্পর্কের ক্লোনিং করা একটু কৃপণ কিন্তু আমি একটি উদাহরণ অন্তর্ভুক্ত করেছি। আপনাকে কেবল নতুন আইডিতে পুরানো আইডির ম্যাপিং তৈরি করতে হবে এবং তারপরে পুনরায় সিঙ্ক করতে হবে।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.