Як правильно видалити колекцію полів?


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функцію, яку саме колекція Field Field використовує для очищення елементів при видаленні посилального поля
Clive

Дякую за вашу відповідь, я ціную це. Чи знаєте ви, які аргументи я повинен надати field_collection_field_delete? Я бачу, що підпис є field_collection_field_delete ($ entit_type, $ entity, $ field, $ instance, $ langcode, & $ items), але я не знаю, які значення потрібно вводити: $ object (це користувач чи колекція поля ?), $ field (повернене значення з field_collection_item_load?), $ instance, $ langcode (und?) та $ items.
Смос

1
Ця особлива функція - це гакова реалізація, в основному, коли будь-яке поле видалено, ім'я поля передається цій функції, і Field Collection перевіряє, чи є об'єкт FC пов'язаний з цим екземпляром поля. Якщо є, він видаляє його за допомогою entity_delete_multiple(). Після видалення полів вам може знадобитися запустити cron кілька разів (дані поля видаляються за графіком, щоб не навантажувати завантаження однієї сторінки всіма обробками)
Clive

Я знову спробував використати entit_delete_multiple і помітив, що елементи видаляються в таблиці field_collection_item, але поля все ще існують у імені таблиці field_data_field_collection_name. Я думаю, що це спричиняє фатальну помилку Виклик невизначеному методу stdClass :: save (), оскільки вони повинні бути полями, але вони не мають пов'язаного з ним елемента збору полів. Коли я використовую $ field_collection_item-> deleteRevision, він видаляє дані в обох таблицях, але коли я зберігаю користувача, рядок додається до таблиці field_data_field_collection_name і це пустий елемент колекції поля.
Смос

@Smos: ей, приятелю, чи можеш ти допомогти мені у подібному питанні ( drupal.stackexchange.com/questions/239784/… )? Я спробував відповідні біти вашого коду, але не зміг його працювати.
sisko

Відповіді:


13

Я натрапив на подібний випадок використання, коли я хотів відобразити деякі дані в колекцію полів під час mock_feeds_presave (), оскільки структура джерела була надто складною для каналів. Я виявив, що object_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);

Цей двоступеневий підхід - єдиний спосіб, який працює в AFAIK. Не забудьте node_save($node)свій вузол.
Бернхард Фюрст

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

І це все. Field_watch_field_update ( field_collection_field_update) Field Collection подбає про те, щоб фактично видалити будь-які наявні елементи FC, на які було знято посилання.

Єдиним недоліком цього є те, якщо в даних FC не було змін, вони все одно видаляються та створюються заново. Але це для мене не велика справа.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.