Getting Caught by the Cookie Monster

Let's say you have a middleware that is setting a cookie that will be checked in a controller. Today, I ran into an interesting problem where the cookie being set in the middleware has not actually been set yet when the controller is loaded.

Let's take a look at this:

// web.php

Route::get('home', HomeController@index)->middleware('someMiddleware');
// SomeMiddleware.php

...

public function handle($request, Closure $next)
{
    Cookie::queue('cookie', $cookie_value, $cookieTime);

    return $next($request);
}

Now when the request actually hits the controller:

// HomeController.php

...

public function index()
{
    // Attempt to get the cookie
    $cookie = Cookie::get('cookie');

    return view('home', compact('cookie'));
}

So, even though a user went through the middleware and had a cookie queued, once you hit controller you would find that there would be no cookie 😱 Now this makes sense when you think about. The cookie that is queued above will only actually be set by the browser once the page finishes loading. Or put another way:

These [queued] cookies will be attached to the outgoing response before it is sent to the browser.

This makes sense, so of course it wouldn't be there yet! But it wasn't expected the first time I ran into this.

So how did I solve it

I leveraged the requests attributes in the middleware to attach the cookie to the request that could then be parsed in the controller:

// SomeMiddleware.php

...

public function handle($request, Closure $next)
{
    Cookie::queue('cookie', $cookie_value, $cookieTime);
    // Append the attribute for the first time if required
    $request->attributes->add(['cookie' => $cookie_value]);

    return $next($request);
}

Which we can then catch in the controller:

// HomeController.php

...

public function index()
{
    // Attempt to get the cookie
    $cookie = Cookie::get('cookie');
    if (! $cookie) {
        // If there is no cookie that means
        // it hasn't been set yet, but we can
        // get it from the request instead
        $cookie = \Request::get('cookie');
    }

    return view('home', compact('cookie'));
}