touch Laravel model touch relationships in depth explanations

update updated_at of a Laravel eloquent model to the current timestamp, update relationships after the model is saved, save() options

Icons: touch by Gregor Cresnar, clock by Zahroe — the Noun Project

As you know “touch” is a command in linux which let you change the “time of access” and “modification” of an existing file and folder. Refer

touch filename

Touching a Laravel eloquent model has a similar concept. It means renewing the updated_at timestamp of a record and is very simple to do.

Imagine a scenario when I have a library of many articles and want to record the last time of opening (reading) the articles. For this purpose we can use the updated_at field in the table associated to Article eloquent model.

$article = Article::find($id);$article->touch();

As soon as the $article instance is touched, the updated_at time of $article will be updated to the current timestamp.

There is a HasTimestamps.php trait in Illuminate\Database\Eloquent\Concerns here

In HasTimestamps.php trait there is a touch() method which is responsible for checking whether the model uses timestamp or not, if so, then it will update that instance’s timestamp.

We can touch as well one or more eloquent models which are in relationship (Laravel relationships) with an interested model as soon as the model is saved.

When a model belongsTo or belongsToMany another model, such as a Comment which belongs to a Post, it is sometimes helpful to update the parent’s timestamp when the child model is updated. One of the advantages of touching associated model (touching is available for any kind of relationship) is that it is more convenient to know when to invalidate a cache of a model, for the cases we are caching data related to specific instance of a model using a timestamp-dependent key (in controller caching a query or in model like here). When the key will be renewed based on the new timestamp so there is no cache and it must be generated.

Methodology:

As an example, when a Comment model is updated, you may want to automatically “touch” the updated_at timestamp of the owning Post.

Normally, we have:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Comment extends Model
{


/**
* Get the post that the comment belongs to.
*/
public function post()
{
return $this->belongsTo('App\Post');
}
}

for updating an instance of Comment model, we may do:

$comment->text = "something new";
$comment->save();

As soon as the save() method is triggered and if the model is successfully saved, we need to do a few more things once that is done and the following method:

$this->finishSave($options)

is triggered automatically. You can find this in save() method inside Model.php class here.

As you see, there is $options array which is passed to finishSave($options) method.

This $options is what is passed originally to save() method.

// We can do:$comment->save($options);// But usually we don’t pass any array! 

In Model.php class and in the definition of finishSave() here, you can find this method:

$this->touchOwners();

As you see above, one of the keys of the $options array could be: touch

As I see it had some other keys in the older versions of Laravel and only “touch” is remained!

It means we can do this:

$comment->save(['touch'=>$value]); // The $value is boolean which can be true/false
  • If we set false we don’t let touchOwners() method be triggered and there is no permission for touching the other associated models.
  • As you see above, if we do not pass this array to the save() method, the value for touch key will be assumed as true! And by default, touching associated models is permitted.

Let’s go to the definition of this method:

$this->touchOwners();

There is a trait file HasRelationships.php here and you can see the definition of touchOwners.

As you see above, there is a need to an array with the name of touches. While We do not usually define $touches property in each model definition.

For this aim, we just add a touches property containing the names of the relationships methods to the child model. And everything else happens automatically.

<?phpnamespace App;use Illuminate\Database\Eloquent\Model;class Comment extends Model
{
/**
* All of the relationships to be touched.
*
* @var array
*/
protected $touches = ['post'];
/**
* Get the post that the comment belongs to.
*/
public function post()
{
return $this->belongsTo('App\Post');
}
}

Here we have post() method so we set $touches = [‘post’];

Then the foreach loop inside touchOwner method easily touches each associated model with simple touch() method.

public function touchOwners()
{
foreach ($this->touches as $relation) {
$this->$relation()->touch();
....
....
}
}

Note I:

You can also define touches property in the parent model definition and not only child model definition and then touch some of the associated child models as soon as the parent model is updated.

Note II:

In case there is only one relationship or you do not want to define “touches” if the parent or child model are easily in hand, we can simply use touch() for those models. For more relationship, it is worth to define “touches” property.

Posting daily about Python, Nuclear Physicist PhD