twitter rss github stackoverflow
Joseph Silber's Avatar

Joseph Silber

Getting the current user (or other session data) in a Laravel controller constructor

Let's discuss something many people have accidentally discovered: you cannot use session data directly in a controller's constructor.

This change was introduced in Laravel 5.3, when the middleware pipeline was rewired to make global scopes work with session data.

In this post we'll examine the problem in more detail, and look at various ways to solve it.

The Problem

There are many reasons people want session data in the controller's constructor. Let's look at 2 examples:

  1. Sharing the current user to all views:

    Instead of using auth()->user() in your views, it's sometimes nicer to just have a global $user variable available across all views. A quick way to achieve that is to add a constructor to the base controller and share it from there:

    public function __construct()
    {
        view()->share('user', auth()->user());
    }
  2. Setting a property on the controller that needs the session:

    If you need the same piece of information in all of your methods, you may opt to set it as a property on the controller:

    public function __construct()
    {
        $this->account = Account::find(session('account_id'));
    }

In both of these cases, the code in the constructor assumes that the session is primed and ready. But if we try to run the code, it'll fail. At this point, the request has not yet been routed and no session data is available.

Solution #1: The Authenticated event

For every request, Laravel will fire the Authenticated event when the user is authenticated. You can listen for this event in a service provider, and share the $user from there:

use Illuminate\Auth\Events\Authenticated;

class AppServiceProvider extends ServiceProvider
{
    public function boot()
    {
        $this->app['events']->listen(Authenticated::class, function ($e) {
            view()->share('user', $e->user);
        });
    }
}

Solution #2: Inline view composer

If you'd rather share the user from the base controller, you can use an inline view composer:

public function __construct()
{
    view()->composer('*', function ($view) {
        $view->with('user', auth()->user());
    });
}

Since the callback will only execute when the view is composed, you'll have access to the session.

Solution #3: Converting a property to a method

Let's go back to the second example in the problem section. Instead of finding the current account directly in the constructor, you can put its logic into its own method and only call it when you actually need it:

public function index()
{
    $account = $this->account();

    // use it as before...
}

protected function account()
{
    return Account::find(session('account_id'));
}

This is great for new projects, but can be a little tedious if you already have a big project that relies on the property being available across all controller methods. For such cases, you can use an inline middleware.

Solution #4: Inline middleware

Inline middleware are a little more involved than the other solutions, but are probably the most flexible.

You may have seen $this->middleware('auth') before, which registers an existing middleware directly from within the controller constructor. What you may not know is that you can actually use a closure to define the middleware on the fly.

Using an inline middleware, we can hook into the request's routing, so that we have full access to the session:

public function __construct()
{
    $this->middleware(function ($request, $next) {
        $this->account = Account::find(session('account_id'));

        return $next($request);
    });
}

We can also use an inline middleware to share the $user to all views:

public function __construct()
{
    $this->middleware(function ($request, $next) {
        view()->share('user', $request->user());

        return $next($request);
    });
}

As you can see, this is a little more complex than the other solutions, but it gives us the most flexibilty.


These 4 solutions should cover all your session needs. If there's any controller session problem that's not solved by these, or if you have any better solutions, do let me know!


Questions? Comments? Complaints? Ping me on Twitter.

Recent posts

  1. Releasing Bouncer: Roles and Permissions for Laravel apps

    A personal post with some musings about my journey through the years - from inception to final release

    Read more ➺

  2. Lazy Collections in Laravel

    A deep dive into Laravel's Lazy Collections, and how to process enormous amounts of data with very little memory.

    Read more ➺

  3. How to rid your database of PHP class names in Eloquent's Polymorphic tables

    Using Relation::morphMap() to instruct Eloquent to store more generic values for polymorphic types.

    Read more ➺

  4. Gate and authorization improvements in Laravel 5.3

    With Laravel 5.2 we got a nice authorization system right out of the box. In 5.3 this got a lot of improvements and refinements. Let's take a look.

    Read more ➺

  5. The new Closure::fromCallable() in PHP 7.1

    Let's take a look at a neat little feature coming in PHP 7.1: easily converting any callable into a proper Closure using the new Closure::fromCallable() method.

    Read more ➺