web-dev-qa-db-fra.com

Comment supprimer correctement une collection de champs?

Version Drupal: 7.21
Version du module de collecte de champs: 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".

Longue explication: 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 reste 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();
      }
    }
  }
}

Donc ma question est: Comment puis-je supprimer des éléments de collection de champs pour qu'ils soient réellement partis?

9
Smos

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

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

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

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 bogue 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 manière ci-dessus est bonne en utilisant la fonction entity_metadata_wrapper 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 correctif dans # 9, vous obtenez le prochain problème, vérifiez-le ici https://drupal.org/node/2186689 ce bogue est également si vous utilisez la fonction ->delete() une fonction.

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

0
svelandiag

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. Hook_field_update de la collection de champs (field_collection_field_update) se chargera de supprimer les éléments FC existants qui ont été dé-référencés.

Le seul inconvénient est que s'il n'y avait pas 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.

0
Brian

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 de l'élément de collection de champs.

0
terry zhang