Czy istnieje sposób na łatwe sklonowanie elokwentnego obiektu, w tym wszystkich jego relacji?
Na przykład, gdybym miał takie tabele:
users ( id, name, email )
roles ( id, name )
user_roles ( user_id, role_id )
Oprócz utworzenia nowego wiersza w users
tabeli, z takimi samymi kolumnami, z wyjątkiem id
, należy również utworzyć nowy wiersz w user_roles
tabeli, przypisując tę samą rolę nowemu użytkownikowi.
Coś takiego:
$user = User::find(1);
$new_user = $user->clone();
Gdzie model użytkownika ma
class User extends Eloquent {
public function roles() {
return $this->hasMany('Role', 'user_roles');
}
}
Możesz także wypróbować funkcję replikacji dostarczoną przez eloquent:
http://laravel.com/api/4.2/Illuminate/Database/Eloquent/Model.html#method_replicate
$user = User::find(1); $new_user = $user->replicate(); $new_user->push();
źródło
$user = User::with('roles')->find(1);
lub załadować je po uzyskaniu modelu: więc pierwsze dwie linie będą wyglądały tak$user = User::find(1); $user->load('roles');
replicate()
ustawi relacje ipush()
powróci do relacji i zapisze je.$new_user->roles()->save($oldRole->replicate)
Możesz spróbować tego ( klonowanie obiektów ):
$user = User::find(1); $new_user = clone $user;
Ponieważ
clone
nie kopiuje głęboko, więc obiekty podrzędne nie zostaną skopiowane, jeśli jest dostępny jakikolwiek obiekt podrzędny, aw tym przypadku musisz skopiować obiekt podrzędnyclone
ręcznie. Na przykład:$user = User::with('role')->find(1); $new_user = clone $user; // copy the $user $new_user->role = clone $user->role; // copy the $user->role
W twoim przypadku
roles
będzie to zbiórRole
obiektów więc każdyRole object
w kolekcji musi być skopiowany ręcznie za pomocąclone
.Ponadto musisz być tego świadomy, jeśli nie załadujesz pliku
roles
using,with
wtedy te nie zostaną załadowane lub nie będą dostępne w$user
wywołaniu, a kiedy zadzwonisz,$user->roles
te obiekty zostaną załadowane w czasie wykonywania po tym wywołaniu od$user->roles
a do tego teroles
nie są ładowane.Aktualizacja:
Ta odpowiedź była dla
Larave-4
i teraz Laravel oferujereplicate()
metodę, na przykład:$user = User::find(1); $newUser = $user->replicate(); // ...
źródło
null
:-)Dla Laravel 5. Testowane z relacją 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()); } }
źródło
Oto zaktualizowana wersja rozwiązania z @ sabrina-gelbart, która sklonuje wszystko hasMany, a nie tylko nalezy do przynależności, jak napisała:
//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(); } }
źródło
some_parent_id
nie jest takie samo dla wszystkich relacji. Jest to jednak przydatne, dzięki.To jest laravel 5.8, którego nie próbowałem w starszej wersji
//# this will clone $eloquent and asign all $eloquent->$withoutProperties = null $cloned = $eloquent->cloneWithout(Array $withoutProperties)
edytuj, właśnie dzisiaj 7 kwietnia 2019 ruszył laravel 5.8.10
może teraz użyć replicate
$post = Post::find(1); $newPost = $post->replicate(); $newPost->save();
źródło
Jeśli masz kolekcję o nazwie $ user, używając poniższego kodu, tworzy nową kolekcję identyczną ze starą, zawierającą wszystkie relacje:
$new_user = new \Illuminate\Database\Eloquent\Collection ( $user->all() );
ten kod dotyczy laravel 5.
źródło
$new = $old->slice(0)
?Kiedy pobierasz obiekt za pomocą dowolnej relacji, którą chcesz, a następnie replikujesz, wszystkie pobrane relacje również są replikowane. na przykład:
$oldUser = User::with('roles')->find(1); $newUser = $oldUser->replicate();
źródło
Oto cecha, która rekurencyjnie powiela wszystkie załadowane relacje na obiekcie. Możesz łatwo rozszerzyć to na inne typy relacji, takie jak przykład Sabriny dla nalezy do Mnie.
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}); } } }
Stosowanie:
//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);
źródło
Oto inny sposób, aby to zrobić, jeśli inne rozwiązania Cię nie uspokajają:
<?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); }
Sztuczka polega na wyczyszczeniu właściwości
id
iexists
, aby Laravel utworzył nowy rekord.Klonowanie relacji między sobą jest trochę skomplikowane, ale podałem przykład. Musisz tylko utworzyć mapowanie starych identyfikatorów na nowe, a następnie ponownie zsynchronizować.
źródło