プログラムからノードの特定のフィールドだけを修正/更新したいとき、このようなコードを考えるかもしれません。
$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() はノードだけではなく、フィールドを持たせることのできる他のエンティティ(ユーザーやタクソノミーターム、コメントなど)でも同様に使えます。