CakePHP 4.1.4 - How to create, read and check cookies in the new version of CakePHP

Viewed 2251

In CakePHP 3.8 the segment of my controller looks like this:

// ...  

public function beforeFilter(Event $event)
{
    // ...

    $this->Cookie->configKey('guestCookie', [
        'expires' => '+1 days',
        'httpOnly' => true,
        'encryption' => false
    ]);

    if(!$this->Cookie->check('guestCookie')) {
        $guestID = Text::uuid();
        $this->Cookie->write('guestCookie', $guestID);
    }
    
    $this->guestCookie = $this->Cookie->read('guestCookie');

    // ...
}


public function error()
{
    // ...
    $this->Cookie->delete('guestCookie');
    // ...
}

// ...

How to write the same thing in CakePHP4 version? My problem relates to defining Cookies. Cookie settings are described here: https://book.cakephp.org/4/en/controllers/request-response.html#cookie-collections , but unfortunately this didn't help me at all.

I tried to solve the problem on this way:

    public function beforeFilter(EventInterface $event)
    {
//...
    
        if(!$this->cookies->has('guestCookie')) { 
            $cookie = (new Cookie('guestCookie'))->withValue(Text::uuid())->withExpiry(new \DateTime('+20 days'))->withPath('/')->withSecure(false)->withHttpOnly(true); 
            $this->cookies = new CookieCollection([$cookie]);
        }

        $this->guestCookie = $this->cookies->get('guestCookie')->getValue();      
 //...       
    } 

In my case $ this->cookies->has('guestCookie') is always 'false'. The cookie value is never stored in the browser. Please help.

1 Answers

There's rarely a need to touch cookie collections, most of the times simple reading of cookie values from the request object, and writing cookies to the response object is all you need, so I would suggest that you stick with that until the actual need for collections comes up.

The docs could probably do a better job here at explaining when to use what.

Reading, writing, and deleting cookies

As shown in the linked docs, cookie values can be read via:

$this->request->getCookie($cookieName)

and written via:

$this->response = $this->response->withCookie($cookieObject)

It's important to reassign the response object (unless you directly return it from the controller), as it is immutable, meaning withCookie() will return a new response object instead of modifying the current one.

Deleting cookies can be done by responding with an expired cookie, using withExpiredCookie() instead of withCookie(), or obtaining the expired version of a cookie via $cookie->withExpired() and passing it to withCookie().

Configuring cookie defaults

If you wanted to, cookie defaults can be set via Cookie::setDefaults():

\Cake\Cookie\Cookie::setDefaults([
    'expires' => new DateTime('+1 days'),
    'http' => true,
]);

However this will apply application wide to all cookie instances being created after this point, so you'd likely use it rather rarely, and if you do, do so with care!

Porting from the Cookie component

With the new API, your code could be written like this, with $this->guestCookie holding the cookie value, either the newly generated one, or the one obtained from the cookie received by your application:

use Cake\Http\Cookie\Cookie;

// ...  

public function beforeFilter(Event $event)
{
    // ...
    $guestID = $this->request->getCookie('guestCookie');
    if(!$guestID) {
        $guestID = Text::uuid();
        
        $cookie = Cookie::create('guestCookie', $guestID, [
            'expires' => new DateTime('+1 days'),
            'http' => true,
        ]);
        $this->response = $this->response->withCookie($cookie);
    }
    
    $this->guestCookie = $guestID;
    // ...
}

public function error()
{
    // ...
    $cookie = new Cookie('guestCookie');
    $this->response = $this->response->withExpiredCookie($cookie);
    // ...
}

// ...

See also

Related