2

I'm using this piece of code to programmatically alter a node field:

    $node_wrapper = entity_metadata_wrapper('node', 93);
    $node_wrapper->field_field4->set("text");
    $node_wrapper->save();

This code works when I'm testing with hook_init: Eg:

    function cust_init() 
    {
        $node_wrapper = entity_metadata_wrapper('node', 93);
        $node_wrapper->field_field4->set("text");
        $node_wrapper->save(); 
    }

But it does not work when being called via hook_node_update or hook_node_presave or from a form submit handler. For example, after running this code:

function cust_node_update($node) 
{
        $node_wrapper = entity_metadata_wrapper('node', $node->nid);
        $node_wrapper->field_field4->set("hello");
        $node_wrapper->save();  
}

I'm expecting to see a change in my node, but I don't.

Is the hook_node_update the right place where this code should be added? What I need to do is to update a field of a node after a node is being updated.

Thank You.

4 Answers 4

3

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.

1

I have found that hook_node_presave() is more useful when setting node values with a wrapper.

This works for me:

function X_node_presave($node) {
  $wrapper = entity_metadata_wrapper('node', $node);
  $wrapper->field_name->set(VALUES);
}

No need to call anything else.

0

I'm not sure, but it looks like that updates hooks retrieves $node by reference (& - symbol, Im not so good in english :)). You update node via wrapper, but forget to update $node variable. So, after each update hooks will be executed, $node will be saved second time.

Try to do not use metadata wrapper in node hooks, instead of this use $node->field_field4[LANGUAGE_NONE][0]['value'] = 'text';.

UPD:

You can try overload node object after wrapper save() execution.

$node_wrapper->save();
$node = $node_wrapper->value();

And there is also some feature with cache by node_update(). ;)

4
  • In this case, why it works in hook_init without saving the node? node_wrapper->save() is performing the actual save.
    – rungurean
    Commented Aug 23, 2013 at 13:53
  • node wrapper saves node, but than node also saved by node_update(). So you can try to overload node object, see upd above. Commented Aug 23, 2013 at 14:08
  • I understand. You're right. The node needs to be overloaded. I have a different problem now. I'm getting: Allowed memory size of 134217728 bytes exhausted
    – rungurean
    Commented Aug 25, 2013 at 10:41
  • Upvote plz. Answer to second question is obviously "Just increase allowed momory size in php.ini or try to find memory leaks" :) Commented Aug 25, 2013 at 13:18
0

I fixed the problem using field_attach_update function.

$raw_collection = $node_wrapper->field_additional_sections[$i]->value();

...

//perform the field updates

field_attach_update('field_collection_item', $raw_collection);

//overwrite the node object

$node = $node_wrapper->value();

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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