Profile picture for user admin
Daniel Sipos
21 Mar 2017

Have you ever needed to persist some irregular data relating with a user account? Things like user preferences or settings that are kinda configuration but not really? Storing them as configuration means having to export them, and that’s no option. Storing them in State is an option, but not a good one as you’d have to maintain a State value map with each of your users and who wants to deal with that…

In Drupal 7, if you remember way back when, we had this data column in the user table which meant that we could add to the $user->data array whatever we wanted and it would get serialised and saved in that column (if we saved the user object). So what is the equivalent of this in Drupal 8?

I’m happy to say we have the exact same thing, but of course much more flexible and properly handled. So not exactly the same. But close. We have the user.data service provided by the User module and the users_data table for storage. So how does this work?

First of all, it’s a service. So whenever we need to work with it, we have to get an instance like so:

/** @var UserDataInterface $userData */
$userData = \Drupal::service('user.data');

Of course, you should inject it wherever possible.

The resulting object (by default) will be UserData which implements UserDataInterface. And using this object we can store as many pieces of data or information for a given user as we want. Let’s explore this a bit to learn more.

The interface has 3 methods or handling data: get(), set(), delete(). But the power comes in the method arguments. This is how we can store some data for User 1:

$userData->set('my_module', 1, 'my_preference', 'this is my preference');

So as you can see, we have 4 arguments:

  • The module name we want this piece of data to be findable by
  • The user ID
  • The name of the piece of data
  • The value of the piece of data

This is very flexible. First, we can have module specific data. No more colluding with other modules for storing user preferences. Stay in your lane. Second, we can have multiple pieces of data per user, per module. And third, the value is automatically serialised for us so we are not restricted to simple strings.

Retrieving data can be done like so:

$data = $userData->get('my_module', 1, 'my_preference');

This will return exactly this is my preference (in our case). And deserialisation also happens automatically if your data got serialised on input.

Deleting data is just as easy:

$userData->delete('my_module', 1, 'my_preference');

Moreover, most of the arguments of the get() and delete() methods are optional. Meaning you can load/delete multiple pieces of data at once. Check out UserDataInterface to see how omitting some arguments can return/delete sets of records rather than individual ones.

And that is pretty much it. And in true Drupal 8 form, there’s nobody stopping you from overriding the service and using your own UserDataInterface implementation that has that little extra something you are missing. So no, you probably don’t have to create that custom table after all.

Profile picture for user admin

Daniel Sipos

CEO @ Web Omelette

Danny founded WEBOMELETTE in 2012 as a passion project, mostly writing about Drupal problems he faced day to day, as well as about new technologies and things that he thought other developers would find useful. Now he now manages a team of developers and designers, delivering quality products that make businesses successful.

Contact us

Comments

Tim Mallezie 21 Mar 2017 10:58

Cool

Super timing. This is just what i needed for a side project i'm working on. ;-)

Paul 22 Mar 2017 05:15

Another undocumented feature

I have a couple points:
Why do we have to go to blog posts to find out about these features? Why can't they be cleanly documented on drupal.org? And please don't point me to the reference doc because it's so brief it's pathetic.
Also, isn't this what PrivateTempStore is for?
Thanks in advance.

Paul

James Williams 29 Mar 2017 18:18

In reply to by Paul (not verified)

Someone has to go write that

Someone has to go write that documentation -- go for it! There is also so much to Drupal, that's I find it's quite helpful for people to write up about certain things, and less formally than true documentation. For example, blogging about the process of solving/discovering something can be as informative about the end solution.

PrivateTempStore data expires automatically after a given timeframe, so isn't appropriate for any data that needs to stick around. The user.data service isn't restricted to one user either, so module code could set data for multiple users through the single service rather than having to instantiate multiple PrivateTempStore objects. The PrivateTempStore is only ever used in core through a factory too, so may not be intended to be used directly.

Dalin 30 Mar 2017 03:39

In reply to by Paul (not verified)

Because you haven't written

Because you haven't written it yet.

Drupal is volunteer run. There isn't some sort of paid staff for maintaining documentation.

Daniel Sipos 14 Jun 2020 12:35

In reply to by csoky (not verified)

What would be the use case

What would be the use case for that?

Anonymous 22 Dec 2020 19:27

In reply to by csoky (not verified)

I think if you need that,

I think if you need that, then you probably need another service
Most likely a content entity if it's something you'd want to store permanently

Diao Diallo 12 Oct 2021 21:37

I had to create custom entity to address my problem

This is amazing! how should we store an array in example? sorry did not check the code yet.

Add new comment