11 months ago

Cache result of accessor on model?

Posted 11 months ago by miwal

I have an active record class (eloquent model).

Obviously (and by default) it has fields on it which correspond directly to columns on the underlying database table.

But now I want to be able to access something on it that does not correspond to an underlying database row, but, rather, is a transformation of what's available on a relationship. In the first case this can of course simply be a function on the model.

We have a $series, to which belongs many nodes (or let's say, $episodes). $series->episodes gets all of them. But they also have an ordering. It's a linked list structure I'm using but that doesn't matter here, except to say that's why I want a concept of $sequence, which is the nodes that come back (in random order) on the relationship, put into their correct order.

So, while $series->episodes gives random order of episodes, I also need this concept of $sequence. It's a standard linked list sort that I'm using there. quite beautiful code :).

So, my front end code (and tests) are going to be using $series, and also $sequence.

What I can of course do is load each of these variables separately and that will be fine, they'll load once each.

But I was looking at whether to access sequence as a property of series, i.e. $series->sequence

Currently it's $series->sequence(), a method. Every time I access that, it calls the database (I actually have it cached in redis, now, though). But it still calls redis. I'd like it to keep the result on the variable. (it's slightly confusing what to call this - "caching model attributes"?)

Of course, this idea comes from the way eloquent gives us relationships. We load them once, then they remain as properties on the model.

I found this old answer from 2014. I implemented it and it does work, but it seems a bit hacky to me.

What it does is set the attribute concerned as a private property initialised to false, so a conditional check on it initially fails, that causes the first load. Then on subsequent calls, since the conditional passes, & it just gives back the property.

Why don't I like this technique? The initial property is private, so if you do the load then echo out the model concerned in tinker, it looks like "the relationship" ( ...not a relationship, of course, though) isn't loaded, as it's not listed as a property in the normal way. If you dd(), though, it's there, but private.

All this just smells a bit wrong in terms of not interacting with eloquent correctly.

Wondering what I'm missing here and what a neat way to achieve it could be.

Please sign in or create an account to participate in this conversation.