Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

muuucho's avatar
Level 11

Use session from middleware in AppServiceProvider

I set a session variable in a middleware that I need throughout my application. I have registerd the middleware in Kernel's $routeMiddleware Now I like to access the session in a Gate in AppServiceProvider. However it is not accessible there. I try

public function boot()
    {
        $u = Session::get('u');
        \Log::info($u['name'])
}

I get Trying to access array offset on value of type null. I can access the session from controllers but not from here. Is there any way to achieve it?

0 likes
24 replies
LaryAI's avatar
Level 58

In Laravel, the AppServiceProvider's boot method is executed very early in the request lifecycle, before the session data is available. This is why you're encountering the error when trying to access session data there.

To access session data in a service provider, you need to ensure that the session has been started. One way to achieve this is by deferring the logic that requires session data until after the session middleware has run. You can do this by using a closure that is executed after the request lifecycle has been fully bootstrapped.

Here's how you can modify your AppServiceProvider to access session data:

use Illuminate\Support\Facades\Session;
use Illuminate\Support\Facades\Log;

public function boot()
{
    // Use the 'afterResolving' method to defer execution until after the session is available
    $this->app->afterResolving('router', function () {
        $u = Session::get('u');
        if ($u) {
            Log::info($u['name']);
        } else {
            Log::info('Session data not available');
        }
    });
}

Explanation:

  • afterResolving: This method allows you to defer the execution of a closure until after a specific service has been resolved. In this case, we're using it with the router service, which ensures that the session middleware has already run.

  • Session::get('u'): This retrieves the session data. Make sure that the session key 'u' is set before this point in your middleware.

  • Log::info: This logs the session data if available, or logs a message indicating that the session data is not available.

By using this approach, you ensure that the session data is accessible when you need it in the AppServiceProvider.

muuucho's avatar
Level 11

With this code;

public function boot()
    {
        \Log::info('check');
        // Use the 'afterResolving' method to defer execution until after the session is available
        $this->app->afterResolving('router', function () {
            $u = Session::get('u');
            if ($u) {
                \Log::info('drg'.$u['name']);
            } else {
                \Log::info('Session data not available');
            }
        });

Only check i slogged. Any other solution?

JussiMannisto's avatar

Service providers are booted before the request is handled, so they don't have access to session data. More importantly, they're always booted when the framework is booted. That also means artisan commands, jobs, API calls, etc. Session data doesn't exist in those contexts so your app can't rely on it.

Can you describe your use case more?

2 likes
muuucho's avatar
Level 11

@JussiMannisto I use a gate in my AppSeerviceProvider. Like this:

Gate::define('manage_invoice', function (User $user){
            return $user->manage_invoice == 1;
        });

But a user can belongToMany teams through tables users, teams and team_user

So I now have to check $user->team_user->manage_invoice in the Gate instead, so I was trying to load the user's roles in a session that I create in a middleware named GetUser

muuucho's avatar
Level 11

Solved. I moved the session call into each Gate. Like this:

Gate::define('manage_invoice', function (User $user){
            $u = Session::get('u');
            return in_array('manage_invoice', $u['roles']);
        });

Inside the Gate, the session seems to be available.

JussiMannisto's avatar

@muuucho That doesn't sound like something that should go in the session data. I'd just use Eloquent like you showed. What's your reasoning for storing it in session?

muuucho's avatar
Level 11

@JussiMannisto The reason is that I like to reduce the number of Querys that runa on each request. In the navbar I echo out $user->name and $user->team->name In controllers I need $user->userTeam->isAdmin And as mentioned I use it in AppServiceProvider. So, I try to reduce all these queries to a single one so I run it as early as possible in a middleware and store the result in a session that is available throughout all the following files is called in the request. Does it make sense?

JussiMannisto's avatar

@muuucho That's an attempt at micro-optimization. Also, it's worse for performance. You're adding a middleware, making more DB queries and writing more data to the session store.

Let's compare two approaches to accessing $user->currentTeamUser->name in your code:

  1. You call $user->currentTeamUser->name.
  2. You use your approach with an additional middleware and session reads/writes.

How many DB queries does the first approach cause:

The auth middleware pulls the user model from the DB (1). The model is injected into the service container and passed around the framework during the request, including gate checks. When $user->currentTeamUser->name is called for the first time, the currentTeamUser relation is retrieved (2) and stored on the user model. All subsequent calls to $user->currentTeamUser will return it without additional DB queries.

Now your approach:

The auth middleware pulls the user model from the DB (1). Your middleware retrieves the user model again (2). As per the with() clause, the currentTeamUser relation is eager loaded (3).

That's already more DB queries per request. You're also reading and writing more data to the session store. Relationships are not loaded on the user model that is passed around the framework. Your gates won't work in commands, jobs or API routes.

In short, you should just use $user->currentTeamUser. Eager loading is also pointless here as it results in the same number of DB queries.

1 like
muuucho's avatar
Level 11

@JussiMannisto Thanks, that makes sense. Could it be an alternative to call a UserService.php that has a method that returns the $u whenever I need currentTeam and/or the roles that the user has in that team?

JussiMannisto's avatar

@muuucho I'd just use the user model and its relations. Eloquent is already designed to eliminate unnecessary queries.

1 like
muuucho's avatar
Level 11

@martinbean Not sure I understand. From where do I get the team (or, in my case team_user) data and how do I accept it in the Gates?

martinbean's avatar

From where do I get the team

@muuucho Only you can answer that. We have no idea how your application is architected or modelled.

how do I accept it in the Gates?

By passing the team when you’re calling a gate method or performing an authorisation check (as per the docs I linked you to):

Gate::authorize('manage_invoice', [$currentTeam]);
muuucho's avatar
Level 11

@martinbean I get how I can send additional data to Gates from controllers:

$user = User::with('currentUserTeam')->find($id);
if (! Gate::allows('manage-invoice', $user)) {
            abort(403);
        }

But how do I use That gate to restrict routes in Routes file? How do I send $user? ->middleware('can:manage_upload')

Snapey's avatar
Snapey
Best Answer
Level 122

If you are going to store data in session, at least check if it is already set (from a previous request) and don't requery the database.

Personally, I would probably decorate the Auth:user with the data you need

1 like
muuucho's avatar
Level 11

@Snapey "Decorate Auth:user()" You mean by creating an accessor in User Model for each element I would like to add?

muuucho's avatar
Level 11

I tried $userRoles = Auth::user()->currentTeamUser->roles->pluck('name', 'id')->toArray(); It works! I didn't know I could chain methods to Auth

Snapey's avatar

@muuucho its just a pointer to a User model (in most cases) so you can call methods on it

1 like
muuucho's avatar
Level 11

@Snapey Yes. I wish it was pointed out more clearly in the docs or like in Laravel in 30 days. Or elsewhere. Untill today I thougt that Auth() was for auth stuff only.

muuucho's avatar
Level 11

@JussiMannisto It sure is. However, when I read that part of the docs, I was a complete newbie and didn't understood that I could chain other relational tables. And after that, I have just looked upon Auth() as a way to authenticate by users id, or use other columns from table users.

And still, from what I can remember, I haven't seen any Laravel tutorial using Auth::user()->someUserMethod Anyways, now I have implemented it in my app.

Please or to participate in this conversation.