This is part of the Semicolon&Sons Code Diary - consisting of lessons learned on the job. You're in the object-oriented-programming category.
Last Updated: 2025-10-30
I had an infinite loop bug when using an observer connected to the updated
callback in an eloquent ORM mode (for an Advisor - which corresponds to a "tax advisor" in real life).
<?php
class AdvisorObserver
  public function updated(Advisor $advisor)
  {
    // Strip non-digits from phone/fax
    $advisor->phone = preg_replace('~\D~', '', $advisor->phone);
    $advisor->save();
    UpdateAdvisorGeocoordinates::dispatch($advisor);
  }
}
// Wiring up the observer
class AppServiceProvider extends ServiceProvider
{
  public function boot()
  {
    Advisor::observe(AdvisorObserver::class);
  }
}
The reason this went on an infinite loop was because I called save() in the
updated() callback, which triggered this exact same code again. 
I could have avoided this:
isDirty(["phone", "fax"]) - that way there
would be a modification (and a save) after the first iteration, but not the second.saving, with the expectation that the modified data would be saved
during the later call to save() in the lifecycle. But be careful here: if I
were to also dispatch the UpdateAdvisorGeocoordinates work to the queue
in this saving method, then it would receive a stale version of the data
without these (as of the moment) unsaved changes. Better to retain the
dispatching to the queue in the updated method.  <?php
    Advisor::withoutEvents(function () {
      $advisor->save();
    });
Another complication cropped up when code that normally occurs in a background queue is instead executed in-line (i.e. synchronously), as might happen in a unit testing environments. Since, by their nature, a background job has to save its changes, this can cause unexpected test failures and infinite loops.