I have run into the following issue where the Fatal Error "The host entity cannot be changed" can be falsely triggered.

The method FieldCollectionItemEntity::updateHostEntity() is looks to compare host entity ids

<?php
if ($current_id == $recieved_id) {
      $this->hostEntity = $entity;
      $delta = $this->delta();
      if (isset($entity->{$this->field_name}[$this->langcode][$delta]['entity'])) {
        $entity->{$this->field_name}[$this->langcode][$delta]['entity'] = $entity;
      }
    }
    else {
      throw new Exception('The host entity cannot be changed.');
    }
?>

At the begining of this method, it makes a call to FieldCollectionItemEntity::fetchHostDetails() which it assumes will set the hostEntityId attribute. Which it will use (for the $current_id) if the FC entity is "In use" (as checked by FieldCollectionItemEntity::isInUse(), which validates as TRUE when FieldCollectionItemEntity is instantiated).

However, FieldCollectionItemEntity::fetchHostDetails() has a logic flaw in it, that may result in the data not being set for the host Entity. The logic issue lies at the start of this method, where it will only fetch Host entity details if the hostEntityId attribute has not been set.

<?php
protected function fetchHostDetails() {
    if (!isset($this->hostEntityId)) {
?>

The problem is that, there can be cases where the object is loaded and the hostEntityId is set to FALSE and the hostEntityRevisionId is set, but the host entity details are not loaded. So the object does have a host entity, but the details n (due to hostEntityId already being set).

A fix to this problem would be to change the logic of fetchHostDetails() to the following:

<?php
  protected function fetchHostDetails() {
    if (!isset($this->hostEntityId) || (!$this->hostEntityId && $this->hostEntityRevisionId)) {
?>

This would then check that if either the hostEntityId has not been set, or the hostEntityId is false, but the hostEntityRevisionId is known, to then get the host entity details.

This will solve the edge case where the object used in updateHostEntity() has hostEntityId set to FALSE but has the the hostEntityRevisionId value set.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

mikemiles86’s picture

Here is a patch that updates the logic of fetchHostDetails to account for the edge case outlined by the issue.

mikemiles86’s picture

Renamed patch file

TomasBru’s picture

I had same issue even after applied patch. Temporarily solved an issue by adding $recieved_id = $current_id; to 354 line, before if ($current_id == $recieved_id) { statement. While debugging the error, I found that $recieved_id is less than $current_id, and that's why I was recieving Exception('The host entity cant be changed.')
Anyone more advanced can fix this?
This error appeared while removing an item from multiple-value field_collection field.

jschrab’s picture

@TomasBru I am having a similar situation with the following scenario:

  • The field is a Field Collection
  • The host node is node-level translated (3 languages, 3 nodes, EN FR DE)
  • The field is marked in the Content Type under "Synchronize translations" as field to be synchronized.

And here is the problem:

When deleting an item in a multi-value field of type Field Collection, within updateHostEntity(), entity_extract_ids() is called. The $received_id returned is NOT the nid of the containing node entity but is the node of another language node. So later on $current_id != $received_id and the exception fires.

Field Collections for synchronized fields "Synchronize translations" does not work the way I might have expected. The field is synchronized across translations with the very same entity id list as the originating node entity. What I would have expected is that field synchronization would duplicate the Field Collection item entities as part of synchronization.

I don't know where to direct this issue - is this a fault of Field Collections? Is this a fault of i18n_sync? Something in core?

I'm on 7.x-1.0-beta8 of Field Collection.

Mirroar’s picture

Thank you @jschrab, I ran into the same issue while running a migration and your comment helped me find out that synchronization of a field collection field was indeed the problem. Temporarily deactivating synchronization allowed me to run my migration.

I'm not sure if it's a good idea to create a second issue for the synchronization problem. The error message is the same, but the cause is different.
As for a possible solution: Maybe we can find a way to check whether translation synchronization is active for the current field. If so, the Node-IDs of any translations of the current node should also be accepted.
If having the same field collection item assigned to multiple nodes is a problem in general, we should probably look for a completely different solution, like cloning the field collection item when a translation is created or synchronized. hook_field_attach_prepare_translation_alter helps when a node is first translated, but I'm not sure what gets called when node translations are being synchronized.

AlxVallejo’s picture

I'm re-rolling this patch from the latest dev release. function fetchHostDetails has been moved to field_collection.entity.inc in 7.x-1.x-dev.

I'm also adding a small fix to function field_collection_field_get_entity. I came across an issue where I was trying to save a bean's field collection and the bean entity was being passed to this function. The function assumes that if an entity is passed to this function that it's already a field collection item but in my case, a bean entity was being passed. I'm just checking the entity type before returning the entity. Both of these changes are needed for me to properly save my bean's field collection items.

AlxVallejo’s picture

Status: Active » Needs review

RoSk0’s picture

+++ b/field_collection.module
@@ -1168,7 +1168,7 @@ function field_collection_remove_submit($form, &$form_state) {
+  if (isset($item['entity']) && ($item['entity']->entityType() == 'field_collection_item')) {

While this check doesn't make any trouble I'm not sure that this belongs here, moreover if you receiving a bean as an entity here it's probably because you have some flows in code outside of FC, bean probably.

webdrips’s picture

#6 did NOT work for me, but this post did: https://www.drupal.org/node/2292639

Re-rolling with the above changes and #6

I wonder if it had to do with the PHP version on the server (5.3.29) now that I think of it?

Status: Needs review » Needs work
dawehner’s picture

In general the fix seems to work pretty well, I'm using #2 now.

#6
I think opening up a dedicated issue for this problem would be a better way to fix this.

+++ b/sites/all/modules/field_collection/field_collection.entity.inc
@@ -302,13 +302,13 @@ class FieldCollectionItemEntity extends Entity {
         if ($this->isInUse()) {
-          $this->hostEntityId = $data ? key($data) : FALSE;
+          $this->hostEntityId = $data ? end(array_keys($data)) : FALSE;
           $this->hostEntityRevisionId = FALSE;
         }
         // If we are querying for revisions, we get the revision ID.
         else {
           $this->hostEntityId = FALSE;
-          $this->hostEntityRevisionId = $data ? key($data) : FALSE;
+          $this->hostEntityRevisionId = $data ? end(array_keys($data)) : FALSE;
         }

I think this change is wrong in the sense that the actual flaw is before, ... there is a missing entity type condition for the EFQ, in there.

wmnnd’s picture

I was experiencing the same problem with synchronized translations that has been described here. #6 did not work for me but the patch pointed out by #10 worked perfectly fine. I have referenced the other issue for this one.

wmnnd’s picture

Looks like I spoke to soon. The fix described in #2292639 did work - but only for when I changed something in the translated node. When I changed something in the original node, I still got the error described in this issue.
I worked around it by simply commenting out
throw new Exception('The host entity cannot be changed.');
in field_collection_entity.inc.

The problem was, that when editing the original node, $current_id ended up being the nid of the translation whereas $recieved_id was the nid of the original. Maybe one of the maintainers could explain this - or comment on why throwing the exception is even necessary. For me, everything seems to work fine without that check.

joachim’s picture

Status: Needs work » Reviewed & tested by the community

Patch #2 works for me.

I agree with @dawehner: #6 and #10 fix unrelated issues and shouldn't be in the patch here.

joachim’s picture

thehyperlink’s picture

Tested patch #16; working well here!

belaustegui’s picture

The patch #16 works nice. Thank you!

caspervoogt’s picture

I have applied this patch but for me the error occurs when modifying entity values with VBO, and this patch did not solve that for me.
I perused the code but am unsure what to change in order to fix it. Anyone else experiencing this with VBO?

adrien.felipe’s picture

Patch #16 didn't solve my problem for me either, and as @caspervoogt, it's caused while using VBO.
I have created a new issue as the cause of the problem is different, and submitted a patch for it.

#2658882: "the host entity cannot be changed" error with VBO

esolitos’s picture

Applying patch #16 (rerolled from #2) seems to be working.

chriserickson’s picture

@Mirroar, this is a bit of a hail mary. @jschrab was a colleague of mine, and I have inherited this issue (his specific ask was here).

I'm trying to figure out how to manually delete a couple fields in the field collection, but getting the aforementioned Exception.

I'm a complete Drupal idiot, so any help or guidance would be appreciated. I suspect I can delete a row in a few tables to remove these by hand, but I can't figure them out. On a dev instance, I tried deleting rows from two tables named `field_data_field_my_collection` and `field_data_field_my_collection` for as best I can tell is the current revision.

-- EDIT --
Disabling field synchronization allows me to delete them properly. I have to do all three languages one at a time in the admin, then I re-enable synchronization for that field. Have I created a time-bomb that I'll pay for later? Is there a fix for this behavior I can apply?

mqanneh’s picture

This patch should work with both latest release 7.x-1.0-beta11 and the dev one, it also fix the warnings for php variables should be passed by reference in field_collection.entity.inc line 305.

RoSk0’s picture

+1 to #16 and RTBC.
#23 as stated earlier some of the fixes in your patch is not relevant to current issue.

ktogias’s picture

I have also found an other logic issue in fetchHostDetails(). When In a multilingual site there exist entity translations, the execution of the EntityFieldQuery at line 301 brings multiple entries (nodes) one for each of the translations of the entity. The line
$this->hostEntityId = $data ? key($data) : FALSE
selects the first one of the returned nodes instead of the one that matches the node currently being edited. This results in current_id not being equal to received_id and the Exception('The host entity cannot be changed.') to be thrown.

I have circumvented this by passing $entity->nid as an argument to fetchHostDetails() and adding the following code after line 302

if (count($data)>1 && $nid && array_key_exists($nid, $data)){
      $data = array_intersect_key($data, array($nid => $nid));
}

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 25: field_collection.entity.inc_.ktogias-2016-03-28.patch, failed testing.

ashedryden’s picture

#16 applies cleanly but does not resolve issues for me

bowersox’s picture

We solved the error by simply commenting out the throw new Exception which allowed our content authors to go on making new revisions.

The new revision of the node seems to work correctly from then on. $current_id and $recieved_id values match in the new revision.

Can someone explain why it's important to throw an Exception which interrupts saving the node?

Slown’s picture

Hi All,

I commented on the indicated line like bowersox #28, but is this really the right solution? Anyway it works.

Marty2081’s picture

@ktogias: shouldn't you set $nid to a default value to prevent notices and warnings when the method is called without the $nid parameter?

Something like: proctected function fetchHostDetails($nid = FALSE) {}?

maestro888’s picture

Synchronize translations in field is enabled

Exception: The host entity cannot be changed. in FieldCollectionItemEntity->updateHostEntity()
(line 239 of /sites/all/modules/field_collection/field_collection.entity.inc).

scuba_fly’s picture

Just some info:
Downgrading to beta8 seams to solve this issue.
Downgrading to beta10 does fix this for most nodes. But I found some cases where this still did not work.

I have not tried any other versions.

maestro888’s picture

scuba_fly, thank you very much
I downgranding to beta8 and fixed this problem

shi99’s picture

I also downgraded to beta8 and the issue is resolved for me too.
Thanks

vistree’s picture

I want to create a rule to automatically create translations for a saved node. I recieve an equal message when I save my nodes:
Exception: The host entity cannot be changed. in FieldCollectionItemEntity->updateHostEntity() (line 239 of /var/www/domain.com/docroot/sites/all/modules/field_collection/field_collection.entity.inc).

I am not sure if this is the same problem described by other users in this issue - because non of the proposed solutions work for me.

The code I use to create the translations:

function auto_create_node_translations_action_node_translate($node) {
    // If this is not a source node, don't do automatic translations.
    if ($node->tnid == 0 or $node->tnid == $node->nid) {
        // Create a translation of the node for each enabled language
        // other than the source node language.
        $languages = i18n_language_list();
        unset($languages[$node->language]);

        foreach ($languages as $langcode => $language) {
            // Prepare the new node.
            $new_node = clone $node;

            $new_node->language = $langcode;
            $new_node->tnid = $node->nid;
            $new_node->nid = null;
            $new_node->vid = null;
            $new_node->path = null;
            $new_node->created = null;

            // Create the new translation node.
            node_save(node_submit($new_node));
        }

        // Update tnid value of the source node to create a translation set.
        if (count($languages) > 0) {
            db_update('node')->fields(array(
                'tnid' => $node->nid,
            ))
                ->condition('nid', $node->nid, '=')
                ->execute();
        }

        return array('node' => $node);
    }
}

Am I creating the nodes wrong (in respect of field collections)??? Or do I run in the same problem described by the other users in this thread?

RoSk0’s picture

@vistree: Yes you are doing it wrong. When creating a new node for translation you also need to create clones of all your field collections and place them in same places as originals. There is no need to save them before saving host node.

vistree’s picture

Hi RoSk0 - thanx for your reply. After searching on how to clone the fc I found a comment, that this is not neccessary.
After adding

                $new_node->is_new = TRUE;

to my code, the field collections are cloned automatically and the error has gone!!

Gomez_in_the_South’s picture

Status: Needs work » Needs review

There have been 4 posters since the patch in #16 that confirm the patch fixes their problem (5 including me!). Does this qualify as "Reviewed and tested by the community" so that we can get #16 committed? Does the maintainer need additional information from us?

A couple of those for whom it didn't help were also using V.B.O., and others fixed the problem by downgrading to beta-8, which isn't a great solution for us all going forward.

Gomez_in_the_South’s picture

Plus the 3 previous posters who acknowledged that #2 works for them, on which #16 is based.

jmuzz’s picture

Status: Needs review » Needs work

A few have asked why the exception is necessary. Field collection items are meant to behave as if they are field data in their host entity. Though they are separate entities themselves, it's more of an implementation detail and field collection fields aren't supposed to act the same as entity references. A field collection item shouldn't be able to change hosts or have multiple hosts. Originally updateHostEntity() didn't even exist, but it was added as part of support for cloning entities and translating them, so that's why all the checks to make sure it's actually the same host even if the host is being "updated." If the check is removed and a field collection item gets saved under multiple hosts then it would no longer be possible to edit the field data in one host without changing the data in the other.

Support for host entity cloning and content translation was added in beta 8. If you are using content translation it should be creating new clones of the field collection items whenever a new translation of their host gets created which should be assigned to the new translation.

I saw some of you are using beta 8 and experiencing this problem while using content translation. Is there any chance that these translations were created prior to upgrading to field collection beta 8? I'm wondering if some translations of nodes were created referencing the same field collection items then and that's what's causing the error now.

About the proposed solution:

the object is loaded and the hostEntityId is set to FALSE and the hostEntityRevisionId is set, but the host entity details are not loaded.

updateHostEntity should be able to work with a hostEntityRevisionId too. It's not really an edge case. Usually hostEntityId or hostEntityRevisionId will be set, but not both, depending on whether the version of the host being operated on is the default revision or not. It may not be when creating a draft version in workbench moderation, for example.

fetchHostDetails, in theory, shouldn't produce a different result than the current hostEntityId / hostEntityRevisionId is already set. That's why that check will skip to the end if it doesn't pass. The fact that it does seem to need to get run again may be an edge case, but I don't think this solves the underlying problem if that can happen. What I would like to know is when is hostEntityRevisionId getting set and why would it not be sufficient when updateHostEntity gets called.

errev’s picture

Had the same problems.
But after installing last dev version of module fc, it has gone.
But now after deleting some field collection date having the next message

•Notice: Undefined index: entity keys in entity_extract_ids() (line 7874 of /domain.com/includes/common.inc).
•Notice: Undefined index: entity keys in entity_extract_ids() (line 7875 of /domain.com/includes/common.inc).

How to fix it? Any ideas?

riddhi.addweb’s picture

Issue tags: +Field collection
jmuzz’s picture

@Jigar.tanna please stop tagging issues with "Field collection". As it says under the issue tag input you shouldn't use tags to duplicate other fields such as the Project field. We already know that issues in the field collection issue queue are about field collection.

moonray’s picture

The patch in #23 gives me the following warning:

Warning: array_keys() expects parameter 1 to be array, null given in FieldCollectionItemEntity->fetchHostDetails() (line 316 of /Users/bala/Sites/memorialcare/docroot/sites/all/modules/contrib/field_collection/field_collection.entity.inc).

However, the node now saves correctly.

moonray’s picture

Attached patch accounts for no results found by the entity query. It's based on #23.
With this tweak (which gets rid of the warning, but doesn't change the rest of the patch), the original error is removed and the node saved on changing a field_collection field.

dineshw’s picture

Hello @moonray the Patch from #47 works for me and it basically taking care of combining fixes proposed in both #16 and #23

This works fine!

slhemanthkumar’s picture

Hi @Moonray the patch #47 works for me. Thanks once again.

dineshw’s picture

Status: Needs review » Reviewed & tested by the community

Thanks Hemanth for confirmation

ollioi’s picture

Patch #47 also works for me, thanks. In my case the problem arose, when I wanted to save a node, which was a translation of another node and the field collection items had been synchronized with the original node.

  • moonray authored c1f704f on 7.x-1.x
    Issue #2382089 by moonray, ktogias: Fixed a logic issue with...
jmuzz’s picture

Issue summary: View changes
Status: Reviewed & tested by the community » Fixed

Thanks for the patch guys.

I agree the changes may not cleanly cover just the described issue. If they cause any problems please open a followup issue.

Hopefully this will help some of those who encounter this error during the updates.

@dawehner (#12) I set up some simple EFQ examples and from what I saw it returns the same whether the entity type is specified or not. That part is how the item discovers what entity type its host is so specifying the type there would not be possible, though if it is causing problems it could be changed to another kind of query.

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.

giupenni’s picture

I've updated to the last dev but the issue persist:
Exception: The host entity cannot be changed. in FieldCollectionItemEntity->updateHostEntity() (linea 250

caspervoogt’s picture

The patch does not solve this for all cases. See https://www.drupal.org/node/2833061, posted by giupenni.

llribas’s picture

same problem here, not solved with beta12 nor dev version beta12+4dev.

Exception: The host entity cannot be changed. en FieldCollectionItemEntity->updateHostEntity() (línea 250 de /var/www/generalgalerias/drupal/sites/all/modules/field_collection/field_collection.entity.inc).

othmen’s picture

Patch #47 also works for me, thanks @moonray .

othmen’s picture

Based on #47, Simple changes to be applyed for 7.x-1.0-beta11 version.

shenzhuxi’s picture

+++ b/field_collection.module
@@ -1168,7 +1168,7 @@ function field_collection_remove_submit($form, &$form_state) {
+  if (isset($item['entity']) && ($item['entity']->entityType() == 'field_collection_item')) {

entityType is protected in class entity. It cause Error in drafty.field_collection.test https://www.drupal.org/node/2730265.

Error: Call to undefined method stdClass::entityType() in field_collection_field_get_entity() (line 1433 of /Users/lshen/Documents/drupal7/sites/all/modules/contrib/field_collection/field_collection.module).

davidsickmiller’s picture

Patch #59 gives me this error:

Notice: Undefined variable: data in FieldCollectionItemEntity->fetchHostDetails() (line 318 of //sites/all/modules/patched/field_collection/field_collection.entity.inc).

Looking at the code, I think this problem in patch #59 was not in patch #47.

pianomansam’s picture

jdleonard’s picture

For anyone encountering this exception when editing an unpublished node using the Revisioning module, I found my solution in #27 of
#2658882: "the host entity cannot be changed" error with VBO

svenryen’s picture

I had this problem on a site, and the patch in #59 fixed my problem.

btopro’s picture

#59 but relative to directory the patch would live in

propagganda’s picture

#59 fixed my problem. Thanks!

MsNingrum’s picture

After applying patch #59, I am experiencing similar issue as mentioned on comment #61.

jaesperanza’s picture

Patch #47 works, thanks, moonray! Multisite, last and latest D7 versions, and latest field collection to date.

simgui8’s picture

Patch #47 works for me.
Drupal 7.56
Field collection 7.x-1.0-beta12

Thanks moonray and everyone

CHiLi.HH’s picture

Stumbled upon the same problem with an old D7 installation today. Patch in #65 solved the issue for me (current module version / 1.0-beta12 @ Drupal 7.59).