Storing Session Data with Drupal 8

I recently needed to temporarily store information associated with a user's session in Drupal 8. In past versions of Drupal, I might have just thrown the data in $_SESSION. In Drupal 8 there's a service for that; actually, two services: use user.private_tempstore and user.shared_tempstore for temporarily storing user-specific and non-user-specific data, respectively.

Background Reading

If you're new to Drupal 8, there are a number of OOP principles and specific Symfony concepts worth getting to know. This post does not directly address those. Here I'll just briefly address storing temporary data (aka writing to $_SESSION). For more background, the following are a few good resources about OOP, dependency injection and services in Drupal 8 (or skip ahead directly to writing $_SESSION variables in Drupal 8):

Method One: Use "\Drupal::service()"

Statically calling user.private_tempstore is simple and easy to use from just about anywhere within your application. It is especially useful for accessing the user.private_tempstore service from within procedural code (i.e. any hook implementation or simple function in your application).

To set temporary data:

// For "mymodule_name," any unique namespace will do.
// I'd probably use "mymodule_name" most of the time.
$tempstore = \Drupal::service('user.private_tempstore')->get('mymodule_name');
$tempstore->set('my_variable_name', $some_data);

To read temporary data:

$tempstore = \Drupal::service('user.private_tempstore')->get('mymodule_name');
$some_data = $tempstore->get('my_variable_name');

Method Two: Use Dependency Injection

Within controller or plugin classes, use dependency injection and Symfony's ContainerInterface to inject user.private_tempstore as a dependency.

Here's how to use dependency injection in a Drupal 8 controller:

namespace Drupal\my_module;
 
use Drupal\Core\Controller\ControllerBase;
use Drupal\user\PrivateTempStoreFactory;
 
 
class MyModuleController extends ControllerBase {
 
  protected $tempStore;
 
  // Pass the dependency to the object constructor
  public function __construct(PrivateTempStoreFactory $temp_store_factory) {
    // For "mymodule_name," any unique namespace will do
    $this->tempStore = $temp_store_factory->get('mymodule_name');
  }
 
  // Uses Symfony's ContainerInterface to declare dependency to be passed to constructor
  public static function create(ContainerInterface $container) {
    return new static(
      $container->get('user.private_tempstore')
    );
  }
 
  // Save some temporary data
  public function index() {
    $this->tempStore->set('my_variable_name', $some_data);
    // Do other stuff, return a render array, etc...
  }
 
  // Read some temporary data
  public function listPage() {
    $visited = $this->tempStore->get('my_variable_name');
    // Do other stuff, return a render array, etc...
  }
 
}

For a real life example of a Drupal 8 class that uses dependency injection, check out \Drupal\user\Form\UserMultipleCancelConfirm in Drupal core.

Both methods are roughly equivalent to:

// Save the data:
$_SESSION['mymodule_name']['my_variable_name'] = $somedata;
 
// Get the data:
$somedata = $_SESSION['mymodule_name']['my_variable_name'];

But (and it's an important but) using Drupal 8 services provides needed abstraction and structure for interacting with a global construct. It's part of an overall architecture that allows developers to build and extend complex applications sustainably.

Drupal 8 Drupal