The difference between hook_init()
and hook_node_update()
/hook_node_presave()
is that the node is already in progress of being saved when either of the last hooks are invoked.
Notice how the argument to cust_node_update($node)
in your non-working example is a $node object. In fact, it's a reference to the very node object about to be saved. This is important because only the data actually in that node object will be passed on to the database.
Your first line calls entity_metadata_wrapper()
with the id of that node to create an object oriented wrapper around it. The problem here is that since you're only passing the id of the node, entity_metadata_wrapper()
has no choice but to fetch a copy of that node from the database (or perhaps a cache, but that is irrelevant to the discussion). This newly fetched and wrapped node will be an entirely new object representing the same node, with no connection to the node reference passed to hook_node_update()
or hook_node_presave()
. Altering this new node object has no effect whatsoever on the node object already in progress of being saved. Both objects represent the same node data, but the one you fetched from the database and wrapped with an object oriented interface is a copy of the original node data.
Calling $node_wrapper->save()
, node_save()
or entity_save()
inside hook_node_update()
or hook_node_presave()
is a bad idea because it tells Drupal to save the node already in progress of being saved, which would again invoke those hooks - but this time with a reference to the node object you fetched from the database. Your hook implementation will then again fetch a new object representing the same node from the database, before it has actually been updated, wrap it in an object and try to save it. Endless recursion is a fact and you're likely to suffer the White Screen of Death.
So, how do we fix this?
Jupiter's answer is on the right track, but it also works with hook_node_update()
if that's what you want to use.
Calling entity_metadata_wrapper()
and passing in the entire node to be wrapped will let the wrapper reference and alter the actual node object instance you pass in - containing all the latest information - instead of loading a copy from the database.
Calling $node_wrapper->save()
would still trigger a recursion (this time without creating node copies), but you don't need to care about actually saving things because you're acting on the node reference passed to the hook implementation, which is the very same object Drupal will store in the database once the hook implementation has returned and a few extra tasks have been performed.
There's at least one gotcha when using entity_metadata_wrapper()
in these hooks though, depending on whether other modules use the same hooks - or one invoked later than the one you implemented - to reorganize the node structure into a form that can be saved to the database more easily. The node may, in other words, be structured differently compared to when it's loaded using node_load()
or entity_load()
, depending on which modules you use. The easiest way to find out the structure of the node being saved is to use Devel module's dpm($node, 'node to be saved');
and inspect the structure before actually attempting to modify it.