I want to submit a form and store the submitted values in the session. In Drupal 7 I just used $_SESSION['key'] = $value;
but how do I correctly implement this in Drupal 8?
-
1I'm not sure about this question. It's really vague, which generally isn't the sort of question we accept here. On the other hand, I can definitely see the same question showing up again and again, so perhaps it has enough value to stay. I've answered as well as I can below, but I'll also vote to close to see what the community thinks.– LetharionCommented Oct 16, 2014 at 19:01
7 Answers
In D8 the session data is provided by a symfony session object, which is attached to the request. See this comparison of D7 against D8:
Drupal 7:
function mymodule_session_counter_increment() {
if (!isset($_SESSION['mymodule_count'])) {
$_SESSION['mymodule_count'] = 0;
}
return $_SESSION['mymodule_count']++;
}
Drupal 8:
class MymoduleSessionCounter {
function increment(Request $request) {
$session = $request->getSession();
$value = $session->get('mymodule_count', 0);
$session->set('mymodule_count', $value + 1);
return $value;
}
}
Change record: Access session data through the Request object
Session management has not changed. You don't need to do anything, because Drupal starts a session for each request automatically and you can use it right away.
Examples how to get the session:
Procedural
In procedural code get the request from the static wrapper \Drupal
:
$request = \Drupal::request();
$session = $request->getSession();
$session->set('mymodule_value', $value);
Controller
In a controller you can pull the request from the route argument stack by including a typed request parameter. An example from UserController
:
public function resetPass(Request $request, $uid, $timestamp, $hash) {
...
$session = $request->getSession();
$session->set('pass_reset_hash', $hash);
$session->set('pass_reset_timeout', $timestamp);
...
}
It's not necessary to define the route parameter in the route definition. The request is always available.
Form
In a form method get the request from getRequest()
:
public function submitForm(array &$form, FormStateInterface $form_state) {
$session = $this->getRequest()->getSession();
$session->set('mymodule_form_value', $form_state->getValue('value'));
}
Private Tempstore
The private tempstore is not a replacement for sessions. It depends on the session id and it's not possible to use it without a session. So you can store the values directly in the session as well. Drupal core uses the private tempstore for big amount of data and only for authenticated users who already have a session. For anonymous users it's not easy to get a private tempstore started, because drupal uses dummy session id's until it starts a real session. If you start a private tempstore in custom code it has the wrong session id.
-
1You say, "It's not necessary to define the route parameter in the route definition. The request is always available." But I get a runtime exception: Controller "...\Controller\MatomoReportsController::reports()" requires that you provide a value for the "$request" argument. Either the argument is nullable and no null value has been provided, no default value has been provided or because there is a non optional argument after this one. in Symfony\Component\HttpKernel\Controller\ArgumentResolver->getArguments() (line 78 of /var/www/vendor/symfony/http-kernel/Controller/ArgumentResolver.php).– shelaneCommented Jan 22, 2020 at 18:44
To temporary store data in the session, in Drupal 8.5 and higher there is the tempstore.private service that, for example, is used from the node edit form to temporary store the node preview.
public function form(array $form, FormStateInterface $form_state) {
// Try to restore from temp store, this must be done before calling
// parent::form().
$store = $this->tempStoreFactory
->get('node_preview');
// Attempt to load from preview when the uuid is present unless we are
// rebuilding the form.
$request_uuid = \Drupal::request()->query
->get('uuid');
if (!$form_state
->isRebuilding() && $request_uuid && ($preview = $store
->get($request_uuid))) {
/** @var $preview \Drupal\Core\Form\FormStateInterface */
$form_state
->setStorage($preview
->getStorage());
$form_state
->setUserInput($preview
->getUserInput());
// Rebuild the form.
$form_state
->setRebuild();
// The combination of having user input and rebuilding the form means
// that it will attempt to cache the form state which will fail if it is
// a GET request.
$form_state
->setRequestMethod('POST');
$this->entity = $preview
->getFormObject()
->getEntity();
$this->entity->in_preview = NULL;
$form_state
->set('has_been_previewed', TRUE);
}
/** @var \Drupal\node\NodeInterface $node */
$node = $this->entity;
if ($this->operation == 'edit') {
$form['#title'] = $this
->t('<em>Edit @type</em> @title', [
'@type' => node_get_type_label($node),
'@title' => $node
->label(),
]);
}
// ...
}
/**
* Form submission handler for the 'preview' action.
*
* @param $form
* An associative array containing the structure of the form.
* @param $form_state
* The current state of the form.
*/
public function preview(array $form, FormStateInterface $form_state) {
$store = $this->tempStoreFactory
->get('node_preview');
$this->entity->in_preview = TRUE;
$store
->set($this->entity
->uuid(), $form_state);
$route_parameters = [
'node_preview' => $this->entity
->uuid(),
'view_mode_id' => 'full',
];
$options = [];
$query = $this
->getRequest()->query;
if ($query
->has('destination')) {
$options['query']['destination'] = $query
->get('destination');
$query
->remove('destination');
}
$form_state
->setRedirect('entity.node.preview', $route_parameters, $options);
}
Before Drupal 8.5, the service is user.private_tempstore, which will be removed in Drupal 9.0.
Since you want to store the submitted values, which I assume are used only for a limited period of time and aren't necessary anymore after the user logs in or logs out, I would use the same approach used from Drupal core to show a preview of a node before it gets created/saved. It has also the pro that deleting the data is automatically done.
-
1I had come here to check if I was following the right approach for a multi-step submit process (i.e., exactly the example you have done, edit and then preview), so I was happy to have a confirmation. Also, thanks for the warning about
user.private_tempstore
, it will save me part of the headache when I finally migrate to Drupal 9 (I know, I'm late, but at least my project is for internal use only...) Commented Sep 15, 2022 at 7:27
Here is an article on how to use the appropriate service to store data in the session: http://atendesigngroup.com/blog/storing-session-data-drupal-8
There are two methods described in the article, here's the first one which doesn't use dependency injection. (code is not mine)
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');
-
The services user.private_tempstore and user.shared_tempstore are deprecated in favour of tempstore.private and tempstore.shared. Code that uses the User module services should be updated to use the new services from core.– AugustoCommented Sep 20, 2022 at 9:58
Sessions (Drupal 8) are used via the simple Session implementation of SessionInterface interface.
Symfony sessions are designed to replace several native PHP functions. Applications should avoid using session_start(), session_regenerate_id(), session_id(), session_name(), and session_destroy() and instead use the APIs in the following section.
Example:
use Symfony\Component\HttpFoundation\Session\Session;
$session = new Session();
$session->start();
// set and get session attributes
$session->set('name', 'Yash');
$session->get('name');
// set flash messages
$session->getFlashBag()->add('notice', 'Profile updated');
// retrieve messages
foreach ($session->getFlashBag()->get('notice', array()) as $message) {
echo '<div class="flash-notice">'.$message.'</div>';
}
-
1You don't need to instantiate a session object or start a session. Drupal adds a session to each request in the bootstrap process in
Drupal\Core\StackMiddleware\Session::handle()
and starts the session. You can get it with$session = \Drupal::request()->getSession()
.– 4uk4Commented May 31, 2017 at 18:19
You can use "\Drupal::service()".
To set temporary data:
$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');
You can refer to this tutorial for more details.
-
The services user.private_tempstore and user.shared_tempstore are deprecated in favour of tempstore.private and tempstore.shared. Code that uses the User module services should be updated to use the new services from core.– AugustoCommented Sep 20, 2022 at 9:57
Before you go rolling your own, see if the Session Node Access module does what you need.
how about ? :
$session = new Session();
$session->set('key', 'value');
$key= $session->get("key");
var_dump($key);