Score:0

Entity caching and computed fields

ph flag

I have a custom entity that I've added some computed fields to, so the classes involved are:

  • class MyEntity extends ContentEntityBase
  • class ComputedFieldA extends FieldItemList
  • class ComputedFieldB extends FieldItemList

My issue is that I want the computed fields to either not be cached at all, or to be cached but with a different invalidation strategy.

Is what I want possible? Or do I just need to invalidate cache for the whole entity?

Edit: The custom field itself is basically a count of the rows in another database table. I want that count to be cached separately then invalidated if new rows are inserted.

The output is being consumed via the default jsonapi endpoint for the entity.

Edit 2: I guess this is more complex than I thought so I'll describe the entities in more detail:

Entity A:

  • Has a 1:1 relationship with nodes based on UUID
  • Has a count of Entity B entities that are related to the node (computed)
  • Has a calculation based on that count (computed)

Entity B:

  • Has an N:1 relationship with nodes based on NID

So, if a new Entity B is created for node X then I want the cache invalidated on the two computed fields of Entity A for node XXXX-XXXX-XXXX-XXXX so that the jsonapi endpoint shows up to date computed data.

If I can add a cache tag of node:X to each instance of the computed field I expect that would achieve what I want.

4uk4 avatar
cn flag
The edit is more like a new question. Is this other database table also an entity table? If yes, you can use the CRUD hooks of that entity to invalidate this entity or you could add the entity tag of the other entity to the field to have it invalidated automatically on CRUD operations. If no, you have to implement something similar yourself.
4uk4 avatar
cn flag
Edit 2: If this is about different content types the new bundle list tag might be helpful. See https://www.drupal.org/node/3107058
Lambic avatar
ph flag
No it's not about different content types. Entity A and Entity B are pure custom single table entities, but Entity A has a uuid field that refers to a specific node and Entity B has a nid field that refers to the same node.
4uk4 avatar
cn flag
OK, then use the tag `entity_b_list`.
Lambic avatar
ph flag
That would probably work, but I'm not sure where I add that tag. In the entity definition? The FieldItemList implementation? Somewhere else?
4uk4 avatar
cn flag
For a universal field module having such caching needs the most straightforward way is probably a custom normalizer for the FieldItemList which can return a CacheableNormalization object with the cache tag. See my comment under the answer. If you are looking for an easy solution for this specific case you could use a hook `mymodule_entity_a_load()` and add the cache tag to the entity. See https://stackoverflow.com/questions/68523829/how-do-i-control-the-cacheability-of-a-node-rendered-through-jsonapi-in-drupal
Lambic avatar
ph flag
I'll get complained at for using a hook implementation in code review but that seems like the most straightforward solution, thanks!
4uk4 avatar
cn flag
The hook implementation is what I've found at SO. If this works you can add the list tag to the entity class as well, see the edited answer.
Score:3
cn flag

I doesn't matter whether the field is computed. Caching depends on how the field is rendered in the field formatter. Cache metadata added to the render array bubbles up and is merged with that of the other fields and the entity.

Fields by default are not cached on their own, but you can change this. If the field needs a different caching strategy, add cache keys, and the rendered field can be shared between fields in other entity instances rendering with the same cache keys. If the field is highly dynamic then add a lazy builder and make sure it gets placeholdered, either automatically or by setting #create_placeholder.

Edit 2:

The entity class could add a cache list tag for the entity b:

class MyEntity extends ContentEntityBase {

  /**
   * {@inheritdoc}
   */
  public function getCacheTags() {
    $cache_tags = parent::getCacheTags();
    $cache_tags[] = 'entity_b_list';
    return $cache_tags;
  }
Lambic avatar
ph flag
So it sounds like I need to define my own field formatters for the fields I want special caching on? Does it matter that I'm consuming the end result via jsonapi?
4uk4 avatar
cn flag
Then define a FieldNormalizer with `protected $supportedInterfaceOrClass = ComputedFieldA::class;`
mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.