function node_save

You are here

7 node.module node_save($node)
4.6 node.module node_save($node)
4.7 node.module node_save(&$node)
5 node.module node_save(&$node)
6 node.module node_save(&$node)

Saves changes to a node or adds a new node.

Parameters

$node: The $node object to be saved. If $node->nid is omitted (or $node->is_new is TRUE), a new node will be added.

23 calls to node_save()
book_admin_edit_submit in modules/book/book.admin.inc
Form submission handler for book_admin_edit().
callback_batch_operation in modules/system/form.api.php
Perform a single batch operation.
CommentInterfaceTest::setEnvironment in modules/comment/comment.test
Re-configures the environment, module settings, and user permissions.
CommentRSSUnitTest::testCommentRSS in modules/comment/comment.test
Test comments as part of an RSS feed.
DrupalWebTestCase::drupalCreateNode in modules/simpletest/drupal_web_test_case.php
Creates a node based on default settings.

... See full list

1 string reference to 'node_save'
menu_test_menu in modules/simpletest/tests/menu_test.module
Implements hook_menu().

File

modules/node/node.module, line 1076
The core that allows content to be submitted to the site. Modules and scripts may programmatically submit nodes using the usual form API pattern.

Code

function node_save($node) {
  $transaction = db_transaction();

  try {
    // Load the stored entity, if any.
    if (!empty($node->nid) && !isset($node->original)) {
      $node->original = entity_load_unchanged('node', $node->nid);
    }

    field_attach_presave('node', $node);
    global $user;

    // Determine if we will be inserting a new node.
    if (!isset($node->is_new)) {
      $node->is_new = empty($node->nid);
    }

    // Set the timestamp fields.
    if (empty($node->created)) {
      $node->created = REQUEST_TIME;
    }
    // The changed timestamp is always updated for bookkeeping purposes,
    // for example: revisions, searching, etc.
    $node->changed = REQUEST_TIME;

    $node->timestamp = REQUEST_TIME;
    $update_node = TRUE;

    // Let modules modify the node before it is saved to the database.
    module_invoke_all('node_presave', $node);
    module_invoke_all('entity_presave', $node, 'node');

    if ($node->is_new || !empty($node->revision)) {
      // When inserting either a new node or a new node revision, $node->log
      // must be set because {node_revision}.log is a text column and therefore
      // cannot have a default value. However, it might not be set at this
      // point (for example, if the user submitting a node form does not have
      // permission to create revisions), so we ensure that it is at least an
      // empty string in that case.
      // @todo: Make the {node_revision}.log column nullable so that we can
      // remove this check.
      if (!isset($node->log)) {
        $node->log = '';
      }
    }
    elseif (!isset($node->log) || $node->log === '') {
      // If we are updating an existing node without adding a new revision, we
      // need to make sure $node->log is unset whenever it is empty. As long as
      // $node->log is unset, drupal_write_record() will not attempt to update
      // the existing database column when re-saving the revision; therefore,
      // this code allows us to avoid clobbering an existing log entry with an
      // empty one.
      unset($node->log);
    }

    // When saving a new node revision, unset any existing $node->vid so as to
    // ensure that a new revision will actually be created, then store the old
    // revision ID in a separate property for use by node hook implementations.
    if (!$node->is_new && !empty($node->revision) && $node->vid) {
      $node->old_vid = $node->vid;
      unset($node->vid);
    }

    // Save the node and node revision.
    if ($node->is_new) {
      // For new nodes, save new records for both the node itself and the node
      // revision.
      drupal_write_record('node', $node);
      _node_save_revision($node, $user->uid);
      $op = 'insert';
    }
    else {
      // For existing nodes, update the node record which matches the value of
      // $node->nid.
      drupal_write_record('node', $node, 'nid');
      // Then, if a new node revision was requested, save a new record for
      // that; otherwise, update the node revision record which matches the
      // value of $node->vid.
      if (!empty($node->revision)) {
        _node_save_revision($node, $user->uid);
      }
      else {
        _node_save_revision($node, $user->uid, 'vid');
        $update_node = FALSE;
      }
      $op = 'update';
    }
    if ($update_node) {
      db_update('node')->fields(array('vid' => $node->vid))->condition('nid', $node->nid)->execute();
    }

    // Call the node specific callback (if any). This can be
    // node_invoke($node, 'insert') or
    // node_invoke($node, 'update').
    node_invoke($node, $op);

    // Save fields.
    $function = "field_attach_$op";
    $function('node', $node);

    module_invoke_all('node_' . $op, $node);
    module_invoke_all('entity_' . $op, $node, 'node');

    // Update the node access table for this node.
    node_access_acquire_grants($node);

    // Clear internal properties.
    unset($node->is_new);
    unset($node->original);
    // Clear the static loading cache.
    entity_get_controller('node')->resetCache(array($node->nid));

    // Ignore slave server temporarily to give time for the
    // saved node to be propagated to the slave.
    db_ignore_slave();
  }
  catch (Exception $e) {
    $transaction->rollback();
    watchdog_exception('node', $e);
    throw $e;
  }
}

Comments

Probably functions like this should return a value. For example, TRUE on success, FALSE otherwise. Or resulting node object.

Was just looking the documentation for that exactly...

The $node object passed in should be an object. Objects are implicitly passed by reference in PHP. Therefore, after node_save is run, $node is populated with all the stuff that was done to it. No need for a return value. This stumpted me at first too, until I did a quick run through with a debugger.

Unless you are updating an existing node...

Update or new, you can detect changes just fine. Save a vid before node_save() or just save a copy and then check for changes on specific properties (or all of them).

We can be sure that the node_save() was successful by checking the 'nid' property of the $node object.

<?php
 
// Assign properties, etc.
  // Save node.
 
node_save($node);
   
 
// node_save() does not return a value. It instead populates the $node object. Thus to check if the save was successful, we check the nid.
 
$node_id = $node->nid;

  return

$node_id;
?>

Ah! great point. This stumped me for a while.

Still...its silly that this (and other basic drupal functions) don't return a proper value. It seems to violate some basic multi-lingual receipt conventions.

I'm not sure you can rely on the existence of $node->nid alone, as it's possible that the process failed on an existing node object (where $node->nid already existed). I think you should instead wrap your node_save() in try/catch:

<?php
try {
 
node_save($node);
 
$success = TRUE;
}
catch (
Exception $e) {
 
// Do your error handling here.
}
?>

E.g.:

<?php
watchdog
('foo', $e, array(), WATCHDOG_ERROR)
?>

E.g.:

<?php
watchdog
('foo', $e, array(), WATCHDOG_ERROR)
?>

Very surprising node_save doesn't return a boolean...

The node object is passed by reference.

After saving, the $node variable will have its $node->nid value set.

Suppose you created a custom node type called 'node_example'. It contains an image field called 'node_example_image' with 'cardinality' set to 5 (can upload 5 images). To create a node of type 'node_example' and upload 5 images (the images are taken from your custom module directory; image1.jpg, image2.jpg, ...), you can do something along the lines of:

<?php
 
// Create a node object, and add node properties.
 
$newNode = (object) NULL;
 
$newNode->type = 'node_example';
 
$newNode->uid = 0;
 
$newNode->created = strtotime("now");
 
$newNode->changed = strtotime("now");
 
$newNode->status = 1;
 
$newNode->comment = 0;
 
$newNode->promote = 0;
 
$newNode->moderate = 0;
 
$newNode->sticky = 0;
 
$newNode->language = 'und';

 

node_object_prepare($newNode);

  for (

$i = 0; $i < 5; $i++) {
   
$j = $i + 1;
   
$file_path = drupal_get_path('module', 'node_example') . '/image' . $j . '.jpg';
   
$file = (object)array(
     
"uid" => 1,
     
"uri" => $file_path,
     
"filemime" => file_get_mimetype($file_path),
     
"status" => 1
   
);

   

$file = file_copy($file, 'public://');
   
$newNode->node_example_image['und'][$i] = (array)$file;
  }

 

// Save the node.
  // The node will also get published, if $newNode->status was set to 1.
 
node_save($newNode);
?>

I need to notify Facebook when a new node is submitted, Facebook checks the response of the url, but doing it in the "insert" hook I always get a 404. I am checking it with

print_r(get_headers('url_to_page/node/' . $node->nid));
exit();

The node gets saved, but at this point, I always get a 404 Response...

It is probably the same problem as http://api.drupal.org/api/drupal/modules--node--node.module/function/nod... above. But node_save is called much earlier cannot be the solution. When I call node_save again there is an error because of a double unique identifier..

I need to save node with status = 0;

add every value i need it and the status set to 0
$node->status = 0;

node_save($node);

the node save as status 1 why how can save the node as unpublished.

Hi Sami,

This is how I do it :

function hook_node_presave($node){
global $user;
if($node->type=='event'){
$node->status=0;
drupal_set_message(t('Thank you for your submission, your event will need to be validated before it appears in our calendar'));

}

}

Checking if an nid exists doesn't really help when updating an existing node… I mean you could check the 'changed' property, but this still seems pretty ugly… A boolean return value determining if the function succeeded would be greatly appreciated.

I have a module which imports nodes - and to get it to work on D6 was very difficult due to the fact that I wanted to create a new node but save it with a certain ID number.

Does anyone know if this version can be used in any way to save a node with a defined ID number? It should probably also create a new revision - but that's not as important.

To answer my own question - it does not look like this can save a node with a pre-determined nid.

To me this is a bit of an oversight in this function - as an API function it should at least give the option to save a node with a specific nid value.

OK - it's not pretty - but a small modification allows for saving a new node with a specific ID. This has been briefly tested and seems to work OK.

(This relies on drupal_write_record() now allowing a new record to be saved with the ID number supplied).

This is the new modified function - it should be relatively easy to patch node_save() to allow for an extra parameter to tell it that the nid needs to be used when creating a new node.

function node_save_with_defined_id($node) {
 
  // Here we will remove the nid from the object to trick the rest of
  // the function into thinking it is dealing with a normal insert.
  // We will then re-introduce the nid when needed.
  $defined_nid = $node->nid;
  unset($node->nid);
   
    $transaction = db_transaction();

  try {
    // Load the stored entity, if any.
    if (!empty($node->nid) && !isset($node->original)) {
      $node->original = entity_load_unchanged('node', $node->nid);
    }

    field_attach_presave('node', $node);
    global $user;

    // Determine if we will be inserting a new node.
    if (!isset($node->is_new)) {
      $node->is_new = empty($node->nid);
    }

    // Set the timestamp fields.
    if (empty($node->created)) {
      $node->created = REQUEST_TIME;
    }
    // The changed timestamp is always updated for bookkeeping purposes,
    // for example: revisions, searching, etc.
    $node->changed = REQUEST_TIME;

    $node->timestamp = REQUEST_TIME;
    $update_node = TRUE;

    // Let modules modify the node before it is saved to the database.
    module_invoke_all('node_presave', $node);
    module_invoke_all('entity_presave', $node, 'node');

    if ($node->is_new || !empty($node->revision)) {
      // When inserting either a new node or a new node revision, $node->log
      // must be set because {node_revision}.log is a text column and therefore
      // cannot have a default value. However, it might not be set at this
      // point (for example, if the user submitting a node form does not have
      // permission to create revisions), so we ensure that it is at least an
      // empty string in that case.
      // @todo: Make the {node_revision}.log column nullable so that we can
      // remove this check.
      if (!isset($node->log)) {
        $node->log = '';
      }
    }
    elseif (!isset($node->log) || $node->log === '') {
      // If we are updating an existing node without adding a new revision, we
      // need to make sure $node->log is unset whenever it is empty. As long as
      // $node->log is unset, drupal_write_record() will not attempt to update
      // the existing database column when re-saving the revision; therefore,
      // this code allows us to avoid clobbering an existing log entry with an
      // empty one.
      unset($node->log);
    }

    // When saving a new node revision, unset any existing $node->vid so as to
    // ensure that a new revision will actually be created, then store the old
    // revision ID in a separate property for use by node hook implementations.
    if (!$node->is_new && !empty($node->revision) && $node->vid) {
      $node->old_vid = $node->vid;
      unset($node->vid);
    }

    // Save the node and node revision.
    if ($node->is_new) {
      // For new nodes, save new records for both the node itself and the node
      // revision.
     
        // Just before we save the record we are going to reset the nid -
        // currently drupal_write_record() looks like it respects an nid
        // which has been sent in to it.
        $node->nid = $defined_nid;
       
        drupal_write_record('node', $node);
      _node_save_revision($node, $user->uid);
      $op = 'insert';
    }
    else {
      // For existing nodes, update the node record which matches the value of
      // $node->nid.
      drupal_write_record('node', $node, 'nid');
      // Then, if a new node revision was requested, save a new record for
      // that; otherwise, update the node revision record which matches the
      // value of $node->vid.
      if (!empty($node->revision)) {
        _node_save_revision($node, $user->uid);
      }
      else {
        _node_save_revision($node, $user->uid, 'vid');
        $update_node = FALSE;
      }
      $op = 'update';
    }
    if ($update_node) {
      db_update('node')
        ->fields(array('vid' => $node->vid))
        ->condition('nid', $node->nid)
        ->execute();
    }

    // Call the node specific callback (if any). This can be
    // node_invoke($node, 'insert') or
    // node_invoke($node, 'update').
    node_invoke($node, $op);

    // Save fields.
    $function = "field_attach_$op";
    $function('node', $node);

    module_invoke_all('node_' . $op, $node);
    module_invoke_all('entity_' . $op, $node, 'node');

    // Update the node access table for this node. There's no need to delete
    // existing records if the node is new.
    $delete = $op == 'update';
    node_access_acquire_grants($node, $delete);

    // Clear internal properties.
    unset($node->is_new);
    unset($node->original);
    // Clear the static loading cache.
    entity_get_controller('node')->resetCache(array($node->nid));

    // Ignore slave server temporarily to give time for the
    // saved node to be propagated to the slave.
    db_ignore_slave();
  }
  catch (Exception $e) {
    $transaction->rollback();
    watchdog_exception('node', $e);
    throw $e;
  }
}

This works perfectly in D7, thanks! I called it using drush scr with the following code (gleaned from http://www.group42.ca/creating_and_updating_nodes_programmatically_in_dr... ):

  $body_text = 'This is the body text I want entered with the node.';
 
  $node = new stdClass();
  $node->type = 'page';
  node_object_prepare($node);
 
  $node->title    = 'Node Created Programmatically on ' . date('c');
  $node->language = LANGUAGE_NONE;

  $node->body[$node->language][0]['value']   = $body_text;
  $node->body[$node->language][0]['summary'] = text_summary($body_text);
  $node->body[$node->language][0]['format']  = 'filtered_html';
 
  //$path = 'content/programmatically_created_node_' . date('YmdHis');
  //$node->path = array('alias' => $path);

  $node->nid = 3;

  node_save_with_defined_id($node);

I would like to add for each "myTypeName" content type, when editing or creating a content, put the statut to 0 and the pathauto checkbox to true all the time even if it was uncheck before. I tried this code but it doesn't work.

function themeName_node_presave($node){
  global $user;
  if($node->type=='myTypeName'){
    $node->status=0;
    $node->path[pathauto] = TRUE;
  }
}

Any ideas why?

I actually figured out why it was not working, I created it with theme butnot with a module !
So now after I created the module it works !

<?php
function my_module_nameo_node_presave($node){
  global
$user;
  if(
$node->type=='myType'){
   
$node->status=0;
   
$node->sticky=1;
   
$node->path['pathauto']=1;
  }
}
?>

@bailey86:
The main reason you cannot specify a nid in the default flavor of Drupal is because the {node}.nid column in the database is auto-incremental.

To build a workaround in your code for this may very well affect Drupal's database-wrapper's ability to properly handle autoincrements at all.

The only reason I can think of that requires a specific NID would be if you want to maintain a specific URL. In this case I would suggest using URL Aliases instead.

Hiya,

I agree about the need to be careful when setting ID values. The reason I needed to do this was as part of my module https://drupal.org/project/data_export_import - for that module I needed to recreate nodes, terms and users with the same ID as when they were exported.

We're at 5,000 downloads and no real bugs to speak of - so it's looking OK - but I agree that this area has to be handled carefully.

So, I have a form and my submit handler creates nodes. After the nodes are created I have a call to drupal_mail in that handler. Now, the interesting part, I also have a node_insert hook that redirects users when that node type is created. I now realize this is a bad place for my redirect, and I can just put it in my submit handler, but I never though that a drupal_goto would prevent the remainder of my submit hook to complete. In fact, I still don't really believe it, maybe something else I am doing?

pseudocode follows:

function myform_submit() {
....some node preparation.....
node_save($node);
drupal_mail(<params>);
}

function myform_node_insert() {
drupal_goto('somewhere');
}

The drupal_mail will never fire. I guess after having written it out, it makes some sense, if you set your redirect, it can be assumed you want to abandon the script(function).

Also, remember to do node_save(&$node) to get your nid everyone(when drupal starts using php 5.4 this will change in node_save(&$node)!

Objects are passed by reference in PHP 5