回答:
絶対に行きたいですfield_attach_update
。
アイデアはシンプルです。ノードをロードして、field_attach_updateを使用して保存するだけです。
例:
$node = node_load($nid);
$node->field_name[LANGUAGE_NONE][0]['value'] = 'New value';
field_attach_presave('node', $node);
field_attach_update('node', $node);
// Clear the static loading cache.
entity_get_controller('node')->resetCache(array($node->nid));
これにより、node_saveが通常呼び出すタイムスタンプやその他のフックは変更されません。ノードをロードするといくつかのフックも呼び出されるため、おそらく効率的ではありません。
nidがあり、ノード構造が非常に単純な場合、次のようにすることもできます。
$node = new stdClass();
$node->nid = $nid; // Enter the nid taken. Make sure it exists.
$node->type = 'article';
$node->field_name[LANGUAGE_NONE][0]['value'] = 'New value';
field_attach_presave('node', $node);
field_attach_update('node', $node);
// Clear the static loading cache.
entity_get_controller('node')->resetCache(array($node->nid));
とにかく、フィールド以外を更新しようとしても、これは機能しません(コメントステータス、公開ステータスなど)。また、node_saveを使用している場合、特定のノードのキャッシュは、「entity_get_controller」でクリアする必要があるさまざまなメソッドに対して自動的にクリアされます。
更新:field_attach_presave()
他のモジュールにフィールド入力を適切に処理させるために
呼び出す必要があるようです。たとえば、ファイルモジュールは、このフックを使用してファイルステータスを永続的に設定するために使用します。上記の2つの例を更新しました。
node_save
ためfield_attach_update
、「から」EntityFieldQuery
への切り替えdb_query_range
は非常にやりがいがありました。3時間の更新から40分まで。
標準のイベントとアクションを発生させずにフィールドデータを保存したくない場合は、drupal_write_recordを使用できます。
IDが1のarticleタイプのノードのbodyフィールドにHello Worldを挿入する例を次に示します。
$values = array(
'entity_type' => 'node',
'bundle' => 'article',
'entity_id' => 1,
'revision_id' => 1,
'language' => 'und',
'delta' => 0,
'body_value' => 'HELLO WORLD',
'body_summary' => '',
'body_format' => 'filtered_html',
);
drupal_write_record('field_data_body', $values);
drupal_write_record('field_revision_body', $values);
サイトが多言語の場合は、「und」ではなく「en」またはコンテンツの言語を使用します。
リビジョンを作成している場合は、正しいリビジョンIDを挿入するように注意する必要があります。そうでない場合は、単にentity_idと同じ値を挿入できます。
このデータが2つのテーブルfield_data_ *およびfield_revision_ *に挿入される方法に注意してください。両方に挿入して、サイトが希望どおりに機能することを確認してください。
これを実行した後、キャッシュの設定方法に応じて表示されるフィールドのキャッシュをクリアする必要があります。
多数のノードを更新する必要があるこのような単純な更新では、常にMySQL更新ステートメントを使用します。はい、キャッシュを考慮する必要がありますが、完了後にキャッシュをフラッシュするだけで十分です。もちろん、データ構造に精通する必要がありますが、Drupal 6では比較的単純です(Drupal 7では恐ろしいことですが)
field_attach_update
sqlはノードキャッシュオブジェクトを更新せず、次にnode_load
更新されたフィールド値をロードしないので、古い値をロードするため、SQLクエリではなく、私もお勧めします。
field_attach_update
直接SQLクエリよりもはるかに優れています。
stdClass
ロードせずにオブジェクトとしてノードを作成することを提案しました。すべてのフィールドを設定せずにこの方法でノードを更新しようとするとどうなるかご存知ですか?これらはヌルまたはデフォルトで上書きされますか?プロセスを更新することで、これらが無視される可能性がありますか?
他の回答に記載されているすべてのアプローチを試した後、この記事を見つけるまで、更新時間が非常に遅くなりました(20+フィールドを持つノードタイプの700.000ノードで約7日)。ブログ/のみ更新-変更-フィールド・オア・プロパティ・エンティティ- drupalの。
hook_updateに以下のコードのようなものを実装した後、更新時間を2時間に短縮しました。これは管理しやすいと思います。
if (!isset($sandbox['storage']['nids'])) {
$sandbox['storage']['nids'] = [];
$query = 'SELECT {nid} FROM node WHERE type = \'article\';';
$result = db_query($query)->fetchCol();
if ($result) {
$sandbox['storage']['nids'] = $result;
$sandbox['storage']['total'] = count($sandbox['storage']['nids']);
$sandbox['storage']['last_run_time'] = time();
$sandbox['progress'] = 0;
}
}
$amount = 300;
$nids = array_slice($sandbox['storage']['nids'], 0, $amount);
if (!empty($nids)) {
$nodes = node_load_multiple($nids, [], TRUE);
foreach ($nodes as $node) {
// Lets manipualte the entity.
$article_wrapper = UtilsEntity::entity_metadata_wrapper('node', $node);
// Eventual logic here.
// Field to update
$article_wrapper->my_field = 'my_value';
$article_wrapper->save();
$sandbox['progress']++;
}
$sandbox['message'] = 'Runs left: ' . (($sandbox['storage']['total'] - $sandbox['progress'])/$amount) . ' Progress: ' . (($sandbox['progress'] * $amount)/$sandbox['storage']['total']) . '%';
$sandbox['storage']['last_run_time'] = time();
unset($nids);
}
$sandbox['storage']['nids'] = array_slice($sandbox['storage']['nids'], 100, count($sandbox['storage']['nids']));
if (!empty($sandbox['storage']['total'])) {
$sandbox['#finished'] = ($sandbox['storage']['total'] - count($sandbox['storage']['nids'])) / $sandbox['storage']['total'];
}
return $sandbox['message'];
特定のコンテンツタイプのすべてのノードのフィールドを更新するという同じ要件さえありました。node_load_multipleとfield_attach_updateを使用しました。
$nodes = node_load_multiple(array(), array('type' => 'content_type_name'));
foreach ($nodes as $node) {
$node->field_name['und'][0]['value'] = 'field value';
field_attach_update('node', $node);
}
私はそれを駆け抜けて走りましたが、それはかなり迅速でした。
mySQLを使用してこれらの更新をデータベースに直接実行することを検討しましたか?それはおそらくあなたが望むものを達成するための最も簡単で最速の方法です。
以下に簡単な例を示します。このようなコマンドは、phpMyAdminの「SQL」タブから実行できます。メンバープロファイルと呼ばれるコンテンツタイプがあるとします。その中に「メンバーのタイプ」という名前のフィールドがあります(会社、個人、組織など)。「COMPANY」のすべての出現を「company」に更新するとします。次のコマンドはそれを行います。
UPDATE content_type_member_profile SET field_type_of_member_value
= 'company' WHERE field_type_of_member_value
= 'COMPANY';
field_attach_update
ですか?