ianspangler's avatar

Auth is null in boot or constructor of model

Using Laravel 5.2 with "web" routes, I am hung up on getting Auth::user() or Auth::id() to be recognized from within a boot or __construct method of my model. It is accessible from all other methods of the model except for these.

namespace App;
use Illuminate\Database\Eloquent\Model;
use Auth;

class Movie extends Model
{
   public static function boot() {
        parent::boot();
        dd(Auth::user()); //returns null...

        static::updating(function($movie) {
            $movie->revisions()->attach(Auth::id(), ['revisable_type' => $movie->morphClass]);
        });
    }

/**
     * A movie may have multiple sources associated with it.
     *
     * @return \Illuminate\Database\Eloquent\Relations\belongsToMany
     */
    public function sources() {
        dd(Auth::user()); //just for testing -- returns the full User Collection
        return $this->belongsToMany('App\Source');
    }
}

Thanks in advance.

0 likes
12 replies
Jaytee's avatar

This is an obvious one, if a user isn't logged in then it's going to be NULL.

Is a user logged in?

ianspangler's avatar

Yes, definitely logged in... and Auth::user() is set correctly in the "sources" public method.

Jaytee's avatar

Try:

    Auth::check()

    and try dd($request->user());

See what that returns.

thomaskim's avatar

At which point in the Laravel lifecycle are you calling these methods?

ianspangler's avatar

@DPJack Auth::check() returns false in boot() and true in sources(). $request-user() throws an error because request is not a defined parameter.

@thomaskim the "Movie" model gets instantiated like any other. I didn't do anything special with the loading or sequence of loading of the models that I am aware of.

thomaskim's avatar

Sorry about that. :) I meant where are you calling it. A lot of times, these issues come about because people try to do this in the service providers.

ianspangler's avatar

It stands to reason that perhaps the session does not get instantiated until after boot() is called, but before sources() is called, but I am not sure why that would be.

ianspangler's avatar

@thomaskim: I am just calling the model from its own controller "MoviesController" using L5.2's implicit route-model binding, which is just the basic default implementation:

public function show(Movie $movie) {
    
        dd($movie->sources); //this works fine

        return view('movies/show', compact('movie'));
}

thomaskim's avatar
Level 41

@ianspangler Unfortunately, by default, you can't use anything related to sessions with route model binding. Route model binding kicks in before your sessions do because the middleware responsible for booting up the sessions has not been "activated" yet in a sense. I believe the recommended method for these cases is to not use route model binding.

However, if you have to use it, then one solution would be to scrap the web middleware and move everything in that middleware group into the global middleware stack. In other words, in your app\Http\Kernel file, do this:

    /**
     * The application's global HTTP middleware stack.
     *
     * These middleware are run during every request to your application.
     *
     * @var array
     */
    protected $middleware = [
        \Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,
        \App\Http\Middleware\EncryptCookies::class,
        \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
        \Illuminate\Session\Middleware\StartSession::class,
        \Illuminate\View\Middleware\ShareErrorsFromSession::class,
        \App\Http\Middleware\VerifyCsrfToken::class,
    ];

By doing so, those middlewares will run very early in the life cycle, and you will have access to the authenticated user during route model binding. You can also get rid of the web middleware in this case.

ianspangler's avatar

Ah, thanks for pointing this out. The middleware adjustment is appealing to me, but I'm almost afraid to do it because I've seen so much documentation indicating to put everything in the web group for L5.2. This begs the question of why the web group was introduced in the first place which I don't really recall. Is there a downside to not using it? Performance/ speed is the same?

Also, just to be sure, there isn't any other place besides boot() where I could put the static::updating closure, correct? That would probably be my first choice.

ianspangler's avatar

Okay, so taking the routes out of the web group solves the problem of Auth::user() being null on boot(). However, it is still coming out null once the closure static::updating runs...Any ideas about why it might lose scope there?

public static function boot() {
        parent::boot();
        dd(Auth::user()); //returns a Collection -- great!
      static::updating(function($movie) {
            dd(Auth::user()); //returns null... WHY?
        });
}
ianspangler's avatar

OK, seems that I came upon the issue... I was neglecting to consider that when inserting updates in "php artisan tinker", it is running on a different session than when testing in browser, so I had to Auth::login($user) in tinker in order to get a valid user. So it appears to be working now...

Please or to participate in this conversation.