2

I have a custom entity. During a save operation, under certain circumstances, I need to change the value of a specific field (not field API) of some other entities (of the same type).

If I perform a db_update on the entity's table, is it safe for caches or for any other reason yet unknown to me?

Alternatively, I suppose I could use a rather complicated select query to retrieve the ID's under concern. Then update each entity separately in a loop or batch.

Which causes my second similar question, is it safe to perform a select query in the middle of this operation? Are there any circumstances where entities have different content in memory or DB cache comparing to the DB values?

  • 1
    I know if your using the entitycache module then you will need to clear the cache after the db update to get the changes to reflect on screen (I just tried it...). – Felix Eve Mar 27 '14 at 16:23
  • @FelixEve thanks. I do not use the entitycache module. I have too tried on my own but I have been wondering if there could be theoretic circumstances for a non-obvious bug. – Wtower Mar 27 '14 at 18:06
5

The thing to look out for is invalidating the correct caches and making sure the correct hooks are called if needed.

When updating entities directly you will need to flush the appropriate caches so the next time the entity is loaded it does not grab the old data from the cache.
entity_load()
-> DrupalDefaultEntityController::load() {uses a static cache by default}
--> DrupalDefaultEntityController::attachLoad()
---> field_attach_load() {uses the cache_field cache bin}.

Once you have that taken care of you will then need to address any 3rd party modules that use hook_entity_load() or hook_TYPE_load() (hook_node_load()). Odds are you won't have to do anything in here but it is best to double check.

Once loading has been addressed you will need to address the hooks that get ran on insert/update to make sure nothing else is done in a different place that you may not know about. hook_entity_update(), hook_entity_insert(), field_attach_presave(), hook_entity_presave(), hook_TYPE_presave() (hook_node_presave()), etc... You can follow the trail of hooks in node_save() as an example. Once again odds are nothing crazy is going to happen in here, but it is best to double check.

In regards to getting ID's entity_load_unchanged() might be what you are looking for.

Most people will tell you it is not "proper" to update fields in the database directly via SQL queries. This is because 3rd party modules might do something with that data or react to it in different ways; also be aware of modules added after you've done this workaround. In reality you can directly update the database as long as you know the risks and how the code works; most people do not and thus using the API is the best answer.

Now for the interesting part. My guess is you're looking to work around the entity api due to speed issues. There are some ways to fix this. Using a background worker to do the updates is my recommendation. register_shutdown_function() when used in combindation with HTTPRL's threading can help. I don't have a good how-to yet but here is a really quick explanation:

httprl_qcinp - Queue Callback to run In a New Process. This takes arguments similar to call_user_func_array(). I'll use a node as a example.

<?php
hook_node_save($node) {
  // Preform update at the end of this request.
  register_shutdown_function('my_shutdown_function', $node);
}

my_shutdown_function($node) {
  // Load other entity that needs to be updated.
  $alt_node = entity_load_unchanged('node', $node->some_field_reference_nid);
  // Update object
  $alt_node->my_field = 'updated info';

  // Queue background node_save.
  // node_save($alt_node);
  httprl_qcinp('node_save', array($alt_node), FALSE);
  // Run it.
  httprl_send_request();
}
?>

At this point you might not even need HTTPRL; register_shutdown_function might do what you need.

EDIT: Also noted that this is a handy guide for just updating a single field http://timonweb.com/how-insert-and-update-only-specific-fields-your-entity-drupal-7

  • Many thanks for the extensive and quite useful answer. My question is certainly about performance. Now, does cache_clear_all('field:my_entity:*', 'cache_bin', TRUE) flush the appropriate caches? How do I flush the static cache regarding to my entity? I suppose module_invoke_all() is the appropriate function to invoke all relevant hooks? The register_shutdown_function() and possibly HTTPRL are needed but in another place. Unfortunately, the particular update needs to be completed before the next part of the procedure goes on. – Wtower Mar 28 '14 at 8:45
  • 1
    DrupalDefaultEntityController::resetCache() Is how to reset the static cache. I think cache_clear_all("field:$entity_type:$id", 'cache_field', FALSE) would do it. If you're looking to invoke hooks as well, you really should just use the API. You should provide a lot more details into the what and why of what your trying to accomplish here. – mikeytown2 Mar 28 '14 at 16:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.