5

Can somebody shine a light into the difference of the two, in terms of their interaction with database when hook_node_insert() is invoked? The documentation explains that

Note that when this hook is invoked, the changes have not yet been written to the database, because a database transaction is still in progress. The transaction is not finalized until the save operation is entirely completed and node_save() goes out of scope.

In fact, when I tried doing node_save($node), it will give me an error.

However, if I was doing it this way,

$nodeWrapper = entity_metadata_wrapper('node', node_load($node->nid));
$nodeWrapper->title->set(t("Saved by Entity Save"));
$nodeWrapper->save();

It works just fine. Has the new node being committed into the database after the $nodeWrapper->save() was executed?

5
  • I edited the title. The code you posted is about entity_metadata_wrapper(). entity_save() is different, and I think this matches you question better.
    – mpdonadio
    Commented Feb 2, 2014 at 23:29
  • @rwaery What error message do you get?
    – berliner
    Commented Feb 3, 2014 at 1:53
  • @rwaery It seems strange to me to call node_save() inside a hook_node_insert(), as this hook is triggered from node_save(). Smells bad. If you just want to alter the data before it get's saved, use hook_node_presave().
    – berliner
    Commented Feb 3, 2014 at 1:56
  • @berliner, thanks for looking into this. Exactly my thoughts. The error was about integrity constraint violation. Initially I had the same questions as the other posters on hook_node_insert() [doc page] (api.drupal.org/api/drupal/modules!node!node.api.php/function/…), on whether or not to trust the nid contains in $node object while still inside hook_node_insert() scope. Say, I wanted to trigger an email with link to the new node, and trigger it from hook_node_insert() can I be sure that the node is already committed to database?
    – rwaery
    Commented Feb 3, 2014 at 2:07
  • Technically you can't. It is clearly stated that the node is not yet finally saved in the database at the time when hook_node_insert is invoked. Practically though, I'm not aware of any other hook based solution to react on a node being created, and usually there should be no reason for a transaction rollback. The note in the docs is also mostly about not tying to reload node data from the database, as it's not yet written.
    – berliner
    Commented Feb 3, 2014 at 2:17

3 Answers 3

2

Essentially, there really isn't much difference between node_save() and the save() method of the entity metadata wrapper. Neither should however be used to save the node passed in as the argument to hook_node_insert().

The argument is passed by reference (since it's an object and all objects are passed by reference in later PHP versions), and any changes you make to it will be included when the node is saved by Drupal just after it's done calling the hook implementation.

Trying to manually save the same node again could trigger endless recursion into your hook_node_insert() implementation.

Just alter the node object as you see fit (setting the title in this case, though that is not a translatable string, so replace t() with check_plain()) and do nothing more.

If you need to do more complex alterations, the entity metadata wrapper could come in handy, but you must remember that any alterations you want to have saved must be performed on the node instance passed as a reference to hook_node_insert(). This means you can't node_load($node->nid) here first, since that would actually just create a new node object with data that's already in the database. If you do manage to save that node copy without ending up in a recursion loop, it would either mean that the node save already in progress - the reason hook_node_insert() was originally called - overwrites any changes you made, or fails because the new node to be saved already exists.

Instead, pass the $node argument to entity_metadata_wrapper() directly and the wrapper will be able to alter the correct node object for you, but remember to not call save() on the wrapper!

1

Calling node_save() from inside a hook_node_insert() implementation is error prone and should only be done with sufficient condition checks as to not provoke function nesting.

When you call node_save() coming from an insert operation and saving the $node object that already has an nid, then this call of node_save probably creates an update operation, which might result in strange errors. I don't fully follow the information flow here, but I would guess that one of the two calls to node_save() can easily run into a problem.

The correct way in a scenario where you want to alter node properties (e.g. status, promote, title, ...) prior to inserting, is to use hook_node_presave(). If you want to update field values inside a hook_node_insert(), that should be done by calling field_attach_update('node', $node).

Using the save() method of entity_metadata_wrapper doesn't exactly feel good either. Looking at the code in EntityDrupalWrapper::save, which I suppose is the wrapper class that is responsible, it looks like node_save() is going to be called in the end too. But I have never really used Entity Wrapper, so I'm a bit short here.

Edit: Regarding entity_metadata:_wrapper, looking again at the code of entity_save() I realize that it's not node_save() that is called in the end, but that this is entirely handled by the Entity module. At the end it will end up here if I'm not mistaken: EntityAPIController::save. I'm not sure though how that explains the behavior described in the comment.

2
  • I did a couple of tests, and found the following results. node_save($node) and entity_save('node', $node) returns integrity violation error. We both see that $nodeWrapper->save() eventually calls entity_save('node', $node), this is what confuses me, since $nodeWrapper->save() works okay. Oh and, doing $nodeWrapper->save(), and follows by node_save($node), I thought, would be okay, thinking that the new node is already saved by entity wrapper, and will only be updated when node_save() is invoked but no, it also returned the same integrity violation error.
    – rwaery
    Commented Feb 3, 2014 at 2:53
  • @rwaery I have updated the answer
    – berliner
    Commented Feb 3, 2014 at 9:29
0

The reason why node_save($node->nid) gives you an error is because node_save takes node object to be saved, not a node id. Just change it to node_save($node) and it will work just fine.

In simple terms, node save is just a specific kind of entity save and just makes your life easier by doing things that you would have to do yourself (like in your case, node_save is one line, saving the entity does the same thing but you have to deal with metadata wrapper unnecessarily).

1
  • Thanks for pointing that out. I didn't type it correctly, it did use node_save($node) inside hook_node_insert() it returns, Integrity Constraints Violation. I have updated the question accordingly.
    – rwaery
    Commented Feb 3, 2014 at 1:51

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.