Come eliminare correttamente una raccolta di campi?


9

Versione Drupal: 7.21
Versione modulo raccolta campi: 7.x-1.0-beta5

Breve spiegazione : sono impegnato nel tentativo di importare le raccolte di campi a livello di codice ma quando si eliminano alcune di esse, rimane sempre una raccolta di campi "fasulli".

Spiegazione lunga : i miei utenti hanno un campo di raccolta dei campi sul loro profilo. Questa raccolta di campi contiene 3 campi di testo. Voglio importare i dati da un database sql personalizzato nella raccolta dei campi dell'utente. Questa raccolta di campi può avere più valori. Quando importi i dati per la prima volta, tutto funziona correttamente, vedo i dati nei campi della raccolta dei campi. Grande.

Ma ecco che arriva la parte difficile. Supponiamo di importare per un utente specifico 5 righe dal database personalizzato. Vengono aggiunti alla raccolta di campi, quindi questa raccolta di campi ha 5 elementi ciascuno contenente 3 campi. Quindi elimino alcune righe dal mio database personalizzato in modo da avere solo 3 righe per questo utente. Eseguo di nuovo l'importazione, aggiornando i primi 3 elementi della raccolta di campi, ma poi mi rimangono 2 elementi dall'importazione precedente. Dovrebbero essere eliminati perché ho solo 3 righe importate ma ancora 5 elementi di raccolta dei campi.

Quindi ho cercato di eliminare questi elementi della raccolta di campi, ma rimangono sempre uno o più elementi. I campi sono vuoti quando guardo il profilo utente ma c'è ancora qualcosa lì. Diciamo che a questo punto aggiungo 5 nuove righe per l'utente nel mio database personalizzato, quindi ho 8 righe in totale per questo utente. Quindi eseguo nuovamente l'importazione. I primi 3 elementi vengono aggiornati, ma quando provo ad aggiungere la 4a riga ottiene ancora un ID entità dall'elemento della raccolta del 4o campo, tenta di aggiornarlo ma non riesce e restituisce questo errore:

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

Ho provato a eliminare gli elementi della raccolta di campi con ciascuno di questi metodi di seguito:

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

Questo è il mio codice 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();
      }
    }
  }
}

Quindi la mia domanda è: come posso eliminare gli elementi della raccolta dei campi in modo che siano effettivamente spariti?


2
entity_delete_multipleè sicuramente il modo giusto per farlo al 100% - dai un'occhiata alla field_collection_field_deletefunzione, che è quella che Field Collection stessa usa per ripulire gli oggetti quando il campo di riferimento viene rimosso
Clive

Grazie mille per la tua risposta, lo apprezzo molto. Ti capita di sapere quali argomenti dovrei fornire con field_collection_field_delete? Vedo che la firma è field_collection_field_delete ($ entity_type, $ entity, $ field, $ instance, $ langcode e $ items) ma non so davvero quali valori inserire: $ entity (è questo l'utente o la raccolta dei campi ?), $ field (restituisce valore da field_collection_item_load?), $ instance, $ langcode (und?) e $ items.
Smos

1
Quella particolare funzione è un'implementazione hook, fondamentalmente quando un campo viene eliminato il nome del campo viene passato a quella funzione e la Raccolta campi verifica se esiste un'entità FC associata a quell'istanza di campo. Se c'è, lo elimina usando entity_delete_multiple(). Potrebbe essere necessario eseguire cron un paio di volte dopo aver eliminato i campi (i dati dei campi vengono eliminati in base a una pianificazione in modo da non gravare un singolo caricamento di pagina con tutte le elaborazioni da eseguire)
Clive

Ho provato a utilizzare entity_delete_multiple di nuovo e ho notato che gli elementi vengono eliminati nella tabella field_collection_item ma i campi esistono ancora nella tabella field_data_field_collection_name. Penso che questo causi l'errore fatale Call al metodo indefinito stdClass :: save () perché dovrebbero essere campi ma non hanno un elemento di raccolta di campi collegato ad esso. Quando uso $ field_collection_item-> deleteRevision cancella i dati in entrambe le tabelle, ma quando salvo l'utente viene aggiunta una riga alla tabella field_data_field_collection_name e questo è un elemento di raccolta di campi vuoto.
Smos

@Smos: hey amico, puoi aiutarmi con un problema simile ( drupal.stackexchange.com/questions/239784/… )? Ho provato parti rilevanti del tuo codice ma non sono riuscito a farlo funzionare.
Sisko,

Risposte:


13

Mi sono imbattuto in un caso d'uso simile in cui volevo mappare alcuni dati in una raccolta di campi durante hook_feeds_presave () poiché la struttura di origine era troppo complessa per Feed. Ho scoperto che entity_delete_multiple () ha rimosso gli elementi della raccolta di campi, ma quando ho modificato il nodo, c'erano ancora un mucchio di raccolte di campi vuoti. Disattivare ed eliminare ha fatto il trucco, che ho trovato qui: https://drupal.stackexchange.com/a/31820/2762

Se l'origine dei feed è cambiata, elimino tutti gli elementi della raccolta di campi e li ricrea. Spero sia utile.

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

Questo approccio in 2 passaggi è l'unico modo che funziona con AFAIK. Non dimenticare il node_save($node)tuo nodo.
Bernhard Fürst,

@ BernhardFürst in realtà non abbiamo bisogno node_save($node), DrupalEntityControllerfarà questo lavoro
coffeduong

8

Il modo migliore per farlo ora è call $field_collection->delete()e che gestirà tutto.

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

Le risposte di cui sopra non sono il modo migliore, con deselezionate tutte le altre voci scomparse dalla raccolta dei campi, e l'altro modo ->delete()genera un bug con il modulo Entity.

Modo corretto. Bene, quello che ho fatto è stato questo:

Nel mio caso, volevo eliminare l'ultimo elemento all'interno della raccolta di campi

dai un'occhiata a questo codice, (per i principianti: "ricorda che devi avere l'entità $ già caricata")

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

È tutto! l'altro modo è

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

Il modo sopra è buono usando la entity_metadata_wrapperfunzione ma con quel modo c'è un bug complesso che non so come risolverlo, puoi controllarlo su https://drupal.org/node/1880312 e dopo applicare la patch in # 9 ottieni il prossimo numero, controlla qui https://drupal.org/node/2186689 questo errore è anche se usi la ->delete()funzione.

Spero che aiuti qualcuno.


0

utilizzando vbo per eliminare gli elementi della raccolta di campi. rimuoverà automaticamente la relazione di campo con l'entità host dell'elemento della raccolta di campi.


0

Ho avuto un problema simile, in cui sto importando dati da un feed in un articolo FC. Quando viene effettuato un aggiornamento a un'entità host dal feed e sto importando tali modifiche, volevo assicurarmi che tutti gli elementi FC esistenti che non esistono più dalla fonte del feed siano stati rimossi.

La mia soluzione:

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

E questo è tutto. Hook_field_update ( field_collection_field_update) di Field Collection si occuperà effettivamente di eliminare tutti gli elementi FC esistenti che sono stati de-referenziati.

L'unico aspetto negativo di questo è se non ci sono stati cambiamenti nei dati FC, vengono comunque eliminati e ricreati. Ma non è un grosso problema per me.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.