Stashing data in the D7 User Object (or... no more magical fairy cruft saving)

A unique and surprising feature of the Drupal user object up through Drupal 6 was that you could throw any crap into it you wanted and then run user_save() and it would be there. So in Drupal 6:

user_save($account, array('some_random_stuff' => 'surprisingly random');

Well, that always seemed way too easy and it was. After a security problem was discovered with this in way early D7 alpha days, it was decided to make this far more explicit and deliberate. Now you have to have implement hook_user_presave() to actually move your data into the right place and prove that you really wanted to do it.

Best practice says that we're going to save settings into the user object keyed by the module name. So if my module is named 'mymodule' in Drupal 7:


...
$stuff = array('some_random_stuff' => 'surprisingly_random');
user_save($account, array('mymodule' => $stuff);

...
function mymodule_user_presave(&$edit, $account, $category) {
// Make sure that our form value 'mymodule_foo' is stored as 'mymodule_bar'.
if (isset($edit['mymodule']['some_random_stuff'])) {
$edit['data']['mymodule']['some_random_stuff'] = $edit['mymodule']['some_random_stuff'];
}
}

This is demonstrated, but not so well explained, in hook_user_presave(). The issue where this was done is #721436.

Note that the entire data column in the users table will probably be removed in Drupal 8, so the "preferred" storage technique of storing data in a module-owned table may be an appropriate and future-proof approach.

4 Comments

Thanks, Andy - The idea here

Thanks, Andy - The idea here was to explain how D6->D7 ports of this feature could be done most easily.

Would love to have you demonstrate here how you'd do it!

Why?

I guess I don't understand what the use case would be that would warrant doing this in the first place.

Presumably it's to associate some data needed by a module with a specific user, but why then not just have a separate table in the DB for that module that stores the needed data associated with the UID?

I've done this for persistent recipe scaling on http://thelunchbox.org

Logged in users have the ability to scale any recipe on the site by either elementary servings or secondary servings. Their choice is then inserted into a table whose entire purpose for being is to handle these relationships.

So, when user with UID 5 scales the recipe with nid 1026, an entry is either inserted (or updated) in that table with their desired number of servings. Primary key for the table is a combination of UID & NID, and there is a field for each of the two serving types. Some JS only allows populating one of the two fields.

In this case, the table row for this user might have values like this:
UID = 5, NID = 1026, elementary = NULL, secondary = 300

Once saved, this recipe will always be shown to this user with their desired number of servings, as long as the user is logged in, until they either change the number, or submit an empty value (which will remove the entry from the table, and cause the recipe to display with default values).

This is of course what you're referring to in your last sentence about using a module owned table, but I guess I don't see a reason ever to do this in the other way by just attaching arbitrary data to the user object. It doesn't seem terribly useful to me.

Anyone have any good examples of when it might make more sense to attach some arbitrary data to the user object like this?