DEV Community

Josh Pike
Josh Pike

Posted on

Don't touch parent timestamps, just this once.

I have a couple models related to each other. We'll call them Parent and Child. Here's the child...

class Child extends Model
{
  protected $touches = ['parent'];

  public function parent()
  {
    return $this->belongsTo(Parent::class);
  }
}
Enter fullscreen mode Exit fullscreen mode

I ran into a situation where I create the parent model, save it to the db, then loop over some inputs, creating the children. But if that takes long enough (it usually does), the parent’s updated_at no longer matches its created_at. This was an issue for me because I'm showing the user an icon if the db record has been updated. With actual updates to the children, I want it to happen, but on the initial save, I don’t.


Laravel includes a couple undocumented ways to not touch the parent timestamps, even when you've set up your model to do so.


The base Model includes a static method to turn off the touching.

/**
 * Disables relationship model touching for the current class during given callback scope.
 *
 * @param  callable  $callback
 * @return void
 */
 public static function withoutTouching(callable $callback)
 {
   static::withoutTouchingOn([static::class], $callback);
 }
Enter fullscreen mode Exit fullscreen mode

Although it didn't strike me as terribly intuitive, in my case I can toss this in the controller method responsible for saving a form.

$parent = new Parent();
// set some props...
$parent->save();

foreach ($request->input('child-data') as $childData) {
  Parent::withoutTouching(function() use ($parent) {
    $child = new Child();
    $child->parent_id = $parent->id;
    $child->data = $childData;
    $child->save();
  }
}
Enter fullscreen mode Exit fullscreen mode

This works to keep the creation of child records from updating the parent's updated_at value. Nice!


There is another, and in my opinion, much simpler way to accomplish this. The base Model's save() method accepts an optional $options array as an argument. As far as I can tell, there is currently only one $options key that the framework checks for. Lucky for us, it allows us to turn off touching!

Instead of nesting the child logic inside the callback of the static method, you can just pass an options array to the save method

$parent = new Parent();
// set some props...
$parent->save();

foreach ($request->input('child-data') as $childData) {
  $child = new Child();
  $child->parent_id = $parent->id;
  $child->data = $childData;
  $child->save(['touch' => false]);
}
Enter fullscreen mode Exit fullscreen mode

This one seems more intuitive to me, and definitely less complicated. Nicer!

Top comments (0)