ノードの特定のフィールドだけを更新するには

プログラムからノードの特定のフィールドだけを修正/更新したいとき、このようなコードを考えるかもしれません。

$node = node_load($nid);
$node->field_fieldname[LANGUAGE_NONE][0]['value'] = '更新したフィールドの値';
node_save($node);

node_load() でいったん変更したいノードの完全なデータを読み込み、修正/変更したいフィールドを操作して、node_save() で保存する。

「しかしこれでは無駄が多い」と感じてしまいますね。まず変更する必要のないフィールドもすべて読み込まなければなりません。そして変更していないフィールドや、ノード自体もすべて保存し直すことになります。フィールドの数が3つや4つならまだしも、10 や 20、50 ~ものフィールドがあったらどうでしょうか?それで1つか2つのノードを変更したいならまだしも、数千ものノードを連続で修正したい場合は?

これでは処理に時間がかかってしまいます。また、ノードの読み込み、更新に関連するフックも、ここでは関係の無いものまですべてコールされます。

SQLクエリでデータベーステーブルを直接操作する?しかしそれだと必要なフックのコールや、フィールドのキャッシュの消去は自分でやらなければならないし、クエリを書くのもちょっと面倒です。コードも冗長になってしまうでしょう。

このような場合は field_attach_update() が使えます。

$node = node_load($nid);
$node->field_fieldname[LANGUAGE_NONE][0]['value'] = '更新したフィールドの値';
field_attach_update('node', $node);

このようにすると、ノードテーブルには触れずに、フィールドデータのみが更新されます。対象のノードのフィールドキャッシュのクリアも行われますし、必要なフックもコールされます。

ただこれでも、node_load() でノードを一旦まるごと読み込んでから、変更のないフィールドのデータも保存し直すことになるので、やはり無駄が多いですね。フィールドの数が大量にある場合は大変です。

このような場合は以下のようにすると良いです。

$node = new stdClass();
$node->nid = $nid;
$node->vid = $vid;
$node->type = $type;
  
$node->field_fieldname[LANGUAGE_NONE][0]['value'] = '更新したフィールドの値';
field_attach_update('node', $node);

field_attach_update() ではこれに渡す $node は完全なノードオブジェクトである必要はなく、必要なのは対象のノードの nid とvid、それとバンドルだけです。なので新たにオブジェクトを生成してこの3つを指定してやれば良いわけです。

この方法ならデータの更新が行われるのは操作対象のフィールドのみで、他の関係ないフィールドを保存しなおすこともありません。

もちろん field_attach_update() はノードだけではなく、フィールドを持たせることのできる他のエンティティ(ユーザーやタクソノミーターム、コメントなど)でも同様に使えます。

コア: 
Drupal7