10

I need to save a cookie for anonymous users in Drupal 8 when users visit the site for the first time, which will be used to display a message.

I can successfully save one using setcookie("FirstTimer", 1, strtotime('+1 year')); but I am sure Drupal 8 should have some alternatives.

I can also use the following code.

$user_is_first_timer = !isset($_COOKIE["Drupal_visitor_FirstTimer"]);

When I open it in Google Chrome, this works, but when I open it in incognito or in Firefox, there is no message. Somehow cached pages are being served.

The code is used in an implementation of hook_page_attachments().

2 Answers 2

13

With Drupal 7, Drupal 8, and Drupal 9 you could use user_cookie_save() to store a cookie.

function user_cookie_save(array $values) {
  foreach ($values as $field => $value) {
    // Set cookie for 365 days.
    setrawcookie('Drupal.visitor.' . $field, rawurlencode($value), REQUEST_TIME + 31536000, '/');
  }
}

The first parameter is an array, whose keys are the names of the cookies to set and its values are the cookie values.
user_cookie_save() doesn't allow to set when a cookie expires; cookies created with this function expires after 365 days.

The only method that in Drupal 8 calls user_cookie_save() is SystemController::compactPage().

public function compactPage($mode) {
  user_cookie_save(['admin_compact_mode' => $mode == 'on']);
  return $this->redirect('<front>');
}

Unfortunately, there isn't an equivalent function to read a cookie. In Drupal 8 and Drupal 9, to read the cookie saved from SystemController::compactPage() it's used system_admin_compact_mode().

function system_admin_compact_mode() {
  // PHP converts dots into underscores in cookie names to avoid problems with
  // its parser, so we use a converted cookie name.
  return \Drupal::request()->cookies->get('Drupal_visitor_admin_compact_mode', \Drupal::config('system.site')->get('admin_compact_mode'));
}

In alternative, in Drupal 8 and Drupal 9, you could store cookies using code similar to the one used by BigPipeController::setNoJsCookie() (implemented since Drupal 8.1.x).

if ($request->cookies->has(BigPipeStrategy::NOJS_COOKIE) || !$request->hasSession()) {
  throw new AccessDeniedHttpException();
}
if (!$request->query->has('destination')) {
  throw new HttpException(400, 'The original location is missing.');
}
$response = new LocalRedirectResponse($request->query->get('destination'));

// Set cookie without httpOnly, so that JavaScript can delete it.
$response->headers->setCookie(new Cookie(BigPipeStrategy::NOJS_COOKIE, TRUE, 0, '/', NULL, FALSE, FALSE, FALSE, NULL));
$response->addCacheableDependency((new CacheableMetadata())
  ->addCacheContexts([
    'cookies:' . BigPipeStrategy::NOJS_COOKIE,
    'session.exists',
  ]));
return $response;

This code used the $response object from Symfony, instead of using a PHP function.
It's also more "symmetric," as the same object allows to read and write cookies.

Using $response->headers->setCookie() gives more control over the cookie parameters (the parameters Cookie::__construct() accepts).

0
6

Drupal 8 cookies can be set using ResponseHeaderBag from the Symfony\Component\HttpFoundation\Response object.

Set new cookie value

use Symfony\Component\HttpFoundation\Cookie;

$cookie = new use Cookie('cookie_name', TRUE);
$response->headers->setCookie($cookie);
return $response;

Get cookies value

$request->cookies->get('cookie_name');

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.