Hello,
I use hook_user in my module ("roles_by_profile") to modify account data when a user account gets updated:

function role_by_profile_user($op, &$edit, &$account, $category = NULL)
{
	if ($op == 'update')
	{
		...
	}
}

Within the ($op == 'update') condition I modify the $account object. However, these modifications are not saved to the database, although they should be – after all, $account is passed as a reference. What exactly is the problem here?

Comments

nevets’s picture

The case op == 'submit' happens before the user data is inserted/updated and sounds like what you want. The insert and update ops happen after the user data is saved and allow you to save additional data (typically in other tables).

Dio-1’s picture

Even with op == submit, changes to $account aren't saved. Is this is a bug?

I now have my module write directly to the roles table, which of course isn't as elegant and possibly incompatible with later versions of Drupal.

timothyf’s picture

$op == submit is only called when you are submitting the user edit form at user/[uid]/edit, from what I can tell.
$op == update is called before the database is updated, but from what I can tell, only changes to $edit are saved into the user record.
$op == insert is called after the user is inserted into the database, but user_save does an update afterwards. Again, only changes to $edit are saved into the user record.

See http://api.drupal.org/api/5/function/user_save and pay close attention to where user_module_invoke() is called to get an idea of what's going on.

thomie’s picture

but you should use the $edit object in hook_user for that. The info at hook_user is a bit unclear about this. It says: "submit": Modify the account before it gets saved.

What actually happens is this (Drupal 5):
1. You push submit from user edit form, the function user_edit_submit is called.
2. user_edit_submit() calls user_module_invoke('submit', $form_values, $account, $category);
3. user_edit_submit() calls user_save($account, $form_values, $category);
4. user_save() calls user_module_invoke('update', $form_values, $account, $category);
5. user_save() processes the $form_values (this is $edit in hook_user).

You jump in at step 2 (or 4, then you should use case 'update', I don't think there is any difference really), with your hook_user. You can add fields to the $edit object, or change them. In my hook_user this is what I do:
case 'submit':
$edit['fullname'] = $account->profile_first_name .' '. $account->profile_last_name;
break;

The function user_save figures out what it is that you added to the $edit object. If it is a standard field like [name] or [email], it is stored directly. If it's a role, the users_role table is update. If it doesn't know the field yet, it will add it (after serialization) to the [data] field in the users table. In that last case, the nice thing is that this data is automatically deserialized and added to the user object every time the user is loaded. So for example I'm able to acces a users fullname with $user->fullname always.

Note that although $account is passed by reference, user_save() doesn't actually do anything with the changes you do to it.

Another way of saving changes after an update from the user edit form is to store them by some database update as suggested in the hook_user, case update, documentation. But, as someone else mentioned on this page, this is only needed when you are working with data in a different table then the users or users_roles table.

Hope this helps anyone!

glennnz’s picture

I can't get this to work either.

My 'login' and 'insert' cases work fine, but neither 'update' or 'submit' will work.

Extract from code:


    // .......the rest of my hook_user code is here, and works fine......

    case 'login': case 'insert': case 'update':
      check_assessor_status($account);
    break;
  }
}

function check_assessor_status($account) {
  $rid = db_result(db_query("SELECT rid FROM {role} WHERE name = 'Member'"));
  if ($rid) {
    db_query("DELETE FROM {users_roles} WHERE uid = %d && rid = %d", $account->uid, $rid);
    db_query("INSERT INTO {users_roles} (uid, rid) values (%d, %d)", $account->uid, $rid);
  }
}

This code doesn't really do anything, it's just test code to tray and get this working.

Thanks

Glenn

Glenn
THECA Group

nevets’s picture

You code appears to fail because the user module updates the roles after user_hook() is called for either insert or update calls.

Dio-1’s picture

If it is a Drupal bug, where can I report/file it?

Gilbert Pelletier’s picture

You say your module name is roles_by_profile (with an s in roles), but your function is named role_by_profile_user without an s in the word role.