Bagaimana cara menghapus koleksi bidang dengan benar?


9

Versi Drupal: 7.21
Versi modul koleksi lapangan: 7.x-1.0-beta5

Penjelasan singkat : Saya sibuk mencoba mengimpor koleksi lapangan secara terprogram tetapi ketika menghapus beberapa dari mereka selalu ada beberapa koleksi bidang 'palsu' yang tersisa.

Penjelasan panjang : Pengguna saya memiliki bidang koleksi bidang di profil mereka. Koleksi bidang ini berisi 3 bidang teks. Saya ingin mengimpor data dari database sql khusus ke dalam kumpulan bidang pengguna. Koleksi bidang ini dapat memiliki beberapa nilai. Saat saya mengimpor data untuk pertama kalinya semuanya berfungsi dengan baik, saya melihat data di bidang kumpulan bidang. Bagus.

Tapi inilah bagian yang sulit. Katakanlah saya mengimpor untuk satu pengguna tertentu 5 baris dari database khusus. Mereka ditambahkan ke koleksi bidang, jadi koleksi bidang ini memiliki 5 item masing-masing berisi 3 bidang. Lalu saya menghapus beberapa baris dari basis data khusus saya sehingga saya hanya memiliki 3 baris tersisa untuk pengguna ini. Saya menjalankan impor lagi, memperbarui 3 item pertama dari koleksi bidang, tapi kemudian saya pergi dengan 2 item dari impor sebelumnya. Mereka harus dihapus karena saya hanya memiliki 3 baris yang diimpor tetapi masih 5 item koleksi lapangan.

Jadi saya mencoba menghapus item kumpulan bidang ini, tetapi selalu ada satu atau lebih item yang tersisa. Kolom kosong ketika saya melihat profil pengguna tetapi masih ada sesuatu di sana. Katakanlah pada titik ini saya menambahkan 5 baris baru untuk pengguna di basis data khusus saya, jadi saya memiliki total 8 baris untuk pengguna ini. Lalu saya menjalankan impor lagi. 3 item pertama diperbarui, tetapi kemudian ketika saya mencoba menambahkan baris ke-4 ia masih mendapatkan id entitas dari item koleksi bidang ke-4, mencoba untuk memperbaruinya tetapi gagal dan mengembalikan kesalahan ini:

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

Saya mencoba menghapus item kumpulan bidang dengan masing-masing metode berikut:

// 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();

Ini kode lengkap saya:

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();
      }
    }
  }
}

Jadi pertanyaan saya adalah: Bagaimana cara saya menghapus item koleksi kolom sehingga mereka benar-benar hilang?


2
entity_delete_multipleadalah 100% jelas cara yang tepat untuk melakukannya - lihat field_collection_field_deletefungsinya, yang digunakan oleh Field Collection untuk membersihkan item ketika bidang yang direferensikan dihapus
Clive

Terima kasih banyak atas tanggapan Anda, saya menghargainya. Apakah Anda tahu argumen apa yang harus saya berikan dengan field_collection_field_delete? Saya melihat tanda tangannya adalah field_collection_field_delete ($ entity_type, $ entitas, $ field, $ instance, $ langcode, & $ items) tetapi saya tidak benar-benar tahu nilai apa yang dimasukkan: $ entitas (apakah ini pengguna atau kumpulan bidang ?), $ field (mengembalikan nilai dari field_collection_item_load?), $ instance, $ langcode (und?) dan $ items.
Smos

1
Fungsi khusus itu adalah implementasi pengait, pada dasarnya ketika bidang apa pun dihapus nama bidang diteruskan ke fungsi itu, dan Pengumpulan Bidang memeriksa apakah ada entitas FC yang terkait dengan instance bidang itu. Jika ada, itu menghapusnya menggunakan entity_delete_multiple(). Anda mungkin perlu menjalankan cron beberapa kali setelah Anda menghapus bidang (data bidang dibersihkan pada jadwal agar tidak membebani satu halaman memuat dengan semua pemrosesan yang harus dilakukan)
Clive

Saya mencoba menggunakan entitas_delete_multiple lagi dan saya perhatikan bahwa item bisa dihapus di tabel field_collection_item tetapi bidang masih ada di tabel field_data_field_collection_name. Saya pikir ini menyebabkan kesalahan fatal Panggilan ke metode yang tidak ditentukan stdClass :: save () karena mereka seharusnya adalah bidang tetapi mereka tidak memiliki item koleksi bidang yang ditautkan dengannya. Ketika saya menggunakan $ field_collection_item-> deleteRevision menghapus data di kedua tabel tetapi ketika saya menyimpan pengguna baris ditambahkan ke tabel field_data_field_collection_name dan itu adalah item koleksi bidang kosong.
Smos

@Smos: hai sobat, dapatkah Anda membantu saya dengan masalah serupa ( drupal.stackexchange.com/questions/239784/… )? Saya mencoba bit kode Anda yang relevan tetapi saya tidak bisa membuatnya berfungsi.
sisko

Jawaban:


13

Saya mengalami kasus penggunaan serupa di mana saya ingin memetakan beberapa data ke dalam kumpulan bidang selama hook_feeds_presave () karena struktur sumber terlalu kompleks untuk Feed. Saya menemukan entitas_delete_multiple () menghapus item koleksi lapangan, tetapi ketika saya mengedit node, masih ada banyak kumpulan field kosong di sana. Mengecek dan menghapus berhasil, yang saya temukan di sini: https://drupal.stackexchange.com/a/31820/2762

Jika sumber umpan telah berubah, saya menghapus semua item koleksi bidang dan membuat ulang. Semoga ini bermanfaat.

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);

Pendekatan 2 langkah ini adalah satu-satunya cara yang bekerja AFAIK. Jangan lupa ke node_save($node)simpul Anda.
Bernhard Fürst

@ BernhardFürst sebenarnya kita tidak perlu node_save($node), DrupalEntityControllerakan melakukan pekerjaan ini
coffeduong

8

Cara terbaik untuk melakukan ini sekarang adalah panggilan $field_collection->delete()dan itu akan menangani semuanya.

 <?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

Jawaban di atas bukanlah cara terbaik, dengan menghapus semua item lainnya menghilang dari koleksi bidang, dan cara lain dengan ->delete()melempar bug dengan modul Entity.

Jalan yang benar. Yang saya lakukan adalah ini:

Dalam kasus saya, saya ingin menghapus item terakhir dalam koleksi bidang

lihat kode ini, (untuk pemula: "ingat bahwa Anda harus sudah memiliki $ entitas")

// 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);

itu saja! cara lainnya adalah

//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));

Cara di atas baik menggunakan entity_metadata_wrapperfungsi tetapi dengan cara itu ada bug kompleks yang saya tidak tahu bagaimana menyelesaikannya, Anda dapat memeriksanya di https://drupal.org/node/1880312 dan setelah menerapkan tambalan di # 9 Anda mendapatkan masalah berikutnya, periksa di sini https://drupal.org/node/2186689 bug ini juga jika Anda menggunakan ->delete()fungsi.

Semoga ini bisa membantu seseorang.


0

menggunakan vbo untuk menghapus item koleksi bidang. itu akan otomatis menghapus relasi bidang dengan entitas host item koleksi bidang.


0

Saya memiliki masalah serupa, di mana saya mengimpor data dari umpan ke item FC. Ketika pembaruan ke entitas host dibuat dari umpan, dan saya mengimpor perubahan itu, saya ingin memastikan bahwa setiap item FC yang ada yang tidak lagi ada dari sumber umpan dihapus.

Solusi saya:

// 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();

Dan itu saja. Hook_field_update Field Collection ( field_collection_field_update) akan benar-benar menghapus semua item FC yang ada yang telah di-referensikan.

Satu-satunya downside ke ini adalah jika tidak ada perubahan dalam data FC, itu tetap dihapus dan dibuat kembali. Tapi itu bukan masalah besar bagi saya.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.