web-dev-qa-db-fra.com

Cloner un objet Eloquent incluant toutes les relations?

Est-il possible de cloner facilement un objet Eloquent, y compris toutes ses relations?

Par exemple, si j'avais ces tables:

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

En plus de créer une nouvelle ligne dans la table users, toutes les colonnes étant identiques, à l'exception de id, elle doit également créer une nouvelle ligne dans la table user_roles, en attribuant le même rôle au nouvel utilisateur.

Quelque chose comme ça:

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

Où le modèle utilisateur a

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

testé dans laravel 4.2 pour les relations d'appartements personnels

si vous êtes dans le modèle:

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

Vous pouvez également essayer la fonction de réplication fournie par éloquent:

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

$user = User::find(1);
$new_user = $user->replicate();
$new_user->Push();
50
Piotr Borek

Vous pouvez essayer ceci ( Object Cloning ):

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

Étant donné que clone ne copie pas en profondeur, les objets enfants ne seront pas copiés si un objet enfant est disponible. Dans ce cas, vous devez copier l'objet enfant à l'aide de clone manuellement. Par exemple:

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

Dans votre cas, roles sera une collection d'objets Role. Chaque Role object de la collection doit donc être copié manuellement à l'aide de clone.

Aussi, vous devez être conscient de cela, si vous ne chargez pas roles avec with, ceux-ci ne seront pas chargés ou ne seront pas disponibles dans le $user et lorsque vous appelerez $user->roles, ces objets seront chargés à l'exécution heure après cet appel de $user->roles et jusqu’à cette date, ces roles ne sont pas chargés.

Mettre à jour:

Cette réponse concernait Larave-4 et Laravel propose désormais la méthode replicate(), par exemple:

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

Pour Laravel 5. Testé avec de nombreuses relations.

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

Voici une version mise à jour de la solution de @ sabrina-gelbart qui clonera toutes les relations hasMany au lieu de la seule, comme elle l'a posté:

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

Si vous avez une collection nommée $ user, en utilisant le code ci-dessous, il crée une nouvelle collection identique à l'ancienne, incluant toutes les relations:

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

ce code est pour laravel 5.

3
Mihai Crăiță

Lorsque vous récupérez un objet selon la relation souhaitée et que vous le répliquez par la suite, toutes les relations que vous avez extraites sont également répliquées. par exemple:

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

C'est dans laravel 5.8, je n'ai pas essayé dans une version plus ancienne

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

modifier, seulement aujourd'hui 7 avril 2019 Laravel 5.8.10 lancé

peut utiliser répliquer maintenant

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

Voici une autre façon de le faire si les autres solutions ne vous apaisent pas:

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

L'astuce consiste à effacer les propriétés id et exists afin que Laravel crée un nouvel enregistrement.

Le clonage des relations de soi est un peu délicat, mais j'ai inclus un exemple. Il vous suffit de créer un mappage d'anciens identifiants sur de nouveaux identifiants, puis de le resynchroniser.

0
mpen