$ lexprog.com

// notes from an old coder -- php, databases, and the occasional rant

[December 13, 2025] Eloquent ORM

Eloquent Replication: Cloning Models

Eloquent Replication: Cloning Models

────────────────────────────────────────────────────────

Eloquent Replication: Cloning Models

Tip: replicate() for Quick Cloning

$newPost = $post->replicate();
$newPost->title = 'Copy of ' . $post->title;
$newPost->save();

Creates a copy without the ID and timestamps.

Gotcha: replicate() Doesn't Clone Relations

$newPost = $post->replicate();
$newPost->push(); // Save first

foreach ($post->tags as $tag) {
    $newPost->tags()->attach($tag);
}

Relations must be copied manually.

Tip: Exclude Specific Attributes

$newPost = $post->replicate(['slug', 'published_at']);

Skips the listed attributes during replication.

Gotcha: Unique Constraints

Cloning a model with a unique slug will fail on save. Generate a new unique value first.

Tip: Deep Clone with Relations

public function cloneWithComments(): Post
{
    $clone = $this->replicate();
    $clone->push();

    foreach ($this->comments as $comment) {
        $clone->comments()->create($comment->replicate()->toArray());
    }

    return $clone;
}

Gotcha: push() vs save()

push() saves the model AND its loaded relations. save() only saves the model.

Tip: Use cursor() for Memory-Neutral Iteration

When exporting 100K rows, get() loads everything into memory. cursor() uses yield and keeps memory flat regardless of row count. Perfect for artisan commands.

Tip: whereHas() vs load() — Two Different Things

whereHas() filters the parent query by relationship existence. load() eager-loads relationships AFTER the query. Mixing them up is a common source of logic bugs.

Gotcha: withCount() Adds a Subquery

withCount('comments') runs a correlated subquery on every row. On large tables, this can be slower than a separate query. Profile before relying on it.

Senior Insight

The most common Eloquent mistake I've seen across dozens of codebases is treating models as plain data containers. Models are active record objects — they have lifecycle, events, and side effects. Passing a model to a queued job serializes it. Saving a model triggers observers. Deleting a model may cascade to related records. Understanding Eloquent's lifecycle is essential for writing predictable code. When in doubt, use a DTO or plain array instead of a model for data that crosses system boundaries.

Source: Laravel Docs (https://laravel.com/docs/eloquent), Laravel News (https://laravel-news.com/), Freek.dev (https://freek.dev/tags/eloquent)

────────────────────────────────────────────────────────
<-- back to posts