Есть ли способ легко клонировать объект Eloquent, включая все его отношения?
Например, если бы у меня были эти таблицы:
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();
Где модель User имеет
class User extends Eloquent {
public function roles() {
return $this->hasMany('Role', 'user_roles');
}
}
Вы также можете попробовать функцию репликации, предоставляемую 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();
источник
$user = User::with('roles')->find(1);
или загрузить их после того, как у вас есть модель: так что первые две строки будут$user = User::find(1); $user->load('roles');
replicate()
установит отношения иpush()
рекурсивно перейдет в отношения и сохранит их.$new_user->roles()->save($oldRole->replicate)
Вы можете попробовать это ( клонирование объектов ):
$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
и теперь Laravel предлагаетreplicate()
метод, например:$user = User::find(1); $newUser = $user->replicate(); // ...
источник
null
:-)Для Laravel 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()); } }
источник
Вот обновленная версия решения от @ sabrina-gelbart, которая будет клонировать все отношения hasMany, а не только ownToMany, как она опубликовала:
//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
не одинаково для всех отношений. Хотя это полезно, спасибо.Это в laravel 5.8, не пробовал в более старой версии
//# this will clone $eloquent and asign all $eloquent->$withoutProperties = null $cloned = $eloquent->cloneWithout(Array $withoutProperties)
edit, только сегодня, 7 апреля 2019 года, запущен laravel 5.8.10
теперь можно использовать репликацию
$post = Post::find(1); $newPost = $post->replicate(); $newPost->save();
источник
Если у вас есть коллекция с именем $ user, используя приведенный ниже код, она создает новую коллекцию, идентичную старой, включая все отношения:
$new_user = new \Illuminate\Database\Eloquent\Collection ( $user->all() );
этот код предназначен для laravel 5.
источник
$new = $old->slice(0)
?Когда вы выбираете объект по любому желаемому отношению, а затем реплицируете его, все полученные вами отношения также реплицируются. например:
$oldUser = User::with('roles')->find(1); $newUser = $oldUser->replicate();
источник
Вот черта, которая рекурсивно дублирует все загруженные отношения объекта. Вы можете легко расширить это для других типов отношений, таких как пример Сабрины для ownToMany.
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);
источник
Вот еще один способ сделать это, если другие решения вас не устраивают:
<?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
свойства так , что Laravel создаст новый рекорд.Клонирование самоотношений немного сложно, но я привел пример. Вам просто нужно создать сопоставление старых идентификаторов с новыми идентификаторами, а затем выполнить повторную синхронизацию.
источник