How to Clone Eloquent Model: Create or Replicate

Sometimes, you may need to make a copy of an Eloquent Model object to clone it. For example, you have similar products; the only difference is the specific options inside. I will show you two ways.

The first way is to use the Create form and fill it with the data from the resource you want to clone.

The other way is to use the ->replicate() method on your Model.


Cloning With Create Form

The easiest way to clone your resource is to use the Create form and pass the data from the resource you want to clone:

As you can see in the URL, we have a ?from_id=5 parameter, which tells our Create form to fill the fields with the data from the meal with ID 5:

app/Http/Controllers/MealController.php

use App\Models\Ingredient;
use App\Models\Meal;
use App\Models\Menu;
 
// ...
 
public function create(Request $request)
{
$ingredients = Ingredient::pluck('name', 'id');
$menus = Menu::pluck('name', 'id');
 
if ($request->has('from_id')) {
$meal = Meal::with('ingredients')->find($request->from_id);
}
 
return view('meals.create', [
'ingredients' => $ingredients,
'menus' => $menus,
'meal' => $meal ?? null
]);
}
// ...

This allows us to fill the fields with the data from the resource we want to clone:

resources/views/meals/create.blade.php

<form action="{{ route('meal.store') }}" method="post">
@csrf
 
<div class="mb-4">
<label for="menu_id">Menu</label>
<select name="menu_id" id="menu_id"
class="bg-gray-100 dark:bg-gray-700 border-2 w-full p-4 rounded-lg @error('menu_id') border-red-500 @enderror">
@foreach($menus as $id => $name)
<option value="{{ $id }}"
@if(old('menu_id', $meal?->menu_id) == $id) selected @endif
>{{ $name }}</option>
@endforeach
</select>
 
@error('menu_id')
<div class="text-red-500 mt-2 text-sm">
{{ $message }}
</div>
@enderror
</div>
 
<div class="mb-4">
<label for="name">Name</label>
<input type="text" name="name" id="name" placeholder="Meal name"
class="bg-gray-100 dark:bg-gray-700 border-2 w-full p-4 rounded-lg @error('name') border-red-500 @enderror"
value="{{ old('name', $meal?->name) }}">
 
@error('name')
<div class="text-red-500 mt-2 text-sm">
{{ $message }}
</div>
@enderror
</div>
 
<div class="mb-4">
<label for="price">Price</label>
<input type="text" name="price" id="price" placeholder="Meal price"
class="bg-gray-100 dark:bg-gray-700 border-2 w-full p-4 rounded-lg @error('price') border-red-500 @enderror"
value="{{ old('price', $meal?->price ?? 10.00) }}">
 
@error('price')
<div class="text-red-500 mt-2 text-sm">
{{ $message }}
</div>
@enderror
</div>
 
<div class="mb-4">
<h2>Ingredients</h2>
@foreach($ingredients as $id => $name)
<div>
<input type="checkbox" name="ingredients[]" value="{{ $id }}"
id="ingredient-{{ $id }}"
class="mr-2"
@if(in_array($id, old('ingredients', $meal ? $meal->ingredients->pluck('id')->toArray() : []))) checked @endif
>
<label for="ingredient-{{ $id }}">{{ $name }}</label>
</div>
@endforeach
</div>
 
 
<div class="mb-4">
<button type="submit" class="bg-blue-500 text-white px-4 py-2 rounded font-medium">
Create
</button>
</div>
 
</form>

And by adding a "Copy" button to our list view, we can easily clone our resources:

resources/views/meals/index.blade.php

{{-- ... --}}
<a href="{{ route('meal.create', ['from_id' => $meal->id]) }}"
class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">Copy</a>
{{-- ... --}}

The most significant benefit of this approach is that you can modify the data before saving it to the database, and the saving itself happens with an already existing store method:

app/Http/Controllers/MealController.php

use App\Http\Requests\StoreMealRequest;
use App\Models\Meal;
 
// ...
 
public function store(StoreMealRequest $request)
{
$meal = Meal::create($request->validated());
 
$meal->ingredients()->attach($request->validated('ingredients'));
 
return redirect()->route('meal.index');
}
// ...

We did not have to write any more code to make this work. But what if we have fields that are not in the form? Well, for that, we can use the replicate() method.


Cloning Model With Replicate

The replicate() Eloquent method exists on your Models already, and it creates a new instance of the Model with the same attributes as the original Model.

The only difference is that the new Model has no ID primary key and is not saved to the database. Here's how that looks:

app/Http/Controllers/DuplicateMealController.php

use App\Models\Meal;
 
// ...
 
public function __invoke(Meal $meal)
{
$meal->load(['ingredients']);
 
// Replicate the meal
// This will copy all attributes and relations
$newMeal = $meal->replicate();
 
// Save new Meal to Database
$newMeal->save();
 
// Sync the ingredients
$newMeal->ingredients()->sync($newMeal->ingredients->pluck('id')->toArray());
 
return redirect()->route('meal.edit', $newMeal->id);
}

As you can see, we have a few things in here:

  • We load all the relations on the $meal model - this is important because we want to copy all the relations as well
  • We replicate the $meal model and save it to the $newMeal variable - That's our new hydrated model
  • We save the $newMeal model to the database - That way, we have an ID, and we can use it to assign relations
  • We sync the ingredients - This is important because we also want to copy all the relations. You might notice that we are using the new Model's ingredients to sync. This is possible due to the replicate() method copying all the relations as well as the new model
  • We redirect to the edit page of the new model

That's it! The only thing we need to worry about by using this approach is syncing all the relations manually. This can be tricky if you have many relations, but it's still a viable option. And it will sync all the fields, whether visible on the form or not!

You can even create a button in your list view to duplicate the model:

resources/views/meals/index.blade.php

{{-- ... --}}
<a href="{{ route('meal.duplicate', $meal->id) }}"
class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">Quick duplicate</a>
{{-- ... --}}

routes/web.php

use App\Http\Controllers\DuplicateMealController;
use App\Http\Controllers\MealController;
 
// ...
 
Route::resource('meal', MealController::class);
Route::get('meal/{meal}/duplicate', DuplicateMealController::class)->name('meal.duplicate');
// ...

And that's it! You can now duplicate your models with ease.

avatar

$newMeal->ingredients()->sync($newMeal->ingredients->pluck('id')->toArray());

Call to undefined method Illuminate\Database\Eloquent\Relations\HasMany::sync() The relation HasMany doesn't have the sync method. Sorry, but how to do that action?

avatar

sync is not HasMany method. So, you can not use sync if you defined the relation between Meal and Ingredient one to many. The relation here is many yo many

avatar

Good article

Like our articles?

Become a Premium Member for $129/year or $29/month
What else you will get:
  • 68 courses (1188 lessons, total 43 h 18 min)
  • 90 long-form tutorials (one new every week)
  • access to project repositories
  • access to private Discord

Recent New Courses