Comment supprimer correctement une collection de champs?

9

Version Drupal: 7.21
Version du module de collecte sur le terrain: 7.x-1.0-beta5

Brève explication : Je suis en train d'essayer d'importer des collections de champs par programme, mais lors de la suppression de certaines d'entre elles, il reste toujours une collection de champs «fausse».

Explication longue : Mes utilisateurs ont un champ de collecte de champs sur leur profil. Cette collection de champs contient 3 champs de texte. Je souhaite importer des données d'une base de données SQL personnalisée dans la collection de champs de l'utilisateur. Cette collection de champs peut avoir plusieurs valeurs. Lorsque j'importe les données pour la première fois, tout fonctionne correctement, je vois les données dans les champs de la collection de champs. Génial.

Mais voici la partie délicate. Supposons que j'importe pour un utilisateur spécifique 5 lignes de la base de données personnalisée. Ils sont ajoutés à la collection de champs, donc cette collection de champs contient 5 éléments contenant chacun 3 champs. Ensuite, je supprime certaines lignes de ma base de données personnalisée afin qu'il ne me reste que 3 lignes pour cet utilisateur. J'exécute à nouveau l'importation, en mettant à jour les 3 premiers éléments de la collection de champs, mais je me retrouve avec 2 éléments de l'importation précédente. Ils doivent être supprimés car je n'ai que 3 lignes importées mais toujours 5 éléments de collection de champs.

J'ai donc essayé de supprimer ces éléments de collection de champs, mais il reste toujours un ou plusieurs éléments. Les champs sont vides quand je regarde le profil utilisateur mais il y a encore quelque chose. Disons qu'à ce stade, j'ajoute 5 nouvelles lignes pour l'utilisateur dans ma base de données personnalisée, donc j'ai 8 lignes au total pour cet utilisateur. Ensuite, je lance à nouveau l'importation. Les 3 premiers éléments sont mis à jour, mais lorsque j'essaie d'ajouter la 4e ligne, il obtient toujours un identifiant d'entité du 4e élément de collection de champs, essaie de le mettre à jour mais échoue et renvoie cette erreur:

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

J'ai essayé de supprimer les éléments de collection de champs avec chacune de ces méthodes ci-dessous:

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

Voici mon code complet:

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

Ma question est donc la suivante: comment supprimer des éléments de collection de champs pour qu'ils soient réellement partis?

Smos
la source
2
entity_delete_multipleest 100% certainement la bonne façon de le faire - jetez un œil à la field_collection_field_deletefonction, qui est ce que Field Collection utilise lui-même pour nettoyer les éléments lorsque le champ référencé est supprimé
Clive
Merci beaucoup pour votre réponse, je l'apprécie. Savez-vous par hasard quels arguments je dois fournir avec field_collection_field_delete? Je vois que la signature est field_collection_field_delete ($ entity_type, $ entity, $ field, $ instance, $ langcode, & $ items) mais je ne sais pas vraiment quelles valeurs mettre: $ entity (est-ce l'utilisateur ou la collection de champs ?), $ field (valeur de retour de field_collection_item_load?), $ instance, $ langcode (und?) et $ items.
Smos
1
Cette fonction particulière est une implémentation de crochet, essentiellement lorsque tout champ est supprimé le nom du champ est transmis à cette fonction, et vérifie Collection champ s'il y a une entité FC associée à cette instance sur le terrain. S'il y en a un, il le supprime en utilisant entity_delete_multiple(). Vous devrez peut-être exécuter cron plusieurs fois après avoir supprimé des champs (les données de champ sont purgées selon un calendrier afin de ne pas surcharger un seul chargement de page avec tout ce traitement à faire)
Clive
J'ai essayé à nouveau d'utiliser entity_delete_multiple et j'ai remarqué que les éléments sont supprimés dans la table field_collection_item mais les champs existent toujours dans la table field_data_field_collection_name. Je pense que cela provoque l'erreur fatale Appel à la méthode non définie stdClass :: save () car ils sont censés être des champs mais ils n'ont pas d'élément de collection de champs lié. Lorsque j'utilise $ field_collection_item-> deleteRevision, il supprime les données des deux tables mais lorsque j'enregistre l'utilisateur, une ligne est ajoutée à la table field_data_field_collection_name et c'est un élément de collection de champs vide.
Smos
@Smos: hé mon pote, pouvez-vous m'aider avec un problème similaire ( drupal.stackexchange.com/questions/239784/… )? J'ai essayé des morceaux pertinents de votre code mais je n'ai pas pu le faire fonctionner.
sisko

Réponses:

13

J'ai rencontré un cas d'utilisation similaire où je voulais mapper certaines données dans une collection de champs pendant hook_feeds_presave () car la structure source était trop complexe pour Feeds. J'ai trouvé que entity_delete_multiple () supprimait les éléments de la collection de champs, mais lorsque j'ai modifié le nœud, il y avait encore un tas de collections de champs vides. La suppression et la suppression ont fait l'affaire, que j'ai trouvée ici: https://drupal.stackexchange.com/a/31820/2762

Si la source du flux a changé, je supprime tous les éléments de la collection de champs et recrée. J'espère que cela vous sera 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);
Vincent
la source
Cette approche en 2 étapes est le seul moyen qui fonctionne AFAIK. N'oubliez pas node_save($node)votre nœud.
Bernhard Fürst
@ BernhardFürst en fait, nous n'avons pas besoin node_save($node), DrupalEntityControllerfera ce travail
coffeduong
8

La meilleure façon de le faire maintenant est d'appeler $field_collection->delete()et de tout gérer.

 <?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
la source
0

Les réponses ci-dessus ne sont pas la meilleure façon, avec unset tous les autres éléments ont disparu de la collection de champs, et dans l'autre sens avec ->delete()jette un bug avec le module Entity.

Bonne façon. Eh bien, ce que j'ai fait, c'est ceci:

Dans mon cas, je voulais supprimer le dernier élément de la collection de champs

jetez un oeil à ce code, (pour les débutants: "rappelez-vous que vous devez avoir l'entité $ déjà chargée")

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

c'est tout! l'autre voie est

//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 façon ci-dessus est bonne en utilisant la entity_metadata_wrapperfonction mais avec cette façon il y a un bug complexe que je ne sais pas comment le résoudre, vous pouvez le vérifier sur https://drupal.org/node/1880312 et après avoir appliqué le patch dans # 9 vous obtenez le prochain problème, vérifiez-le ici https://drupal.org/node/2186689 ce bug est également si vous utilisez la ->delete()fonction.

J'espère que cela aide quelqu'un.

svelandiag
la source
0

en utilisant vbo pour supprimer les éléments de la collection de champs. il supprimera automatiquement la relation de champ avec l'entité hôte d'élément de collection de champs.

Terry zhang
la source
0

J'ai eu un problème similaire, où j'importe des données d'un flux dans un élément FC. Lorsqu'une mise à jour d'une entité hôte est effectuée à partir du flux et que j'importe ces modifications, je voulais m'assurer que tous les éléments FC existants qui n'existent plus dans la source du flux ont été supprimés.

Ma solution:

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

Et c'est tout. Le hook_field_update ( field_collection_field_update) de Field Collection se chargera de supprimer réellement tous les éléments FC existants qui ont été dé-référencés.

Le seul inconvénient est que s'il n'y a pas eu de changement dans les données FC, elles sont supprimées et recréées de toute façon. Mais ce n'est pas un gros problème pour moi.

Brian
la source