If someone loads an entity, deletes (i.e unsets) one (of the many) values of it's field_collection field, and then saves that entity, that value of that field is deleted but behind-the-scenes the field_collection_item entity that corresponded to that value is never deleted and stays around forever.

This would appear to be a perfectly valid way of deleting the value of a field in Drupal, and so field_collection needs to make sure to detect this situation and also delete everything else associated with that value - namely the associated field_collection_item entity.

This is exactly the sort of behavior core file.module does to ensure that it can delete files when they are no longer referenced by any entities (see modules/file/file.field.inc: file_field_update())

I came across this issue when importing field_collection_items with feeds, since before an 'update' import, feeds simply unsets all current values of a field in preparation for them to be replaced by the import. We have found that our field_collection_item table was getting out of hand.

Patch to follow..

Comments

jamsilver’s picture

jamsilver’s picture

Assigned: jamsilver » Unassigned
Status: Needs work » Needs review
StatusFileSize
new1.84 KB
new2.24 KB

Adding a not empty check before calling entity_delete_multiple.

fago’s picture

Status: Needs review » Needs work

Good catch.

When one uses the delete callback of the formatter, the delete is probably going to be issues twice. But as the delete_multiple issues an entity_load on the ids, I guess that shouldn't harm.

+  // Create a bare-bones entity so that we can load its previous values.
+  $original = entity_create_stub_entity($entity_type, array($id, $vid, $bundle));
+  field_attach_load($entity_type, array($id => $original), FIELD_LOAD_CURRENT, array('field_id' => $field['id']));

Why not just use $entity->original?

tim.plunkett’s picture

sachbearbeiter’s picture

sub

jamsilver’s picture

Why not just use $entity->original?

To be honest, I based this code on core file module's file_field_update() which is why I've done it this way.

However, having looked at it just now, it would appear that we cannot absolutely guarantee that an entity that has a field collection field on it will have this 'original' property set. Sure any entity created using entity.module will have it set (see EntityApiController::save()) and sure any core entity will have it set (e.g. see node_save), but it remains perfectly valid atm in Drupal for someone to create their own entity type without using entity.module - and so not have an $entity->original property available. Which fundamentally is certainly why core file module does not expect to be able to use it either.

I suppose, for efficiency, we could make the code check to see if there is an 'original' property available and if so use that, and if not fallback to the current behavior?

jamsilver’s picture

Status: Needs work » Needs review

OK - I've attached a re-roll of the patch that first checks for the existence of the 'original' property and uses that if available.

However, having made it, I'm not convinced it makes sense to do even this. Just because - logically - if it is valid for someone to come along and make a custom entity_type without using entity.module and so without an $entity->original property, then presumably it is equally valid for them to come along and make a custom entity_type without using entity.module and then use the $entity->original property for something custom that has nothing to do with the unchanged value of the entity?

To be fair, maybe such a person should be forced to get an error in that very rare situation! And start using entity.module =p

Your call which patch you want use - the one in #2 still applies on the latest dev.

jamsilver’s picture

Forgot the patch..

liquidcms’s picture

Status: Needs review » Needs work

Is it safe to say that this patch will not help with any pre-existing corrupted nodes? I applied tha ptch and i do not seem to (so far) be able to get the issue to return. However, the nodes which i could not edit before i still can not edit.

liquidcms’s picture

a bit more editing for my node and i have still been able to corrupt it and again get:

EntityMalformedException: Missing bundle property on entity of type field_collection_item. in entity_extract_ids() (line 7389 of /home/admin/public_html/mit/includes/common.inc).

not sure what i did that broke the node; will keep testing

liquidcms’s picture

i think i was able to still corrupt the node when i cloned the node (#1304214: Doesn't clone properly) and then removed items

rp7’s picture

subscribe

interx’s picture

I'm getting still getting this issue after the patch. When deleting a FieldCollectionItemEntity directly, the reference in the node's collection field remains and triggers an EntityMalformedException.

I thought I would be able to just use
$field_collection_item->delete();
or

  if ($ids = field_collection_field_item_to_ids($node->field_my_field[LANGUAGE_NONE)) {
    entity_delete_multiple('field_collection_item', $ids);
  }

But because of the stale references in the collection field that triggers :
"EntityMalformedException: Missing bundle property on entity of type field_collection_item. in entity_extract_ids() (line 7389..."

I can avoid this by manually removing the collectionfield item from the collection field like

  unset($node->field_my_field[LANGUAGE_NONE]);
  field_attach_update('node', $node);

But shouldn't that be done automatically?

daniel wentsch’s picture

Subscribing... wasn't yet able to reproduce the error yet.

Lerain’s picture

I've got this problem as well and made the following obervation - maybe someone else is able to work his magic with this hint:

So I've got a node I cannot edit anymore, receiving the error:

EntityMalformedException: Missing bundle property on entity of type field_collection_item. in entity_extract_ids() (line 7405 of /srv/www/htdocs/iav.com/data/includes/common.inc).

Now the node id is 8034 and the field_collection_item for that content type is field_global_video. So I checked the database-field: field_data_field_global_video, looked up the line of the entity_id 8034 and found the value 192 in the field: field_global_video_value.

So with the 192 in mind I went over to the field_collection_item table but couldn't find any item_id 192. Since I know that this should be a field_global_video in my case, I just inserted the line manually via PHPMyAdmin.

INSERT INTO `tablename`.`field_collection_item` (`item_id`, `field_name`) VALUES ('192', 'field_global_video');

And voila... the node is editable again. Now this is certainly not a solution, but maybe a hint.

jamsilver’s picture

Title: Delete orphaned field_collection_items when they are removed from field (i.e. in hook_field_update) » Delete orphaned field_collection_item entities when they are (programatically) removed from the host entity
Status: Needs work » Needs review

Correct me if I'm wrong, but all comments from #9 - #15 are about a different issue.

This issue is about how old, unreferenced field_collection_item entities are not removed when the field that referenced them on the 'host' entity is deleted programmatically. It is essentially a 'housekeeping' problem and has not yet been demonstrated to cause errors (e.g. corrupted nodes). The only problem with it is that the field_collection_item table can get very very big.

Issues #9 - #15 seem to be about the complimentary/reverse situation whereby when the field_collection_item entity itself is deleted programmatically, the 'host' entity that was referencing it now becomes somehow unloadable / corrupted.

I am setting this issue to 'needs review' because my patches in #2 and #8 still stand.

I am making another issue to handle the reverse problem: #1331764: Delete host entity reference when a field_collection_item entity is deleted programatically.

jamsilver’s picture

StatusFileSize
new2.24 KB

Re-rolling against latest.

jamsilver’s picture

Status: Needs review » Closed (duplicate)
TD44’s picture

Hi, i have many orphaned field collections items.
Does this patch remove all previous orphaned items ? Or does this patch works only when removing field collection items after installing this patch?

commonpike’s picture

Issue summary: View changes

Same question as @TD44 . Does this patch remove all previous orphaned items ? Or does this patch works only when removing field collection items after installing this patch?

mastoll’s picture

I have orphaned field collection items that I can't figure out how to find/see, nor how to delete. Would love to have the answer to @TD44's question! Anyone?

remyyyyy’s picture

To start, you can use a query like this one to see the orphaned field collection items :

SELECT item_id
FROM
field_collection_item field_collection_item
LEFT JOIN field_data_field_sample field_data_field_sample ON field_collection_item.item_id = field_data_field_sample.field_sample_value
LEFT JOIN node field_sample_field_collection_item ON field_data_field_sample.entity_id = field_sample_field_collection_item.nid
WHERE (( (field_sample_field_collection_item.nid IS NULL ) ))

Then, you can export the result to a comma separated list like this :

$orphaned_ids = array(1,2,3,4,5,6);

And you can delete all the orphaned field collection items with a call to function entity_delete_multiple() like this :

entity_delete_multiple('field_collection_item', $orphaned_ids);
jvieille’s picture

Deleted