0

I'm trying to add a field collection to my user account on an event, and it seems to be being created properly, the fields are updated as expected and it parents it to the user account, however, even though the field collection is setup to have unlimited possible values, it only seems to keep the last value, it seems to keep overwriting the old one whenever a new field collection is added.

My code is below

global $user;        

$field_collection_item = entity_create('field_collection_item', array('field_name' => 'field_downloads'));
$field_collection_item->setHostEntity('user', $user);
$field_collection_item->field_subqueue[LANGUAGE_NONE][] = array('target_id' => $subqueue->subqueue_id);
$field_collection_item->field_file[LANGUAGE_NONE][] = array('fid' => $file->fid, 'display' => 1, 'description' => $filename);
$field_collection_item->save(TRUE);
field_attach_update('user', $user);

As mentioned, it appears to be creating the field collection properly, I can check in my account and it saves the file and correctly sets the entity reference for the field_subqueue field too, but it only seems to save 1, like I say, the field is definitely set to allow unlimited values, it even shows me the button to 'add another item' if I check it in the user account directly.

Is there something I'm doing wrong?

EDIT

I really need to try to get this fixed so I'm gonna open it up to a bounty.

The full context of this code is that it's on the callback of an AJAX form that I've written, everything about the code seems to happen correctly, the form submission happens correctly, it seems to save the field collection, it just seems to overwrite the previous entry each time. It isn't running as part of a loop or anything, each time the form is submitted, it should save it using the field_attach_update line

/**
 * Submit handler for saving a new deck
 */
function my_module_form_submit($form, &$form_state){
    global $user;

    // Load the PHP library
    libraries_load('my_library');

    // Prepare an ajax command to insert the node html into our ajax wrapper.
    $commands = array();
    $commands['effect'] = 'fade';

    if (form_get_errors() !== null) {
        $message = t('You have an error');
        $commands[] = ajax_command_invoke(NULL, array('firePopup'), array(json_encode($message), 'error', 3500)); // This is just a custom Javascript command I have called firePopup
    }
    else {
        $values = $form_state['values'];
        $values['my_var'] = json_decode($form_state['input']['_my_vars']);
        $filename = empty($values['title']) ? date("F j Y - g-ia") : $values['title'];

        // Custom function here to generate a 'subqueue' from the module 'node_queue', returns a $subqueue object

        // Add the downloaded file to the field collection for the user
        $field_collection_item = entity_create('field_collection_item', array('field_name' => 'field_downloads'));
        $field_collection_item->setHostEntity('user', $user);
        $field_collection_item->field_subqueue[LANGUAGE_NONE][] = array('target_id' => $subqueue->subqueue_id);
        $field_collection_item->field_file[LANGUAGE_NONE][] = array('fid' => $pptx->fid, 'display' => 1, 'description' => $filename);
        $field_collection_item->save(TRUE);
        field_attach_update('user', $user);

        // Set the response message
        $message = t('Your download is available.');
        $download_link = l(t('Download'), file_create_url($field_collection_item->field_file[LANGUAGE_NONE][0]['uri']), array(
            'attributes' => array(
                'class' => array(
                    'button-type', 
                    'download'
                ), 
            'download' => file_create_url($field_collection_item->field_file[LANGUAGE_NONE][0]['uri']))));

        $commands[] = ajax_command_invoke(NULL, array('refreshView'), array('downloads'));
        $commands[] = ajax_command_invoke(NULL, array('firePopup'), array(json_encode($message), 'status', 0, $download_link));
    }

    // Render the commands into JSON and print them.
    return array('#type' => 'ajax', '#commands' => $commands);
}

If there's any variables here which look like they come out of nowhere, don't worry about that too much, it's only because I've removed some of the sub-functions and it's probably more confusing to add multiple sub-functions in too etc.

The code all appears to work fine, no errors, it just seems to overwrite the field collection each time.

3 Answers 3

1

The $user entity needs explicit handling (clearing the cache as well as calling attach_presave), when using field_attach_update(). You could try this:

entity_get_controller('user')->resetCache([$user->uid]);
field_attach_presave('user', $user);
field_attach_update('user', $user);

If this doesn't do it for you, you can go a step less efficient and call user_save() after attaching the new field_collection manually, e.g.:

entity_get_controller('user')->resetCache([$user->uid]);
$user->field_downloads[LANGUAGE_NONE][] = array(
  'value' => entity_id('field_collection_item', $field_collection_item),
);
user_save($user);

Update:

This needs some more debugging, one way to work with it would be to break it down and see what works. I would start with such a function:

function _debug_fci_additions($uid) {
  $user = user_load($uid);
  for ($i = 0; $i < 2; $i++) {
    $field_collection_item = entity_create('field_collection_item', array('field_name' => 'field_downloads'));
    $field_collection_item->setHostEntity('user', $user);
    $field_collection_item->save(TRUE);
  }
  user_save($user);
}

Once this function is in place, you could call it using drush, e.g.:

drush ev "_debug_fci_additions(1234);"

and then check is the field collections were added.

Update 2

Reading through the FieldCollectionItemEntity::save() comment I see this clear suggestion:

For creating an item a host entity has to be specified via setHostEntity() before this function is invoked. For the link between the entities to be fully established, the host entity object has to be updated to include a reference on this field collection item during saving. So do not skip saving the host for creating items.

@param $skip_host_save (internal) If TRUE is passed, the host entity is not saved automatically and therefore no link is created between the host and the item or revision updates might be skipped. Use with care.

So, I would suggest trying your code using:

    $field_collection_item->save();

instead of

    $field_collection_item->save(TRUE);
    field_attach_update('user', $user);

Good luck!

12
  • Was going to suggest this.
    – Kevin
    Dec 17, 2018 at 14:25
  • I tried your first comment before you edited it, that didn't work. Will try your second version now. Dec 17, 2018 at 14:25
  • What I have noticed in trying, is that my code (just about to test your second update) isn't just only saving 1, it's actually overwriting the existing value. So I manually tried to add multiple values directly through the UI which worked (proving it does seem to accept multiple entries) but then when I went back into my custom form callback, it overwrite all the entries and and showed the most recent one Dec 17, 2018 at 14:28
  • Nope, second amend doesn't work either :( Still same issue, it's actually overwriting the existing entries Dec 17, 2018 at 14:29
  • Looks like that one worked, it created 2 blank entries on my user account (the drush ev code) Dec 17, 2018 at 16:00
0

From my understanding you create the same entity each time. This overwrites the previous entity holding in its place.

$field_collection_item = entity_create('field_collection_item', array('field_name' => 'field_downloads'));

try adding a count feature like this:

$field_collection_item = entity_create('field_collection_item', array('field_name' => 'field_downloads[1]'));

replace 1 with a variable of your choice that will count++ every time you save a new one.

1
  • It's not that, I'm not creating the field collections in a loop, each time this function is called where I create a new field collection, the user entity is saved until it is called again. I've used almost the exact same code in a batch process elsewhere on my site, but that works fine, that's why I'm so confused Dec 15, 2018 at 20:45
0

After almost bringing myself to tears trying to figure this one out, I figured out what the problem was

calling global $user doesn't get the full user object, and it doesn't bring back the additional fields you've added in to it, so every time I tried running my code, setting the new property and saving it, I was effectively writing it as a new property in my $user object and thus it was overwriting the existing value, because it never loaded the original values in the first place.

The resolution was just to add $account = user_load($user->uid); after the global $user; line, and from then on do everything to the $account object instead.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.