function user_save

You are here

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

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.

23 calls to user_save()
BlockCacheTestCase::setUp in modules/block/block.test
Sets up a Drupal site for running functional and integration tests.
ContactPersonalTestCase::testPersonalContactAccess in modules/contact/contact.test
Tests access to the personal contact form.
DrupalWebTestCase::drupalCreateUser in modules/simpletest/drupal_web_test_case.php
Create a user with a given set of permissions.
EntityCrudHookTestCase::testUserHooks in modules/simpletest/tests/entity_crud_hook_test.test
Tests hook invocations for CRUD operations on users.
FileFieldRevisionTestCase::testRevisions in modules/file/tests/file.test
Tests creating multiple revisions of a node and managing attached files.

... See full list

File

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

Code

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);
        }
      }
      elseif (isset($edit['picture_delete']) && $edit['picture_delete']) {
        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

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);

Depends on Field type.

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

Link - "url"
Email - "email"

'tid' => $new_value,

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!

Wow, this was exactly what I was wanting to do.
I had to change the custom form to submitted node values but still, the whole process is there, well explained
I will not set 'access' though because it confuses admin since it says user has logged in whereas (s)he have not.
Anyway, many thanks you made me save a lot of time.

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.

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.

User data is going away in Drupal 8 and user data is difficult to list. Move your data into fields. You can then list users that have your data and you can list their values.

Each extra field will slow down your database a little bit. If you have several related items, you might want to group them the way the address field module groups address fields in one row.

When you save a user, other modules have their user hooks called to modify the user. If you get weird results, such as missing fields, it can be another module responding to a hook and changing stuff.

Your save might be followed by another module changing the same field. Another module might respond as if you are editing the user in the admin page. A good test is to visit the user in the admin edit page, note all the values, run your save, then visit the user in the admin edit page and look for unexpected changes.

In Drupal 8, this function has been replaced from Entity::save().

The example user_save($user, array("key" => "value")); is wrong! Instead do something like this

<?php
global $user;
$user_fields = user_load($user->uid);
$myfield = $user_fields->field_myfield;
$myfield['und'][0]['value'] = "This value was set programatically";
user_save($user,"field_myfield"=>$myfield);
?>

When searching for examples of updating existing users programmatically you find many variations in code and in most cases they all work.

The reason for this is that $edit (should contain changes only) is copied into $account by user_save().
As a result if you assign a changed field to account instead of to the $edit the result will be the same

so:

  $account = user_load($uid);

  $edit = array();
  $edit['field_first_name'][LANGUAGE_NONE][0]['value'] = 'Guy';
  $edit['field_last_name'][LANGUAGE_NONE][0]['value'] = 'Schneerson';

  user_save($account, $edit);

and

  $account = user_load($uid);
  $account->field_first_name[LANGUAGE_NONE][0]['value'] = 'Guy';
  $account->field_last_name[LANGUAGE_NONE][0]['value'] = 'Schneerson';

  $edit = array();

  user_save($account, $edit);

Will both work, however the first one is the correct way of doing things.

$edit should contain changes you want saved, however if you have a multi value element you need to include all its values
for example adding a role

Wrong way: will remove all roles except 'new role'

  $account = user_load($uid);
  $edit = array();
  $edit['roles'][NEW_ROLE] = 'new role';
  user_save($account, $edit);

Correct way: will add 'new role' to existing roles

  $account = user_load($uid);
  $edit = array();
  $edit['roles'] = $account->roles;
  $edit['roles'][NEW_ROLE] = 'new role';
  user_save($account, $edit);

I wanna ask you something for this part:

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

What is 'field_some_custom_field' ? Where do i find, what name i have to write there? I want to add custom hidden fields as targets for Feeds Module (to import users automatically one time a day).

Maybe somebody can help me how to add this function in the FeedsUserProcessor.inc.php

I have found out, that you have to add 2 parts of code for every target you want to add. I have found the code for Adding User Roles for example:

    if ($account->roles_list) {
     $roles = explode(',', $account->roles_list);
     foreach ($roles as $role_name) {
       $role_name = trim($role_name);
       if (!$role = user_role_load_by_name($role_name)) {
         // Create new role if role doesn't exist
         $role = new stdClass();
         $role->name = $role_name;
         user_role_save($role);
         $role = user_role_load_by_name($role->name);
     }
       $account->roles[$role->rid] = $role->name;
     }
   }

and

  'roles_list' => array(
'name' => t('User roles'),
'description' => t('User roles'),
      ),

I also know, that the first part is to use / work with the the input (but i dont know where it gets saved) and the second part is for naming and descibing the target for the backend UI. But i am not good enough to use this knowledge for adding other/additional user fields.

Maybe somebody could make an example?

<?php
$account
->name = 'me';
$account->pass = 'xxx';
$account->mail = 'x@x.com';
$account->status = 1;

// 3 and 4 are role id's
$roles = array(3 => true, 4 => true);
$account->roles = $roles;

user_save($account);
?>

Beware. I tried this and the password was saved un-encrypted in the database. You need to add it to the second argument:

user_save($account, array('pass' => 'xxx'));

$newUser = array(
'name' => 'username',
'pass' => 'password', // note: do not md5 the password
'mail' => 'email address',
'status' => 1,
'init' => 'email address'
);
user_save(null, $newUser);

See https://drupal.org/node/1969192

D7:
Fill in all of your fields for an example user and run the following code:

<?php
$user
= user_load(1);
debug($user);
?>

Then, copy the array and modify required parts...