Wie lösche ich eine Feldsammlung richtig?

9

Drupal-Version: 7.21
Version des Feldsammlungsmoduls: 7.x-1.0-beta5

Kurze Erklärung : Ich bin damit beschäftigt, Feldsammlungen programmgesteuert zu importieren, aber beim Löschen einiger von ihnen bleibt immer eine gefälschte Feldsammlung übrig.

Lange Erklärung : Meine Benutzer haben ein Feldsammlungsfeld in ihrem Profil. Diese Feldsammlung enthält 3 Textfelder. Ich möchte Daten aus einer benutzerdefinierten SQL-Datenbank in die Feldsammlung des Benutzers importieren. Diese Feldsammlung kann mehrere Werte haben. Wenn ich die Daten zum ersten Mal importiere, funktioniert alles einwandfrei. Ich sehe die Daten in den Feldern der Feldsammlung. Toll.

Aber hier kommt der schwierige Teil. Angenommen, ich importiere für einen bestimmten Benutzer 5 Zeilen aus der benutzerdefinierten Datenbank. Sie werden der Feldsammlung hinzugefügt, sodass diese Feldsammlung 5 Elemente enthält, die jeweils 3 Felder enthalten. Dann lösche ich einige Zeilen aus meiner benutzerdefinierten Datenbank, sodass nur noch 3 Zeilen für diesen Benutzer übrig sind. Ich führe den Import erneut aus und aktualisiere die ersten 3 Elemente der Feldsammlung, aber dann bleiben mir 2 Elemente aus dem vorherigen Import übrig. Sie sollten gelöscht werden, da ich nur 3 importierte Zeilen, aber noch 5 Feldsammlungselemente habe.

Also habe ich versucht, diese Feldsammlungselemente zu löschen, aber es sind immer noch ein oder mehrere Elemente übrig. Die Felder sind leer, wenn ich mir das Benutzerprofil ansehe, aber da ist noch etwas. Angenommen, ich füge an dieser Stelle 5 neue Zeilen für den Benutzer in meine benutzerdefinierte Datenbank ein, sodass ich insgesamt 8 Zeilen für diesen Benutzer habe. Dann führe ich den Import erneut aus. Die ersten 3 Elemente werden aktualisiert, aber wenn ich versuche, die 4. Zeile hinzuzufügen, wird immer noch eine Entitäts-ID aus dem 4. Feldsammlungselement abgerufen, versucht, sie zu aktualisieren, schlägt jedoch fehl und gibt diesen Fehler zurück:

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

Ich habe versucht, die Feldsammlungselemente mit jeder der folgenden Methoden zu löschen:

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

Dies ist mein vollständiger Code:

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

Meine Frage lautet also: Wie lösche ich Feldsammlungselemente, damit sie tatsächlich verschwunden sind?

Smos
quelle
2
entity_delete_multipleist definitiv 100% der richtige Weg, dies zu tun - werfen Sie einen Blick auf die field_collection_field_deleteFunktion, mit der Field Collection selbst Elemente bereinigt, wenn das Feld, auf das verwiesen wird, entfernt wird
Clive
Vielen Dank für Ihre Antwort, ich weiß das zu schätzen. Wissen Sie zufällig, welche Argumente ich mit field_collection_field_delete liefern soll? Ich sehe, dass die Signatur field_collection_field_delete ist ($ entity_type, $ entity, $ field, $ instance, $ langcode und $ items), aber ich weiß nicht wirklich, welche Werte eingegeben werden sollen: $ entity (ist dies der Benutzer oder die Feldsammlung ?), $ field (Rückgabewert von field_collection_item_load?), $ instance, $ langcode (und?) und $ items.
Smos
1
Diese bestimmte Funktion ist eine Hook-Implementierung. Wenn ein Feld gelöscht wird, wird der Feldname an diese Funktion übergeben, und die Feldsammlung prüft, ob dieser Feldinstanz eine FC-Entität zugeordnet ist. Wenn ja, wird es mit gelöscht entity_delete_multiple(). Möglicherweise müssen Sie cron ein paar Mal ausführen, nachdem Sie Felder gelöscht haben (Felddaten werden nach einem Zeitplan gelöscht, um das Laden einer einzelnen Seite nicht mit all dieser Verarbeitung zu belasten)
Clive
Ich habe erneut versucht, entity_delete_multiple zu verwenden, und festgestellt, dass die Elemente in der Tabelle field_collection_item gelöscht werden, die Felder jedoch weiterhin in der Tabelle field_data_field_collection_name vorhanden sind. Ich denke, dies verursacht den schwerwiegenden Fehler. Rufen Sie die undefinierte Methode stdClass :: save () auf, da sie Felder sein sollen, aber kein Feldsammlungselement damit verknüpft ist. Wenn ich $ field_collection_item-> deleteRevision verwende, werden die Daten in beiden Tabellen gelöscht. Wenn ich den Benutzer speichere, wird der Tabelle field_data_field_collection_name eine Zeile hinzugefügt, und das ist ein leeres Feldsammlungselement.
Smos
@Smos: Hey Kumpel, kannst du mir bei einem ähnlichen Problem helfen ( drupal.stackexchange.com/questions/239784/… )? Ich habe relevante Teile Ihres Codes ausprobiert, aber ich konnte ihn nicht zum Laufen bringen.
Sisko

Antworten:

13

Ich bin auf einen ähnlichen Anwendungsfall gestoßen, bei dem ich während hook_feeds_presave () einige Daten einer Feldsammlung zuordnen wollte, da die Quellstruktur für Feeds zu komplex war. Ich habe festgestellt, dass entity_delete_multiple () die Feldsammlungselemente entfernt hat, aber als ich den Knoten bearbeitet habe, gab es dort immer noch eine Menge leerer Feldsammlungen. Das Deaktivieren und Löschen hat den Trick ausgeführt, den ich hier gefunden habe: /drupal//a/31820/2762

Wenn sich die Feedquelle geändert hat, lösche ich alle Feldsammlungselemente und erstelle sie neu. Hoffe das ist hilfreich.

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
quelle
Dieser 2-Stufen-Ansatz ist der einzige Weg, der AFAIK funktioniert. Vergessen Sie nicht node_save($node)Ihren Knoten.
Bernhard Fürst
@ BernhardFürst eigentlich brauchen wir nicht node_save($node), DrupalEntityControllerwerden diesen Job machen
coffeduong
8

Der beste Weg, dies jetzt zu tun, ist ein Anruf $field_collection->delete()und das wird alles erledigen.

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

Die obigen Antworten sind nicht der beste Weg, da alle anderen Elemente aus der Feldsammlung verschwunden sind und der andere Weg ->delete()einen Fehler mit dem Entitätsmodul auslöst.

Der richtige Weg. Nun, was ich getan habe, war Folgendes:

In meinem Fall wollte ich das letzte Element in der Feldsammlung löschen

Schauen Sie sich diesen Code an (für Anfänger: "Denken Sie daran, dass die Entität $ bereits geladen sein muss").

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

das ist alles! der andere Weg ist

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

Der obige Weg ist gut mit der entity_metadata_wrapperFunktion, aber auf diese Weise gibt es einen komplexen Fehler, den ich nicht lösen kann. Sie können ihn unter https://drupal.org/node/1880312 überprüfen und nach dem Anwenden des Patches in # 9 Wenn Sie das nächste Problem erhalten, überprüfen Sie es hier https://drupal.org/node/2186689 Dieser Fehler tritt auch auf, wenn Sie die ->delete()Funktion verwenden.

Hoffe es hilft jemandem.

svelandiag
quelle
0

Verwenden von vbo zum Löschen der Feldsammlungselemente. Die Feldbeziehung zur Host-Entität des Feldsammlungselements wird automatisch entfernt.

Frottee Zhang
quelle
0

Ich hatte ein ähnliches Problem, bei dem ich Daten aus einem Feed in ein FC-Element importiere. Wenn aus dem Feed eine Aktualisierung einer Host-Entität vorgenommen wird und ich diese Änderungen importiere, wollte ich sicherstellen, dass alle vorhandenen FC-Elemente, die nicht mehr aus der Feed-Quelle vorhanden sind, entfernt wurden.

Meine Lösung:

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

Und das ist es. Hook_field_update ( field_collection_field_update) von Field Collection sorgt dafür, dass alle vorhandenen FC-Elemente, auf die verwiesen wurde, tatsächlich gelöscht werden.

Der einzige Nachteil dabei ist, dass die FC-Daten, wenn sie nicht geändert wurden, gelöscht und trotzdem neu erstellt werden. Aber das ist keine große Sache für mich.

Brian
quelle