July 11th, 2021 by Alex Garrett-Smith

Replicating Database Records in Laravel

I recently found myself needing to replicate a database record in Laravel. Turns out, there's a really easy way to achieve this.

First, let's take a look at a naive way we might replicate a record.

The naive approach

Imagine we had a Visit model for analytics that contained some json data about the visit.

Visit::create(['data' => ['ip' => '']]);

One method of replicating it might be the following.

$visit = Visit::find(1);
$replicateVisit = Visit::create($visit->getAttributes());

This does replicate the record in the database, but with a nasty side effect. The data column is actually cast to json.

protected $casts = [
'data' => 'json'

This means the replicate data property ends up escaped when inserting back into the replicated record.

"{\"ip\":\"\"}" // what we get with the method above
{"ip":""} // what we need

No doubt there's a way around this, but we're not even going to explore it.

The solution

What you came for.

Here's how we replicate a record nicely in the database, without any side effects.

$visit = Visit::find(1);

Notice the save call. That's because the replicate method just replicates the existing model and doesn't persist it.

The benefit to having to explicitly save a record is we can modify parts of the replicated record before it goes back into the database.

$replicated = $visit->replicate();
$replicated['data->ip'] = 'localhost';

The data->ip array access is just because we're working with json data here. For normal database columns, you'd just access them as a property.

$replicated = $visit->replicate();
$replicated->verified = true;

Easy and clean.

A quick note on timestamps

When you replicate a record, the created_at and updated_at timestamps will represent the date/time that the new record was created. Those columns are excluded from the replication.

Of course, if you wanted to match those for any reason, you'd do something like this.

$visit = Visit::find(1);
$replicated = $visit->replicate();
$replicated->created_at = $visit->created_at;
$replicated->updated_at = $visit->updated_at;

Happy replicating!