วิธีการลบการรวบรวมฟิลด์อย่างถูกต้อง?


9

รุ่น Drupal: 7.21
โมดูลโมดูลการรวบรวมฟิลด์: 7.x-1.0-beta5

คำอธิบายสั้น ๆ : ฉันไม่ว่างที่จะพยายามนำเข้าคอลเลกชันฟิลด์โดยทางโปรแกรม แต่เมื่อลบบางรายการจะมีการรวบรวมฟิลด์ 'ปลอม' อยู่เสมอ

คำอธิบายแบบยาว : ผู้ใช้ของฉันมีฟิลด์การรวบรวมฟิลด์ในโปรไฟล์ของพวกเขา การรวบรวมฟิลด์นี้มี 3 ฟิลด์ข้อความ ฉันต้องการนำเข้าข้อมูลจากฐานข้อมูล sql แบบกำหนดเองไปยังการรวบรวมฟิลด์ของผู้ใช้ การรวบรวมฟิลด์นี้สามารถมีได้หลายค่า เมื่อฉันนำเข้าข้อมูลเป็นครั้งแรกที่ทุกอย่างทำงานได้ดีฉันเห็นข้อมูลในฟิลด์ของการรวบรวมฟิลด์ ยิ่งใหญ่

แต่ที่นี่ส่วนที่ยุ่งยากมา สมมติว่าฉันนำเข้าสำหรับผู้ใช้ที่ระบุ 5 แถวจากฐานข้อมูลที่กำหนดเอง พวกมันจะถูกเพิ่มลงในคอลเลกชันฟิลด์ดังนั้นคอลเลกชันฟิลด์นี้มี 5 รายการแต่ละรายการมี 3 ฟิลด์ จากนั้นฉันลบบางแถวออกจากฐานข้อมูลที่กำหนดเองของฉันเพื่อที่ฉันจะได้เหลือเพียง 3 แถวสำหรับผู้ใช้รายนี้ ฉันเรียกใช้การนำเข้าอีกครั้งอัปเดต 3 รายการแรกของการรวบรวมฟิลด์ แต่จากนั้นฉันเหลือ 2 รายการจากการนำเข้าก่อนหน้า ควรลบเพราะฉันมีแถวที่นำเข้าเพียง 3 แถว แต่ยังมีรายการคอลเลกชันฟิลด์ 5 รายการ

ดังนั้นฉันจึงพยายามที่จะลบรายการคอลเลกชันเขตข้อมูลเหล่านี้ แต่มีอย่างน้อยหนึ่งรายการที่เหลือ เขตข้อมูลว่างเปล่าเมื่อฉันดูโปรไฟล์ผู้ใช้ แต่ยังมีบางสิ่งอยู่ สมมติว่า ณ จุดนี้ฉันเพิ่ม 5 แถวใหม่สำหรับผู้ใช้ในฐานข้อมูลที่กำหนดเองดังนั้นฉันจึงมีทั้งหมด 8 แถวสำหรับผู้ใช้นี้ จากนั้นฉันก็รันการนำเข้าอีกครั้ง 3 รายการแรกได้รับการอัปเดต แต่เมื่อฉันพยายามเพิ่มแถวที่ 4 มันยังคงได้รับรหัสเอนทิตีจากรายการคอลเลกชันของฟิลด์ที่ 4 พยายามอัปเดต แต่ล้มเหลวและส่งคืนข้อผิดพลาดนี้:

 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_multipleเป็นวิธีที่ถูกต้องแน่นอน 100% - ดูfield_collection_field_deleteฟังก์ชั่นซึ่งเป็นสิ่งที่ Collection Field ใช้ในการล้างรายการเมื่อลบฟิลด์อ้างอิง
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

1
ฟังก์ชั่นเฉพาะนั้นคือการติดตั้ง hook โดยทั่วไปเมื่อมีการลบฟิลด์ใด ๆชื่อฟิลด์จะถูกส่งไปยังฟังก์ชันนั้นและการรวบรวมฟิลด์จะตรวจสอบว่ามีเอนทิตี 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 มันจะลบข้อมูลในตารางทั้งสอง แต่เมื่อฉันบันทึกผู้ใช้แถวจะได้รับการเพิ่มลงในตาราง
Smos

@Smos: เฮ้เพื่อนคุณช่วยฉันด้วยปัญหาที่คล้ายกัน ( drupal.stackexchange.com/questions/239784/ ...... ) ฉันลองใช้รหัสของคุณแล้ว แต่ไม่สามารถใช้งานได้
sisko

คำตอบ:


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)โหนดของคุณ
Bernhard Fürst

@ 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

คำตอบข้างต้นไม่ได้เป็นวิธีที่ดีที่สุดที่มีการล้างทุกรายการอื่น ๆ หายไป Fron คอลเลกชันเขตข้อมูลและวิธีอื่น ๆ ที่มี->delete()การโยนข้อผิดพลาดกับโมดูลนิติบุคคล

วิธีที่ถูกต้อง สิ่งที่ฉันทำคือ:

ในกรณีของฉันฉันต้องการลบรายการสุดท้ายภายในคอลเลกชันฟิลด์

ดูรหัสนี้ (สำหรับผู้เริ่มต้น: "จำไว้ว่าคุณจะต้องโหลดเอนทิตี $ แล้ว")

// 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_collection_field_updateจะดูแลการลบรายการ FC ที่มีอยู่ที่ถูกยกเลิกการอ้างอิง

ข้อเสียเพียงอย่างเดียวคือถ้าไม่มีการเปลี่ยนแปลงข้อมูล FC มันจะถูกลบและสร้างใหม่ แต่นั่นไม่ใช่เรื่องใหญ่สำหรับฉัน

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.