Saving node's fields without saving the node itself
Drupal core function node_save() is used to save nodes and it is frequently used to programmatically alter and save nodes' data, which includes fields (aka CCK fields) as well. If you want to update a field of a node you could do it like this,
<?php
$node = node_load($nid);
$node->field_fieldname[LANGUAGE_NONE][0]['value'] = 'some value';
node_save($node);
?>However, there may be a case when you might want to save a node's field, but not the node itself. For example, you may not want to update the node's timestamp for the last update, because you want it to be changed by human user activity only. In another case, you may not want to call node_save() and thus invoke chain reactions such as email notifications when the node is updated. These two cases are what actually happened to me.
A Drupal function field_attach_update() may save your day if you encountered a similar problem that I had. You could actually replace node_save() with field_attach_update(),
<?php
$node = node_load($nid);
$node->field_fieldname[LANGUAGE_NONE][0]['value'] = 'some value';
field_attach_update('node', $node);
?>The first argument takes 'entity type', which means it can be used for user, comment, etc, as well as node.
If you want to update a single field only and save some time by skipping other fields (because there are a lot and/or the routine is in a loop), you could remove unwanted fields first; the second argument ($node in the example) does not need to be complete with all possible fields.
<?php
unset($node->field_unwanted_1);
unset($node->field_unwanted_2);
unset($node->field_unwanted_3);
. . .
field_attach_update('node', $node);
?>If you wanted the routine to be even lighter, you might want to build the node object yourself. A quick look at the code of field_attach_update() suggests that only 2 properties are what you need (I have not tested this though).
<?php
$node = new stdClass();
$node->id = $id_value; // node id
$node->bundle = $bundle_value; // aka content type
$node->field_fieldname[LANGUAGE_NONE][0]['value'] = 'some value';
field_attach_update('node', $node);
?>Edit:
It may be a good idea to call field_attach_presave() before field_attach_update(); it happens to be called by node_save() too.
c.f.) http://api.drupal.org/api/drupal/modules--node--node.module/function/nod...
<?php
field_attach_presave('node', $node);
field_attach_update('node', $node);
?>Edit (12/27/2011):
This tip I presented may not be generalized to various cases, especially where chain reaction of Drupal hooks is expected to further modify values. My motivation is to simply fill/alter some fields of a node without calling node_save().
10 comments
inline editing
wouldn't this be the way to provide inline editing of nodes?
neat. thanks for sharing.
neat. thanks for sharing.
This *would* be useful for
This *would* be useful for inline editing. Is it possible to extend this to provide timestamp altering and revision bumping for the node? I'm currently working on an inline editor for a client with over 200 fields attached to a node so the speed of a node save is an issue. If I could just save one field that would be a huge benefit.
Doesn't this hose revisions?
Doesn't this hose revisions? A node revision should be a snapshot of a node at a given time, including both the basic properties of the node and all attached field data. By bypassing the node_save(), you must be certain that it's safe to make the change without triggering a new revision. May be perfectly legitimate in various scenarios, but it's something to keep in mind.
Accidental security risk
The problem with this approach is that if you are using node or entity access, saving at the field level may accidentally bypass the proper calculation of access rules. Any code that implements this behavior would have to recalculate the access records of the parent entity.
equivalent in drupal6
Great! but is it possible to do the same in drupal6?
Thanks!
What about drupal6
As was asked before, is it possible to have similar behaviour in drupal 6?
field_attach_update was introduced in 7.
The closest candidate seems
The closest candidate seems to be content_field_instance_update().
node_save
And what about hooks working on node_save which may depend on field values? And what about cache_clear_all we need to show fresh content?
Plus this code is not correct: $node->field_fieldname[LANGUAGE_NONE][0]['value'] = 'some value';
such direct calls are not correct for work.
It doesn't work with multilingual
I have tried with the following code to save the data with the correct language. However, only the data with lang='und' where inserted into the database. I wonder what's wrong with
<?php
$newnode->field_new_caption[$lang][$x]= array('value' => $new_caption_value,'format' => $new_caption_format);
$result['node'])){if(!empty(
$nodes=node_load_multiple(array_keys($result['node']));
//print_r(array_keys($result['node']));
foreach($nodes as $mynode) {
if(!empty($mynode->field_caption)){
$n=$n+1;
$captions=field_get_items('node',$mynode,'field_caption');
$x=0;
$lang=$mynode->language;
$newnode = new stdClass();
foreach($captions as $caption){
$caption_value=$caption['value'];
//print $node->nid."-".$caption_value."<br>";
$new_caption_format='safe_html';
$new_caption_value=$caption_value;
$newnode->field_new_caption[$lang][$x]= array('value' => $new_caption_value,'format' => $new_caption_format);
$x++;
}
$newnode->nid=$mynode->nid;
$newnode->type=$mynode->type;
$newnode->language=$mynode->language;
field_attach_update('node', $newnode);
}
}
print $n." were processed";
}
?>
Thanks!
Post new comment