Laravel obtains the current Guard analysis-from the actual needs of E-commerce shopping carts, laravel e-commerce
The shopping cart requirements in iBrand are complex. We have expanded a shopping cart that better meets the needs of E-commerce based on overture/laravel-shopping-cart. Some Articles have previously made a simple introduction: laravel shopping cart: an e-commerce shopping cart that is running online.
Source Code address: ibrand/laravel-shopping-cart
Original Requirement
This package was initially extended due to the following requirements:
- The user's shopping cart data after login needs to be stored in the database. Because the customer wants to intuitively see the product information in the current shopping cart, so as to push the discount information to promote conversion. Although we passed the data according to GA standards, we found that the data in GA was not very accurate.
- User shopping cart data in the mall
- Shopping guide uses shopping guide mini-programs to place orders on behalf of users or add shopping cart data during checkout, and does not synchronize with the user shopping cart data.
Original Solution
When the first requirement came out, we used different Guard to differentiate the user shopping cart data. Because the shopping mall and shopping guide were two different user systems, the code in the shopping cart ServiceProvider was as follows:
$currentGuard = null; $user = null; $guards = array_keys(config('auth.guards')); foreach ($guards as $guard) { if ($user = auth($guard)->user()) { $currentGuard = $guard; break; } } if ($user) { //The cart name like `cart.{guard}.{user_id}`: cart.api.1 $cart->name($currentGuard.'.'.$user->id); }else{ throw new Exception('Invalid auth.'); }
Traverse all existingGuards
To obtain the guard value and user object of the current user in the request. Originally, when the new demand is not increased, everything runs normally.
New requirements
New requirements in 18 years:
- The shopping cart data for self-ordering by the user's store QR code (QR code or barcode) should be differentiated from the shopping cart data of the mall.
That is, three types of Shopping Cart data exist.
- User's shopping cart data in the mall
- User shopping cart data for self-service orders in offline stores
- Shopping Cart data
New demand Solution
When a new requirement arises, to distinguish the shopping cart data, you must create a new guard directly.config/auth.php
Addedshop guard
The Code is as follows.
'guards' => [ 'web' => [ 'driver' => 'session', 'provider' => 'users', ], 'admin' => [ 'driver' => 'session', 'provider' => 'admins', ], 'api' => [ 'driver' => 'passport', 'provider' => 'users', ], 'shop' => [ 'driver' => 'passport', 'provider' => 'users', ], 'clerk' => [ 'driver' => 'passport', 'provider' => 'clerk', ], ],
Problem generation
I thought it would run well, but we ignored a detail,api
Andshop
The two guard providers are the same, because they belong to users, andapi
It is also defined inshop
So when the following code is executed, the problem occurs becauseauth('api')->user()
And then quit.shop guard
The data will also be storedapi guard
.
foreach ($guards as $guard) { if ($user = auth($guard)->user()) { $currentGuard = $guard; break; } }
Solution
Engineers did not find an appropriate method before, so they used loop traversal guards to determine that the current request has been authenticated guard. When new requirements are generated, this method is no longer applicable, however, you cannot make major changes to the current shopping cart package, so you have to find a solution,
Ideas
In Laravelrequest()->user()
Will get the correct authenticatedguard user
Data, so the preparation decided to start from the source code here.
Source code analysis
request()->user()
The actual called code isIlluminate\Http\Request
In classuser()
Method
public function user($guard = null) { return call_user_func($this->getUserResolver(), $guard); }
Tracking source codeIlluminate\Auth\AuthServiceProvider
Class. The Code is as follows:return call_user_func($app['auth']->userResolver(), $guard);
protected function registerRequestRebindHandler() { $this->app->rebinding('request', function ($app, $request) { $request->setUserResolver(function ($guard = null) use ($app) { return call_user_func($app['auth']->userResolver(), $guard); }); }); }
Continue tracing source codeIlluminate\Auth\AuthManager
Class
public function shouldUse($name) { $name = $name ?: $this->getDefaultDriver(); $this->setDefaultDriver($name); $this->userResolver = function ($name = null) { return $this->guard($name)->user(); }; }
This is actually the end. We found thatrequest()->user()
The final code executed is$this->guard($name)->user()
. So we need to checkshouldUser
Where is the method called.
View Source CodeIlluminate\Auth\Middleware\Authenticate
:
protected function authenticate(array $guards) { if (empty($guards)) { return $this->auth->authenticate(); } foreach ($guards as $guard) { if ($this->auth->guard($guard)->check()) { return $this->auth->shouldUse($guard); } } throw new AuthenticationException('Unauthenticated.', $guards); }
The code is basically clear here, And the authenticated user's request will passAuthenticate
Middleware, and set the system's default guard as the guard of the current request.
// Obtain the authenticated user's guard foreach ($ guards as $ guard) {if ($ this-> auth-> guard ($ guard)-> check ()) {return $ this-> auth-> shouldUse ($ guard );}}
Set the authenticated guard as the default guard instead.config('auth.defaults.guard')
Value in
public function shouldUse($name) { $name = $name ?: $this->getDefaultDriver(); $this->setDefaultDriver($name); $this->userResolver = function ($name = null) { return $this->guard($name)->user(); }; }
Final Solution
Therefore, the Guard value of the current request can be obtained directly throughAuthManager
In classgetDefaultDriver()
You can.
if ($defaultGuard = $app['auth']->getDefaultDriver()) { $currentGuard = $defaultGuard; $user = auth($currentGuard)->user();}