¿Cómo eliminar correctamente una colección de campo?

9

Versión de Drupal: 7.21
Versión del módulo de colección de campo: 7.x-1.0-beta5

Breve explicación : estoy ocupado tratando de importar colecciones de campos mediante programación, pero al eliminar algunas de ellas siempre queda alguna colección de campo 'falsa'.

Explicación larga : Mis usuarios tienen un campo de colección de campos en su perfil. Esta colección de campos contiene 3 campos de texto. Quiero importar datos de una base de datos SQL personalizada a la colección de campo del usuario. Esta colección de campos puede tener múltiples valores. Cuando importo los datos por primera vez, todo funciona bien, veo los datos en los campos de la colección de campos. Excelente.

Pero aquí viene la parte difícil. Digamos que importo para un usuario específico 5 filas de la base de datos personalizada. Se agregan a la colección de campos, por lo que esta colección de campos tiene 5 elementos, cada uno con 3 campos. Luego elimino algunas filas de mi base de datos personalizada para que solo me queden 3 filas para este usuario. Ejecuto la importación nuevamente, actualizando los primeros 3 elementos de la colección de campos, pero luego me quedan 2 elementos de la importación anterior. Deben eliminarse porque solo tengo 3 filas importadas pero todavía 5 elementos de colección de campo.

Así que intenté eliminar estos elementos de la colección de campo, pero siempre quedan uno o más elementos. Los campos están vacíos cuando miro el perfil del usuario, pero todavía hay algo allí. Digamos que en este punto agrego 5 filas nuevas para el usuario en mi base de datos personalizada, por lo que tengo 8 filas en total para este usuario. Luego ejecuto la importación nuevamente. Los primeros 3 elementos se actualizan, pero luego, cuando intento agregar la cuarta fila, aún obtiene una identificación de entidad del cuarto elemento de la colección de campos, intenta actualizarlo pero falla y devuelve este error:

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

Intenté eliminar los elementos de la colección de campos con cada uno de estos métodos a continuación:

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

Este es mi código completo:

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

Entonces mi pregunta es: ¿cómo elimino los elementos de la colección de campo para que realmente se hayan ido?

Smos
fuente
2
entity_delete_multiplees 100% definitivamente la forma correcta de hacerlo: eche un vistazo a la field_collection_field_deletefunción, que es lo que Field Collection usa para limpiar los elementos cuando se elimina el campo de referencia
Clive
Muchas gracias por su respuesta, lo agradezco. ¿Sabes qué argumentos debo proporcionar con field_collection_field_delete? Veo que la firma es field_collection_field_delete ($ entity_type, $ entity, $ field, $ instance, $ langcode, & $ items) pero realmente no sé qué valores poner: $ entity (es el usuario o la colección de campos ?), $ field (valor de retorno de field_collection_item_load?), $ instancia, $ langcode (und?) y $ items.
Smos
1
Esa función en particular es una implementación de enlace, básicamente cuando se elimina cualquier campo, el nombre del campo se pasa a esa función, y Field Collection verifica si hay una entidad FC asociada a esa instancia de campo. Si lo hay, lo elimina usando entity_delete_multiple(). Es posible que deba ejecutar cron un par de veces después de eliminar campos (los datos de campo se depuran en un horario para no cargar una sola carga de página con todo ese procesamiento que hacer)
Clive
Intenté usar entity_delete_multiple nuevamente y noté que los elementos se eliminan en la tabla field_collection_item pero los campos aún existen en la tabla field_data_field_collection_name. Creo que esto causa el error fatal Llamar al método indefinido stdClass :: save () porque se supone que son campos pero no tienen un elemento de colección de campos vinculado a él. Cuando uso $ field_collection_item-> deleteRevision, elimina los datos en ambas tablas, pero cuando guardo al usuario, se agrega una fila a la tabla field_data_field_collection_name y ese es un elemento de colección de campos en blanco.
Smos
@Smos: Hola amigo, ¿puedes ayudarme con un problema similar ( drupal.stackexchange.com/questions/239784/… )? Intenté partes relevantes de su código pero no pude hacerlo funcionar.
sisko

Respuestas:

13

Me encontré con un caso de uso similar en el que quería asignar algunos datos a una colección de campos durante hook_feeds_presave () ya que la estructura de origen era demasiado compleja para los feeds. Descubrí que entity_delete_multiple () eliminó los elementos de la colección de campos, pero cuando edité el nodo, todavía había un montón de colección de campos vacía allí. Desarmar y eliminar hizo el truco, que encontré aquí: https://drupal.stackexchange.com/a/31820/2762

Si la fuente de los feeds ha cambiado, elimino todos los elementos de la colección de campos y vuelvo a crearlos. Espero que esto sea útil.

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);
Vincent
fuente
Este enfoque de 2 pasos es la única forma en que funciona AFAIK. No te olvides de node_save($node)tu nodo.
Bernhard Fürst
@ BernhardFürst realidad no necesitamos node_save($node), DrupalEntityControllerva a hacer este trabajo
coffeduong
8

La mejor manera de hacer esto ahora es llamar $field_collection->delete()y eso se encargará de todo.

 <?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);
      }
    }
 ?>
benjy
fuente
0

Las respuestas anteriores no son la mejor manera, ya que todos los demás elementos desaparecieron de la colección de campo y, por otro lado, ->delete()arroja un error con el módulo Entidad.

Forma correcta. Bueno, lo que hice fue esto:

En mi caso, quería eliminar el último elemento de la colección de campo.

eche un vistazo a este código (para principiantes: "recuerde que debe tener la entidad $ cargada")

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

¡eso es todo! la otra forma es

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

La forma anterior es buena usando la entity_metadata_wrapperfunción, pero de esa manera hay un error complejo que no sé cómo resolverlo, puede verificarlo en https://drupal.org/node/1880312 y luego aplicar el parche en el # 9 obtienes el siguiente problema, compruébalo aquí https://drupal.org/node/2186689 este error también es si usas la ->delete()función.

Espero que ayude a alguien.

svelandiag
fuente
0

usando vbo para eliminar los elementos de la colección de campos. eliminará automáticamente la relación de campo con la entidad anfitriona del elemento de colección de campos.

terry zhang
fuente
0

Tuve un problema similar, donde estoy importando datos de un feed a un elemento FC. Cuando se realiza una actualización a una entidad de host desde el feed, y estoy importando esos cambios, quería asegurarme de que se eliminaran todos los elementos FC existentes que ya no existen desde el origen del feed.

Mi solución:

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

Y eso es. Hook_field_update ( field_collection_field_update) de Field Collection se encargará de eliminar realmente cualquier elemento FC existente que haya sido desreferenciado .

El único inconveniente de esto es que si no hubo cambios en los datos de FC, se eliminan y se vuelven a crear de todos modos. Pero eso no es gran cosa para mí.

Brian
fuente