フィールドコレクションを適切に削除する方法


9

Drupalバージョン:7.21
フィールドコレクションモジュールバージョン:7.x-1.0-beta5

簡単な説明:フィールドコレクションをプログラムでインポートするのに忙しいのですが、それらの一部を削除すると、常にいくつかの「偽の」フィールドコレクションが残っています。

長い説明:ユーザーのプロファイルにフィールドコレクションフィールドがあります。このフィールドコレクションには、3つのテキストフィールドが含まれています。カスタムSQLデータベースからユーザーのフィールドコレクションにデータをインポートしたい。このフィールドコレクションは複数の値を持つことができます。初めてデータをインポートすると、すべてが正常に機能し、フィールドコレクションのフィールドにデータが表示されます。すごい。

しかし、ここでトリッキーな部分があります。たとえば、特定のユーザーに対して、カスタムデータベースから5行をインポートするとします。それらはフィールドコレクションに追加されるため、このフィールドコレクションにはそれぞれ3つのフィールドを含む5つのアイテムがあります。次に、カスタムデータベースからいくつかの行を削除して、このユーザーの残りの行が3行だけになるようにします。インポートを再度実行して、フィールドコレクションの最初の3つのアイテムを更新しますが、前回のインポートの2つのアイテムが残ります。インポートされた行は3つだけですが、フィールドコレクションアイテムが5つあるため、削除する必要があります。

そのため、これらのフィールドコレクションアイテムを削除しようとしましたが、常に1つ以上のアイテムが残っています。ユーザープロファイルを見るとフィールドは空ですが、まだ何かがあります。この時点で、カスタムデータベースにユーザーの新しい行を5つ追加したとしましょう。このユーザーの行は合計で8行になります。次に、インポートを再度実行します。最初の3つのアイテムが更新されますが、4番目の行を追加しようとすると、4番目のフィールドコレクションアイテムからエンティティIDが取得され、更新が試行されますが失敗し、次のエラーが返されます。

 Fatal error: Call to undefined method stdClass::save()

以下の各方法でフィールドコレクションアイテムを削除してみました。

// Method 1
entity_delete_multiple('field_collection_item', array($fc_id));

// Method 2
$field_collection_item = field_collection_item_load($fc_id);
$field_collection_item->delete();

// Method 3
$field_collection_item = field_collection_item_load($fc_id);
$field_collection_item->deleteRevision();

これは私の完全なコードです:

function import_user_field_collection(&$user, $old_user_id) {

  // I do a query to get the rows I want to import for this specific user.
  db_set_active('custom_sql_database');
  $result = db_query("SELECT * FROM {users} WHERE user_id = :user_id", array(':user_id' => $old_user_id));

  db_set_active('default');
  $i = 0; // Keep count of how many rows I imported.
  foreach($result as $row) {
    // Check if the field collection item already exists.
    if(!empty($user->field_profile_diploma_opleiding[LANGUAGE_NONE][$i]['value'])) {
      // If it does exists, update this particular field collection item.
      $fc_id = $user->field_profile_diploma_opleiding[LANGUAGE_NONE][$i]['value'];
      $field_collection_item = entity_load('field_collection_item', array($fc_id));
      // These 3 text fields are children of the field collection field.
      $field_collection_item[$fc_id]->field_profile_diploma_instituut[LANGUAGE_NONE][0]['value'] = $row->instituut;
      $field_collection_item[$fc_id]->field_profile_diploma_vakgebied[LANGUAGE_NONE][0]['value'] = $row->vakgebied;
      $field_collection_item[$fc_id]->field_profile_diploma_jaar[LANGUAGE_NONE][0]['value'] = $row->jaar_diploma;
      $field_collection_item[$fc_id]->save(TRUE);
    } else {
      // If the field collection item doesn't exist I want to create a new field collection item.
      $field_collection_item = entity_create('field_collection_item', array('field_name' => 'field_profile_diploma_opleiding'));
      $field_collection_item->setHostEntity('user', $user);
      $field_collection_item->field_profile_diploma_instituut[LANGUAGE_NONE][0]['value'] = $row->instituut;
      $field_collection_item->field_profile_diploma_vakgebied[LANGUAGE_NONE][0]['value'] = $row->vakgebied;
      $field_collection_item->field_profile_diploma_jaar[LANGUAGE_NONE][0]['value'] = $row->jaar_diploma;
      $field_collection_item->save(TRUE);
    }
    $i++;
  }

  $fc_fields = field_get_items('user', $user, 'field_profile_diploma_opleiding');

  // Check if there are more field collection items than imported rows
  if(count($fc_fields) > $i) {
    for($i; $i <= count($fc_fields); $i++) {
      // Run through each field collection item that's left from the previous import and delete it.
      if(!empty($user->field_profile_diploma_opleiding[LANGUAGE_NONE][$i]['value'])) {
        // Method 1
        $fc_id = $user->field_profile_diploma_opleiding[LANGUAGE_NONE][$i]['value'];
        entity_delete_multiple('field_collection_item', array($fc_id));

        // Method 2
        //$field_collection_item = field_collection_item_load($fc_id);
        //$field_collection_item->delete();

        // Method 3
        //$field_collection_item = field_collection_item_load($fc_id);
        //$field_collection_item->deleteRevision();
      }
    }
  }
}

だから私の質問は、フィールドコレクションアイテムを削除して、実際に削除する方法を教えてください。


2
entity_delete_multiple100%間違いなく正しい方法です。field_collection_field_delete参照されたフィールドが削除されたときにフィールドコレクション自体がアイテムをクリーンアップするために使用する関数を確認してください
Clive

お返事ありがとうございます。私は偶然、field_collection_field_deleteでどの引数を指定すべきか知っていますか?署名がfield_collection_field_delete($ entity_type、$ entity、$ field、$ instance、$ langcode、&$ items)であることがわかりますが、どの値を入力するか本当にわかりません:$ entity(これはユーザーまたはフィールドコレクションですか) ?)、$ field(field_collection_item_load?からの戻り値)、$ instance、$ langcode(und?)、および$ items。
Smos 2013

1
その特定の機能が場合、基本的に、フックインプリメンテーションである任意のそのフィールドのインスタンスに関連付けられたFCエンティティがある場合、フィールドが削除されたフィールド名は、その関数に渡され、フィールドコレクションをチェックします。あれば削除しentity_delete_multiple()ます。フィールドを削除した後、数回cronを実行する必要がある場合があります(フィールドデータは、単一のページのロードで実行するすべての処理に負担をかけないように、スケジュールに基づいてパージされます)
Clive

entity_delete_multipleをもう一度使用してみたところ、フィールドfield_collection_itemの項目が削除されましたが、フィールドfield_data_field_collection_nameにはフィールドが残っています。これらはフィールドであるはずですが、フィールドコレクションアイテムがリンクされていないため、これにより致命的なエラーCall to undefined method stdClass :: save()が発生すると思います。$ field_collection_item-> deleteRevisionを使用すると、両方のテーブルのデータが削除されますが、ユーザーを保存すると、フィールドfield_data_field_collection_nameに行が追加され、それが空白のフィールドコレクションアイテムになります。
Smos 2013

@Smos:こんにちは、同様の問題(drupal.stackexchange.com/questions/239784/…)で私を助けてくれますか?私はあなたのコードの関連部分を試しましたが、それを機能させることができませんでした。
sisko 2017

回答:


13

ソース構造がフィードに対して複雑すぎたため、hook_feeds_presave()の実行中にいくつかのデータをフィールドコレクションにマップする必要がある、同様のユースケースに遭遇しました。entity_delete_multiple()がフィールドコレクションアイテムを削除したことがわかりましたが、ノードを編集したところ、まだ空のフィールドコレクションがたくさんありました。設定を解除して削除すると、私がここで見つけたトリックを実行しました:https ://drupal.stackexchange.com/a/31820/2762

フィードソースが変更された場合は、すべてのフィールドコレクションアイテムを削除して再作成します。これがお役に立てば幸いです。

foreach ($node->field_international_activity[LANGUAGE_NONE] as $key => $value) {
  // Build array of field collection values.
  $field_collection_item_values[] = $value['value'];

  // Unset them.  
  unset($node->field_international_activity[LANGUAGE_NONE][$key]);
}

// Delete field collection items.
entity_delete_multiple('field_collection_item', $field_collection_item_values);

この2ステップのアプローチは、AFAIKを機能させる唯一の方法です。node_save($node)ノードを忘れないでください。
BernhardFürst2013年

BernhardFürst@実際に我々は必要としないnode_save($node)DrupalEntityControllerこの仕事をする
coffeduong

8

これを行うための最善の方法は、呼び出しで$field_collection->delete()あり、それはすべてを処理します。

 <?php
    /**
     * Deletes the field collection item and the reference in the host entity.
     */
    public function delete() {
      parent::delete();
      $this->deleteHostEntityReference();
    }

    /**
     * Deletes the host entity's reference of the field collection item.
     */
    protected function deleteHostEntityReference() {
      $delta = $this->delta();
      if ($this->item_id && isset($delta)) {
        unset($this->hostEntity->{$this->field_name}[$this->langcode][$delta]);
        entity_save($this->hostEntityType, $this->hostEntity);
      }
    }
 ?>

0

上記の答えは最良の方法ではありません。フィールドコレクションから他のすべての項目を設定解除する->delete()と、エンティティモジュールのバグがスローされます。

正しい方法。まあ私がしたことはこれでした:

私の場合、フィールドコレクション内の最後のアイテムを削除したかった

このコードを見てください(初心者向け:「$ entityが既に読み込まれている必要があります」)

// Remove the field value
unset($entity->field_salida_mercanc_a[LANGUAGE_NONE][count($entity->field_salida_mercanc_a[LANGUAGE_NONE])-1]);

// Reset the array to zero-based sequential keys
$entity->field_salida_mercanc_a[LANGUAGE_NONE] = array_values($entity->field_salida_mercanc_a[LANGUAGE_NONE]);

// Save the entity
entity_save($entity_type, $entity);

それで全部です!逆の方法は

//Get the node wapper
$wrapper = entity_metadata_wrapper($entity_type, $entity);
//Get the last position of the array items, thanks to the ->value() statement you can get the complete Array properties.
$field_collection_item_value = $wrapper->field_collection_mine[count($wrapper->field_collection_mine->value())-1]->value();
//Do not use 'unset' to delete the item due to is not the correct way, I use the entity_delete_multiple provided by the entity API
entity_delete_multiple('field_collection_item', array($field_collection_item_value->item_id));

上記の方法はentity_metadata_wrapper関数を使用して良いですが、その方法では私がそれを解決する方法がわからない複雑なバグがあります。https://drupal.org/node/1880312でそれをチェックし、#9でパッチを適用した後次の問題が発生した場合は、こちら(https://drupal.org/node/2186689)で確認してください。このバグは、->delete()関数を使用している場合にも発生します。

それが誰かを助けることを願っています。


0

vboを使用してフィールドコレクションアイテムを削除します。フィールドコレクションアイテムのホストエンティティとのフィールドリレーションが自動的に削除されます。


0

同様の問題があり、フィードからFCアイテムにデータをインポートしています。フィードからホストエンティティの更新が行われ、それらの変更をインポートする場合、フィードソースから存在しない既存のFCアイテムが確実に削除されるようにしたいと考えました。

私の解決策:

// Load host entity that I'm updating.
$host_entity = node_load($nid);
$host_entity_wrapper = entity_metadata_wrapper('node', $host_entity);

// Clear out the references to the existing FC items so that I have a fresh start.
$host_entity_wrapper->field_my_fc_items->set(NULL);

// Re-create the FC items from the source feed data.
foreach ($feed_data->items as $feed_item) {
  $fc = entity_create('field_collection_item', array('field_name' => 'field_my_fc_items'));
  $fc->setHostEntity($host_entity);
  // Some some fields on the FC item from the feed data.
  $fc_wrapper = entity_metadata_wrapper('field_collection_item', $fc);
  $fc_wrapper->field_some_fc_item_field->set($feed_item->data);
}

// Sync other field info onto host entity.
...

$host_entity_wrapper->save();

以上です。フィールドコレクションのhook_field_update(field_collection_field_update)は、逆参照された既存のFCアイテムを実際に削除します。

これの唯一の欠点は、FCデータに変更がなかった場合に、FCデータが削除されて再作成されることです。しかし、それは私にとって大したことではありません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.