user_save

5 user.module user_save($account, $array = array(), $category = 'account')
6 user.module user_save($account, $array = array(), $category = 'account')
7 user.module user_save($account, $edit = array(), $category = 'account')
8 user.module user_save($account, $edit = array())

Save changes to a user account or add a new user.

@todo D8: Drop $edit and fix user_save() to be consistent with others.

Parameters

$account: (optional) The user object to modify or add. If you want to modify an existing user account, you will need to ensure that (a) $account is an object, and (b) you have set $account->uid to the numeric user ID of the user account you wish to modify. If you want to create a new user account, you can set $account->is_new to TRUE or omit the $account->uid field.

$edit: An array of fields and values to save. For example array('name' => 'My name'). Key / value pairs added to the $edit['data'] will be serialized and saved in the {users.data} column.

$category: (optional) The category for storing profile information in.

Return value

A fully-loaded $user object upon successful save or FALSE if the save failed.

22 functions call user_save()

File

modules/user/user.module, line 420
Enables the user registration and login system.

Code

<?php
function user_save($account, $edit = array(), $category = 'account') {
  $transaction = db_transaction();
  try {
    if (!empty($edit['pass'])) {
      // Allow alternate password hashing schemes.
      require_once DRUPAL_ROOT . '/' . variable_get('password_inc', 'includes/password.inc');
      $edit['pass'] = user_hash_password(trim($edit['pass']));
      // Abort if the hashing failed and returned FALSE.
      if (!$edit['pass']) {
        return FALSE;
      }
    }
    else {
      // Avoid overwriting an existing password with a blank password.
      unset($edit['pass']);
    }
    if (isset($edit['mail'])) {
      $edit['mail'] = trim($edit['mail']);
    }

    // Load the stored entity, if any.
    if (!empty($account->uid) && !isset($account->original)) {
      $account->original = entity_load_unchanged('user', $account->uid);
    }

    if (empty($account)) {
      $account = new stdClass();
    }
    if (!isset($account->is_new)) {
      $account->is_new = empty($account->uid);
    }
    // Prepopulate $edit['data'] with the current value of $account->data.
    // Modules can add to or remove from this array in hook_user_presave().
    if (!empty($account->data)) {
      $edit['data'] = !empty($edit['data']) ? array_merge($account->data, $edit['data']) : $account->data;
    }

    // Invoke hook_user_presave() for all modules.
    user_module_invoke('presave', $edit, $account, $category);

    // Invoke presave operations of Field Attach API and Entity API. Those APIs
    // require a fully-fledged and updated entity object. Therefore, we need to
    // copy any new property values of $edit into it.
    foreach ($edit as $key => $value) {
      $account->$key = $value;
    }
    field_attach_presave('user', $account);
    module_invoke_all('entity_presave', $account, 'user');

    if (is_object($account) && !$account->is_new) {
      // Process picture uploads.
      if (!empty($account->picture->fid) && (!isset($account->original->picture->fid) || $account->picture->fid != $account->original->picture->fid)) {
        $picture = $account->picture;
        // If the picture is a temporary file move it to its final location and
        // make it permanent.
        if (!$picture->status) {
          $info = image_get_info($picture->uri);
          $picture_directory =  file_default_scheme() . '://' . variable_get('user_picture_path', 'pictures');

          // Prepare the pictures directory.
          file_prepare_directory($picture_directory, FILE_CREATE_DIRECTORY);
          $destination = file_stream_wrapper_uri_normalize($picture_directory . '/picture-' . $account->uid . '-' . REQUEST_TIME . '.' . $info['extension']);

          // Move the temporary file into the final location.
          if ($picture = file_move($picture, $destination, FILE_EXISTS_RENAME)) {
            $picture->status = FILE_STATUS_PERMANENT;
            $account->picture = file_save($picture);
            file_usage_add($picture, 'user', 'user', $account->uid);
          }
        }
        // Delete the previous picture if it was deleted or replaced.
        if (!empty($account->original->picture->fid)) {
          file_usage_delete($account->original->picture, 'user', 'user', $account->uid);
          file_delete($account->original->picture);
        }
      }
      $account->picture = empty($account->picture->fid) ? 0 : $account->picture->fid;

      // Do not allow 'uid' to be changed.
      $account->uid = $account->original->uid;
      // Save changes to the user table.
      $success = drupal_write_record('users', $account, 'uid');
      if ($success === FALSE) {
        // The query failed - better to abort the save than risk further
        // data loss.
        return FALSE;
      }

      // Reload user roles if provided.
      if ($account->roles != $account->original->roles) {
        db_delete('users_roles')
          ->condition('uid', $account->uid)
          ->execute();

        $query = db_insert('users_roles')->fields(array('uid', 'rid'));
        foreach (array_keys($account->roles) as $rid) {
          if (!in_array($rid, array(DRUPAL_ANONYMOUS_RID, DRUPAL_AUTHENTICATED_RID))) {
            $query->values(array(
              'uid' => $account->uid, 
              'rid' => $rid,
            ));
          }
        }
        $query->execute();
      }

      // Delete a blocked user's sessions to kick them if they are online.
      if ($account->original->status != $account->status && $account->status == 0) {
        drupal_session_destroy_uid($account->uid);
      }

      // If the password changed, delete all open sessions and recreate
      // the current one.
      if ($account->pass != $account->original->pass) {
        drupal_session_destroy_uid($account->uid);
        if ($account->uid == $GLOBALS['user']->uid) {
          drupal_session_regenerate();
        }
      }

      // Save Field data.
      field_attach_update('user', $account);

      // Send emails after we have the new user object.
      if ($account->status != $account->original->status) {
        // The user's status is changing; conditionally send notification email.
        $op = $account->status == 1 ? 'status_activated' : 'status_blocked';
        _user_mail_notify($op, $account);
      }

      // Update $edit with any interim changes to $account.
      foreach ($account as $key => $value) {
        if (!property_exists($account->original, $key) || $value !== $account->original->$key) {
          $edit[$key] = $value;
        }
      }
      user_module_invoke('update', $edit, $account, $category);
      module_invoke_all('entity_update', $account, 'user');
    }
    else {
      // Allow 'uid' to be set by the caller. There is no danger of writing an
      // existing user as drupal_write_record will do an INSERT.
      if (empty($account->uid)) {
        $account->uid = db_next_id(db_query('SELECT MAX(uid) FROM {users}')->fetchField());
      }
      // Allow 'created' to be set by the caller.
      if (!isset($account->created)) {
        $account->created = REQUEST_TIME;
      }
      $success = drupal_write_record('users', $account);
      if ($success === FALSE) {
        // On a failed INSERT some other existing user's uid may be returned.
        // We must abort to avoid overwriting their account.
        return FALSE;
      }

      // Make sure $account is properly initialized.
      $account->roles[DRUPAL_AUTHENTICATED_RID] = 'authenticated user';

      field_attach_insert('user', $account);
      $edit = (array) $account;
      user_module_invoke('insert', $edit, $account, $category);
      module_invoke_all('entity_insert', $account, 'user');

      // Save user roles.
      if (count($account->roles) > 1) {
        $query = db_insert('users_roles')->fields(array('uid', 'rid'));
        foreach (array_keys($account->roles) as $rid) {
          if (!in_array($rid, array(DRUPAL_ANONYMOUS_RID, DRUPAL_AUTHENTICATED_RID))) {
            $query->values(array(
              'uid' => $account->uid, 
              'rid' => $rid,
            ));
          }
        }
        $query->execute();
      }
    }
    // Clear internal properties.
    unset($account->is_new);
    unset($account->original);
    // Clear the static loading cache.
    entity_get_controller('user')->resetCache(array($account->uid));

    return $account;
  }
  catch (Exception $e) {
    $transaction->rollback();
    watchdog_exception('user', $e);
    throw $e;
  }
}
?>

Comments

Problem from D6 still exists!

This (what I consider a bug) bit me again in D7!

When using user_save() to save profile data you cannot simply pass in the edit array for the one field. If you do, it will remove all the data for the other fields in the same category.

The only way I have found to solve this is to load up the profile fields from the category, edit the single field that I need to edit, and then call user_save.

Here is an example of dynamically getting the fields in a given category. This way you can add fields to the user and not have to worry about adding the field to your code.

private function CategoryProfileInformation($category)
  {
    /* due to the horrid way user_save is written, we need to keep all the fields that are already set */
    $result = _profile_get_fields($category);
    while ($field = $result->fetchObject()) {
      $saved_profile_arr[$field->name] = $this->drupalUser->{$field->name};
    }
   
    return $saved_profile_arr;
  }

changing just one field

Could you explain a bit more?
If one first loads a user object with "$user = user_load(3)" then $user contains everything about uid 3, including its profile fields (e.g. $user->profile_myfield).
I dont see categories listed though.

What you mean is one cannot just do what one one expect?
$user->profile_myfield=10; // new value
user_save($user);

So, one can show the properties of a profile field by:
$result = _profile_get_fields('Mycategory');
while ($field = $result->fetchObject()) {
if ($field->name=="profile_myfield") {
drush_print_r($field);
}
}
But I dont see how this can be used to create an edit array?

And would the edit a array basically not just need to be an entire user object?
i.e. can one not just do:
user_save($user, $user); // re-save all fields in $user to $user->uid

Completely agree

I can't think of any reason we shouldn't be able to use user_load() to retrieve and object - alter one item - then use user_save().

And having to fill the whole array with data again makes it even more time consuming.

Will check on D8 to see what's happening there.

Saving fields in user account

To save fields data in user account we can act this way:

$account = user_load($uid); // Loading account
$edit = array(
  'field_some_custom_field' => array(
    'und' => array(
      0 => array(
        'value' => $new_value;
      ),
    ),
  ),
);
user_save($account, $edit);

Small typo in the above

Small typo in the above snippet. It should be:

'value' => $new_value

Big typo

Depends on Field type.

Some types want other than standard "value" key name:

Link - "url"
Email - "email"

Thank you

I've been looking everywhere for the correct format for a custom field. Thank you so much.

What is the point in using $edit?

If you load the complete user object with user_load like above, you can just say:

$account = user_load($uid); // Loading account
$account->field_some_custom_field['und'][0]['value'] = $new_value;
user_save($account);

This seems much easier and straightforward to me.

How do I correctly pass the roles array?

I am trying to create a new user using the user_save function. All the fields are populated correctly except for the role. I have included my code snipped below. Is the roles array defined correctly?

$edit = array(
'name' => $cdid,
'pass' => user_password(),
'mail' => $email,
'init' => $email,
'field_firstname' => array('und' => array('0' => array('value' => $givenName))),
'field_lastname' => array('und' => array('0' => array('value' => $surName))),
'field_city' => array('und' => array('0' => array('value' => $city))),
'field_country' => array('und' => array('0' => array('value' => $country))),
'status' => 1,
'roles' => array(DRUPAL_AUTHENTICATED_USER),
'access' => time(),
);
$account = user_save(drupal_anonymous_user(), $userinfo);

Incorrect constant

It's DRUPAL_AUTHENTICATED_RID not DRUPAL_AUTHENTICATED_USER. And the correct way to define roles:

<?php
 
'roles' => array(DRUPAL_AUTHENTICATED_RID => TRUE),
?>

Getting newly saved user id

I was looking and looking to find out how to add a new user to an organic group via a custom form.
Here is what I found out.

$new_user = array(
      'name' => $form_state['values']['name'],
      'pass' => user_password(),
      'mail' => $form_state['values']['mail'],
      'init' => $form_state['values']['mail'],
      'field_first_name' => array(LANGUAGE_NONE => array(array('value' => $form_state['values']['field_first_name']))),
      'field_last_name' => array(LANGUAGE_NONE => array(array('value' => $form_state['values']['field_last_name']))),
      'field_phone_number' => array(LANGUAGE_NONE => array(array('value' => $form_state['values']['field_phone_number']))),
      'status' => 1,
      'access' => REQUEST_TIME,
      'roles' => $roles,
);

// $account returns user object
$account = user_save(null, $new_user);

// $group is organic group node I want to add the brand new user to
$group = og_get_group('node', $node->nid);

// This adds user to the group
og_group($group->gid, array('entity' => $account));

Hope this helps some folks along with adding a new user to a group!

Saving a new profile picture

Worth noting:

Even though the user object that is returned from user_load() (and by this function) stores an integer value in $user->picture (being the file's fid), that is not the correct data type to use if you wish to update the profile picture using this function. If you try to do so, somehow this function silently fails, and none of your other attempted edits will be saved either (still not sure why there isn't instead an error somewhere).

The correct way to update the profile picture is to pass its associated file object in the $edit array, not its integer fid.

user_save cannot be used to edit the anonymous user

The anonymous user in Drupal has a uid=0.

When execution reaches this line:

  if (!empty($account->uid)  && !isset($account->original)) {

The call to empty returns true because of the zero value, and user_save decides it is a new user. This will fail because the new user has a duplicate username.

So if you need to edit all users (I am setting a default value for a custom field), you need to strip uid=0 from the list and deal with it as a special case.

Login or register to post comments