Way to use Config::set() twice in a function

2019-03-31 21:24发布

问题:

I have googled and stackoverflowed (if you may) for this question, and couldn't find a succint enough answer to it: (some ref: Laravel 4: when using Config::set to change auth.model then Auth::user() not work , Laravel 5.2 how to use config::set in middleware)

How can I successfully set a variable twice at runtime with Config::set() in Laravel 5.2.*?

Here is an example of what I tried to achieve:

I have two tables companies and users (they both login from different routes with JWTAuth). Now I want to fetch all computers record at this single route Route::get('/computers')

Now the issue have is that I want to use the same middleware to do that, but I want to ensure that any of those users (i.e company or user) is authenticated before they can access this resource

Here is what I have attempted trying to use Config::set() in my middleware:

//all.auth middleware
public function handle($request, Closure $next)
{
    Config::set('auth.providers.users.model', \App\Company::class);
    Config::set('jwt.user', \App\Company::class);

      //check if the request is for company
    if($company = JWTAuth::toUser(($request->has('token')) ? $request->token : $request->bearerToken())) 
    {
        return ['COMPANY' => $company];
    }

    //Unfortunately its not company, lets try users

    Config::set('auth.providers.users.model', \App\User::class);
    Config::set('jwt.user', \App\User::class);

    if($user = JWTAuth::toUser(($request->has('token')) ? $request->token : $request->bearerToken())) 
    {
        return ['USER' => $user];
    }
    throw new NotFoundHttpException('Your account could not be found.');
    ...............
}

Now, the behaviour I noticed is that it successfully changed the model JWT would use to Company but didn't change it to User in case the token sent with the request is for a User.

I will be grateful if someone can help out, to understand at least if this is possible or not.

If there is a need to provide more explanations, I am ready to do that.

回答1:

Setting config values multiple times works as expected. The reason your code doesn't work as you expect it is that the service behind the JWTAuth facade is resolved once only, the first time you access it, using current values of those 2 config parameters. Changing it later won't have any impact.

In order to achieve what you want, you need to make Laravel's service container recreate the service with the new config values. To do that you need to do 2 things.

First, remove the resolved instance from the facade so that the next time you use that, it fetches new instance of the service from service container:

JWTAuth::clearResolvedInstances();

Then remove the service instance from the container so that it needs to be created again with new config values:

App::forgetInstance('tymon.jwt.auth');

Add those 2 lines after you set new config values and next time you use JWTAuth facade it should use tne new setup.



回答2:

For what I can understand at the moment you call the JWTAuth Facade for the second time in you code, its own instance already exists in the service container.

JWTAuth dont have to be constructed nor inizialized another time so it uses the old instance that was constructed using the config at the moment you called it the first time.

The Config::set() twice works but the JWTAuth instance is always the same (the first one) so I think you have to destroy the JWTAuth instance before the second call to the Facade.

Hope this helps.



回答3:

As @jedrzej.kurylo pointed out setting the config values multiple times before initiating the JWTAuth facade works as expected, however the JWTAuth facade is only resolved once. So you'll have to force the facade to be forgotten and re-resolved to get the config changes.

The code proposed in jedrzej answer didn't work for me with Laravel 5.5, it seemed like the facade was not being forgotten properly.

Through the process of elimination by forgetting additional instances listed in app()->getBindings() I was able to reset the facade with the following.

JWTAuth::clearResolvedInstances();
app()->forgetInstance('tymon.jwt.auth');
app()->forgetInstance('tymon.jwt.provider.user');

Hope this helps if anyone else is struggling.