Tüm ilişkileri içeren bir Eloquent nesnesi klonlansın mı?


87

Bir Eloquent nesnesini, tüm ilişkileri dahil, kolayca klonlamanın bir yolu var mı?

Örneğin, şu tablolara sahip olsaydım:

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

usersTabloda yeni bir satır oluşturmanın yanı sıra , tüm sütunların aynı olması dışında id, aynı zamandauser_roles aynı rolü yeni kullanıcıya atayarak tabloda .

Bunun gibi bir şey:

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

Kullanıcı modelinin sahip olduğu yerler

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

Yanıtlar:


77

OwnToMany ilişkileri için laravel 4.2'de test edilmiştir

modelin içindeyseniz:

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

3
Laravel 7'de çalıştı
Daniyal Javani

Ayrıca önceki sürüm Laravel 6'da da çalışıyor. (Sanırım önceki yoruma göre bekleniyor :)) Teşekkürler!
mmmdearte

Laravel 7.28.4'te çalıştı. Modelin dışında çalıştırmaya çalışıyorsanız kodun farklı olması gerektiğini fark ettim. Teşekkürler
Roman Grinev

56

Ayrıca eloquent tarafından sağlanan çoğaltma işlevini deneyebilirsiniz:

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
Aslında kopyalamak istediğiniz ilişkileri de yüklemelisiniz. Verilen kod, ilişkileri olmadan yalnızca temel modeli kopyalayacaktır. Yanı ilişkileri klonlamak için, ya ilişkilerinde kullanıcıya alabilirsiniz: $user = User::with('roles')->find(1);ya Modeli var sonra bunları yüklemek: İlk iki satır olacağını bu yüzden$user = User::find(1); $user->load('roles');
Alexander Taubenkorb

2
İlişkilerin yüklenmesi, en azından 4.1'de değil, ilişkileri kopyalıyor gibi görünmemektedir. Ebeveyni kopyaladım, ardından orijinalin alt öğelerini tekrarlamam ve onları yeni ebeveyne işaret etmek için birer birer güncelleştirmem gerekiyordu.
Rex Schrader

replicate()ilişkileri push()kurar ve ilişkilere tekrar eder ve onları kaydeder.
Matt K

Ayrıca 5.2'de çocuklar arasında döngü yapmanız ve her seferinde birini kopyaladıktan sonra onları kaydetmeniz gerekir; bir foreach içinde:$new_user->roles()->save($oldRole->replicate)
d.grassi84

28

Bunu deneyebilirsiniz ( Nesne Klonlama ):

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

Yana clonederin alt nesneler mevcut herhangi bir çocuk nesne varsa kopyalanan ve bu durumda olmayacak şekilde kopyalamaz kullanmakta çocuk nesneyi kopyalamak gerek clonemanuel. Örneğin:

$user = User::with('role')->find(1);
$new_user = clone $user; // copy the $user
$new_user->role = clone $user->role; // copy the $user->role

Sizin durumunuzda nesnelerin rolesbir koleksiyonu olacaktır, Rolebu nedenle Role objectkoleksiyondaki her birinin kullanılarak elle kopyalanması gerekir clone.

Ayrıca, yüklemek yoksa, bunun farkında olması gerekir roleskullanarak withdaha sonra bu yüklenmemiş edilecek veya mevcut olmayacak $userve arayacağım zaman $user->rolessonra bu nesneler o çağrıdan sonra çalışma zamanında yüklenecektir arasında $user->rolesve bu kadar olanlarroles bunlar yüklenmez.

Güncelleme:

Bu cevap Larave-4şimdilik ve şimdi Laravel replicate()yöntem sunuyor , örneğin:

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

2
Dikkatli olun, sadece sığ bir kopya, alt / alt nesneler değil :-)
The Alpha

1
@TheShiftExchange, ilginç bulabilirsin , uzun zaman önce bir deney yaptım. Beğendiğiniz için teşekkürler :-)
The Alpha

1
Bu aynı zamanda nesnenin kimliğini de kopyalamaz mı? Tasarruf için işe yaramaz hale getirmek mi?
Tosh

@Tosh, Evet, aynen ve bu yüzden başka bir kimlik veya null:-)
The Alpha

1
php sırrını ortaya çıkarmak için artı1: P
Metabolic

23

Laravel 5. hasMany ilişkisi ile test edilmiştir.

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

mükemmel çalışıyor laravel 5.6 Çok teşekkürler
Ali Abbas

7

İşte, @ sabrina-gelbart'ın, yayınladığı gibi yalnızca ownToMany yerine tüm hasMany ilişkilerini klonlayacak olan çözümünün güncellenmiş bir sürümü:

    //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_idTüm ilişkiler için aynı değilse zor. Bu yararlı olsa da, teşekkürler.
Dustin Graham

6

Bu laravel 5.8'de, eski sürümde denenmedi

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

düzenleme, sadece bugün 7 Nisan 2019 laravel 5.8.10 yayınlandı

şimdi çoğaltmayı kullanabilir

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

2

Aşağıdaki kodu kullanarak $ user adında bir koleksiyonunuz varsa, eski koleksiyonla aynı olan tüm ilişkileri içeren yeni bir Koleksiyon oluşturur:

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

bu kod laravel 5 içindir.


1
Sadece yapamaz $new = $old->slice(0)mısın?
fubar

2

İstediğiniz herhangi bir ilişkiye göre bir nesneyi getirdiğinizde ve ondan sonra kopyaladığınızda, aldığınız tüm ilişkiler de kopyalanır. Örneğin:

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

Laravel 5.5'te test ettim
elyas.m

2

İşte bir nesne üzerindeki tüm yüklü ilişkileri yinelemeli olarak kopyalayacak bir özellik . Bunu, Sabrina'nın ownToMany örneği gibi diğer ilişki türleri için kolayca genişletebilirsiniz.

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

Kullanım:

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

Diğer çözümler sizi tatmin etmiyorsa, bunu yapmanın başka bir yolu:

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

İşin püf noktası idve existsözelliklerini silmek, böylece Laravel yeni bir kayıt oluşturacaktır.

Kendi kendine ilişkileri klonlamak biraz zor ama bir örnek ekledim. Eski kimliklerden yeni kimliklerle bir eşleme oluşturmanız ve ardından yeniden eşitlemeniz yeterlidir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.