Bir alan koleksiyonu nasıl düzgün bir şekilde silinir?


9

Drupal sürümü: 7.21
Alan toplama modülü sürümü: 7.x-1.0-beta5

Kısa açıklama : Alan koleksiyonlarını programlı olarak içe aktarmaya çalışmakla meşgulüm, ancak bazılarını silerken her zaman 'sahte' alan koleksiyonu kaldı.

Uzun açıklama : Kullanıcılarımın profillerinde bir alan toplama alanı var. Bu alan koleksiyonu 3 metin alanı içerir. Kullanıcının alan koleksiyonuna özel bir sql veritabanından veri almak istiyorum. Bu alan koleksiyonunun birden fazla değeri olabilir. Verileri ilk kez içe aktardığımda her şey yolunda gidiyor, veriyi alan koleksiyonunun alanlarında görüyorum. Harika.

Ama işin zor kısmı geliyor. Özel bir veritabanından belirli bir kullanıcı için 5 satır içe aktardığımı varsayalım. Alan koleksiyonuna eklenirler, bu nedenle bu alan koleksiyonunda her biri 3 alan içeren 5 öğe bulunur. Sonra bu kullanıcı için sadece 3 satır kaldı böylece benim özel veritabanından bazı satırları silin. Alan koleksiyonunun ilk 3 öğesini güncelleyerek içe aktarmayı tekrar çalıştırıyorum, ancak önceki içe aktarmadan 2 öğe kaldı. Yalnızca 3 içe aktarılmış satırım, ancak yine de 5 alan toplama öğem olduğu için silinmelidir.

Bu yüzden bu alan koleksiyonu öğelerini silmeye çalıştım, ancak her zaman bir veya daha fazla öğe kaldı. Kullanıcı profiline baktığımda alanlar boş ama hala orada bir şeyler var. Bu noktada, özel veritabanımda kullanıcı için 5 yeni satır eklediğimi varsayalım, bu nedenle bu kullanıcı için toplam 8 satırım var. Sonra içe aktarmayı tekrar çalıştırıyorum. İlk 3 öğe güncellenir, ancak daha sonra 4. satırı eklemeye çalıştığımda hala 4. alan toplama öğesinden bir varlık kimliği alır, güncellemeye çalışır, ancak başarısız olur ve bu hatayı döndürür:

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

Aşağıdaki yöntemlerin her biri ile alan toplama öğeleri silmeyi denedim:

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

Bu tam kodum:

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

Benim sorum şu: Alan toplama öğelerini gerçekten gitmeleri için nasıl silebilirim?


2
entity_delete_multiple% 100 kesinlikle bunu yapmanın doğru yoludur - field_collection_field_deletefonksiyona bir göz atın , referans alınan alan kaldırıldığında Alan Koleksiyonu'nun kendisi öğeleri temizlemek için kullandığı şey
Clive

Yanıtınız için çok teşekkürler, teşekkür ederim. Field_collection_field_delete ile hangi argümanları sunmam gerektiğini biliyor musunuz? İmzanın field_collection_field_delete ($ entity_type, $ entity, $ field, $ instance, $ langcode, & $ öğeleri) olduğunu görüyorum ama gerçekten hangi değerlerin konulacağını bilmiyorum: $ entity (bu kullanıcı veya alan koleksiyonu ?), $ field (field_collection_item_load? dan dönen değer), $ örneği, $ langcode (und?) ve $ öğeleri.
Smos

1
Bu belirli işlev bir kanca uygulamasıdır, temel olarak herhangi bir alan silindiğinde alan adı o işleve geçirilir ve Alan Koleksiyonu, o alan örneğiyle ilişkilendirilmiş bir FC varlığı olup olmadığını kontrol eder. Varsa, kullanarak siler entity_delete_multiple(). Alanları sildikten sonra birkaç kez cron çalıştırmanız gerekebilir (alan verileri, yapılacak tüm işlemlerle tek bir sayfa yükü yüklememek için bir programda temizlenir)
Clive

Tekrar varlık_delete_multiple kullanarak denedim ve öğeleri field_collection_item tablo silinir fark ettim ama alanlar hala field_data_field_collection_name tablo var. Bu ölümcül hataya neden olduğunu düşünüyorum Alanlar olması gerekiyordu ama ona bağlı bir alan toplama öğesi yok tanımsız yöntem stdClass :: save () çağırın. $ Field_collection_item-> deleteRevision kullandığımda her iki tablodaki verileri siler, ancak kullanıcıyı kaydettiğimde field_data_field_collection_name tablosuna bir satır eklenir ve bu boş bir alan toplama öğesidir.
Smos

@Smos: hey dostum, benzer bir konuda bana yardımcı olabilir misin ( drupal.stackexchange.com/questions/239784/… )? Kodunuzun ilgili bitlerini denedim, ancak çalıştıramadım.
sisko

Yanıtlar:


13

Kaynak yapı Feeds için çok karmaşık olduğundan hook_feeds_presave () sırasında bir alan koleksiyonuna bazı verileri eşlemek istedim benzer bir kullanım durumu koştu. Entity_delete_multiple () 'nin alan toplama öğelerini kaldırdığını gördüm, ancak düğümü düzenlediğimde hala orada bir sürü boş alan koleksiyonu vardı. Burada bulduğum hileyi kaldırmak ve silmek hile yaptı: https://drupal.stackexchange.com/a/31820/2762

Besleme kaynağı değiştiyse, tüm alan toplama öğelerini silip yeniden oluştururum. Umarım bu yardımcı olur.

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

Bu 2 adımlı yaklaşım AFAIK'ın tek yolu. Düğümüne unutma node_save($node).
Bernhard Fürst

Aslında ihtiyacımız yok BernhardFürst @ node_save($node), DrupalEntityControllerbu işi yapacak
coffeduong

8

Bunu şimdi yapmanın en iyi yolu çağrıdır $field_collection->delete()ve bu her şeyi halledecektir.

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

Yukarıdaki cevaplar en iyi yol değildir, diğer tüm öğeler alan koleksiyonunda kaybolur ve diğer yolla ->delete()Varlık modülü ile bir hata atar.

Doğru yol. Peki ne yaptım bu:

Benim durumumda, alan koleksiyonundaki son öğeyi silmek istedim

bu koda bir göz atın (yeni başlayanlar için: "$ varlığının önceden yüklenmiş olması gerektiğini unutmayın")

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

bu kadar! diğer yol

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

Yukarıdaki yol entity_metadata_wrapperfonksiyonu kullanmak iyidir, ancak bu şekilde nasıl çözüleceğini bilmediğim karmaşık bir hata vardır, https://drupal.org/node/1880312 adresinden kontrol edebilir ve # 9'da yamayı uygulayabilirsiniz. bir sonraki sayıyı alırsanız, buradan kontrol edin https://drupal.org/node/2186689 Bu hata ->delete()işlevi kullanıyorsanız.

Umarım birine yardımcı olur.


0

alan toplama öğelerini silmek için vbo kullanarak. alan koleksiyonu öğesi ana bilgisayarı varlığı ile alan ilişkisini otomatik olarak kaldıracaktır.


0

Benzer bir sorun yaşadım, burada bir feed'den bir FC öğesine veri aktarıyorum. Feed'den bir ana makine varlığına güncelleme yapıldığında ve bu değişiklikleri içe aktardığımda, artık feed kaynağında bulunmayan mevcut FC öğelerinin kaldırıldığından emin olmak istedim.

Çözümüm:

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

Ve bu kadar. Field Collection'ın hook_field_update ( field_collection_field_update) yöntemi, atıfta bulunulan mevcut tüm FC öğelerini gerçekten siler .

Bunun tek dezavantajı, FC verilerinde herhangi bir değişiklik olmaması, silinmesi ve yine de yeniden oluşturulmasıdır. Ama bu benim için önemli değil.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.