Laravel Eloquent Essentials: wasChanged() vs isDirty() vs getOriginal()

 How to use wasChanged(), isDirty(), and getOriginal() in Laravel Eloquent

Laravel Eloquent Essentials: wasChanged() vs isDirty() vs getOriginal()


When working with Eloquent models, you sometime needs to inspect what is happening to model attributes before or after they are saved to the database.

For Example:

  • Did the user change their email address? (If yes, send a re-verification link).
  • Was the order status changed to "Completed"? (If yes, trigger a invoice PDF generation).
  • What was the old price before the update happened?

Laravel provides three super helpful methods to inspect state changes on a model: isDirty(), wasChanged(), and getOriginal().

While they seem similar, the key difference comes down to timing: Are you inspecting the model BEFORE or AFTER calling $model->save()?

Here is a step-by-step guide to mastering these three inspection tools.

The Timeline: Before vs. After $model->save()

To understand which method to use, keep this simple execution timeline in mind:


[Fetch Model] ──► [Modify Attributes] ──► [CALL ->save()] ──► [After Save]
                          │                                         │
                   Use isDirty()                            Use wasChanged()
                 & getOriginal()                           & getOriginal()

1. isDirty() — Inspecting BEFORE Saving

The isDirty() method checks if any attributes on the model instance have been modified in memory since the model was loaded, but BEFORE the save() method is called.

$user = User::find(1);
echo $user->name; // Output: 'John'
$user->name = 'Jane';

// Check if the model has unsaved changes
if ($user->isDirty()) {
    echo "The user model has unsaved changes!";
}

// You can also check a specific attribute
if ($user->isDirty('name')) {
    echo "The name was modified!";
}

if ($user->isDirty('email')) {
    echo "The email was NOT modified."; // Will not run
}

Opposite Method: isClean()

Laravel also gives you isClean(), which is the exact opposite of isDirty(). It returns true if the model (or a specific attribute) has not been changed in memory.

if ($user->isClean('email')) {
    echo "Email hasn't changed yet.";
}

2. wasChanged() — Inspecting AFTER Saving

Once you call $user->save(), Eloquent writes the changes to MySQL and resets the model's "dirty" state. At this point, isDirty() will return false.

So how do you know what changed during that save operation? You use wasChanged().

$user = User::find(1); // Currently name = 'John'

$user->name = 'Jane';
$user->save();

// AFTER saving
var_dump($user->isDirty('name'));   // Output: false
var_dump($user->wasChanged('name')); // Output: true

Why Is This Useful?

This is extremely useful in Model Observers or Events (like updated) where you want to execute side effects only if a specific column was modified during the update.

// Inside a UserObserver 'updated' method:
public function updated(User $user): void
{
    // Only send a verification link if the email column actually changed
    if ($user->wasChanged('email')) {
        Mail::to($user->email)->send(new VerifyNewEmailMail($user));
    }
}

3. getOriginal() — Inspecting the Old Values

What if you need to know what a value was before it got modified?

The getOriginal() method returns the original attribute value that was fetched from the database when the model was first instantiated.

$product = Product::find(10); // Currently price = 100

$product->price = 150; // Modified in memory

// Check old vs new BEFORE saving
echo $product->price;                  // Output: 150
echo $product->getOriginal('price');   // Output: 100

You can also call getOriginal() after saving to inspect what the value used to be before the save happened:

$product->price = 200;
$product->save();

// Even after saving, Eloquent keeps track of the original values during the request lifecycle:
echo $product->getOriginal('price'); // Output: 150 (Value before this save)

If you call $product->getOriginal() without passing a column name, it returns an array of all original attributes.

Real-World Example: Price Audit Trail

Here is how you can combine all three in a real-world scenario (like logging price changes for an e-commerce store).

public function updatePrice(Product $product, float $newPrice)
{
    $product->price = $newPrice;

    // 1. Check if the price actually changed before hitting the database
    if ($product->isDirty('price')) {
        $oldPrice = $product->getOriginal('price');
        $product->save();

        // 2. Verify save succeeded and write to audit log
        if ($product->wasChanged('price')) {
            PriceLog::create([
                'product_id' => $product->id,
                'old_price'  => $oldPrice,
                'new_price'  => $product->price,
            ]);
        }
    }
}


Thank you for reading this article 😊

For any query do not hesitate to comment 💬


Previous Post Next Post

Contact Form