Replicating Database Records in Laravel

July 11th, 2021

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' => '127.0.0.1']]);

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\":\"127.0.0.1\"}" // what we get with the method above
{"ip":"127.0.0.1"} // 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);
$visit->replicate()->save();

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';

$replicated->save();

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;

$replicated->save();

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;

$replicated->save();

Happy replicating!

Author
Alex Garrett-Smith

Comments

No coments, yet. Be the first to leave a comment.