Jam社区产品对购物车的需求更为复杂。我们在overture/laravel-shopping-cart的基础上,扩展了更符合电商需求的购物车包,之前的一篇文章简单介绍过:Laravelshoppingcart:电商购物车包,完美运行在线源码地址:ibrand/laravel-shopping-cart最初扩展这个包的需求是因为以下需求:users登录后的购物车数据需要存储在数据库中。因为客户希望能够直观的看到当前购物车中的商品信息,从而推送打折信息促进转化。虽然我们按照GA的标准发送了数据,但是我们发现GA里面的数据并不是很准确。商城用户购物车数据导购使用导购小程序代用户下单或结账时添加购物车数据,与用户购物车数据不同步。最初需求原方案的时候,我们使用不同的Guard来区分用户购物车数据,因为商城和导购是两个不同的用户系统,所以当时购物车ServiceProvider中的代码如下:$currentGuard=null;$用户=空;$guards=array_keys(config('auth.guards'));foreach($guardsas$guard){if($user=auth($guard)->user()){$currentGuard=$guard;休息;}}if($user){//购物车名称如`cart.{guard}.{user_id}`:cart.api.1$cart->name($currentGuard.'.'.$user->id);}else{thrownewException('Invalidauth.');}通过循环遍历当前所有的Guard,得到当前请求中用户所属的guard值和用户对象。一切正常。新要求2018年新增要求:门店自助下单扫码(二维码或条码)用户的购物车数据要与商城的购物车数据区分开来,即现在分三个购物车用户在商城购物的购物车数据类型Cartdata用户线下门店自助购物车数据。购物车数据导购新需求解决方案。当有新的需求出现时,为了区分购物车数据,必须直接新建一个守卫,所以在config/auth.php中增加了如下的shopguard代码。'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',],],问题出现了我认为它会很好用,但是我们忽略一个细节,api和shop这两个守卫的provider是一样的,因为都是属于用户的,而api是定义在shop前面的,所以下面代码执行的时候,会出问题,因为auth('api')->user()的循环会退出,导致shopguard数据也会被存储为apiguard。foreach($guardsas$guard){if($user=auth($guard)->user()){$currentGuard=$guard;休息;}}解决方案工程师之前没有找到合适的方法,所以用循环遍历guards判断当前请求已经通过guard认证,当有新的需求产生时,该方法不再适用,但是无法对当前的购物车包做大的改动,只好重新想办法了。思路是Laravel中的request()->user()会得到正确且经过认证的守卫用户数据,所以我决定从这里的源码入手源码分析request()->user()实际调用的代码是Illuminate\Http\中的user()方法publicfunctionuser($guard=null){returncall_user_func($this->getUserResolver(),$guard)请求类;}追源码到Illuminate\Auth\AuthServiceProvider类,具体执行代码为:returncall_user_func($app['auth']->userResolver(),$guard);protectedfunctionregisterRequestRebindHandler(){$this->app->rebinding('request',function($app,$request){$request->setUserResolver(function($guard=null)use($app){returncall_user_func($app['auth']->userResolver(),$guard);});});}继续追源码到Illuminate\Auth\AuthManager类publicfunctionshouldUse($name){$name=$name?:$this->getDefaultDriver();$this->setDefaultDriver($name);$this->userResolver=function($name=null){返回$this->guard($name)->user();};}到这里其实就结束了,我们找到request()->user()最后执行的代码是$this->guard($name)->user()所以我们需要检查shouldUser方法是在哪里调用的。还是看源码Illuminate\Auth\Middleware\Authenticate:protectedfunctionauthenticate(array$guards){if(empty($guards)){return$this->auth->authenticate();}foreach($guardsas$guard){if($this->auth->guard($guard)->check()){返回$this->auth->shouldUse($guard);}}thrownewAuthenticationException('Unauthenticated.',$guards);}代码到这里基本就清楚了,认证用户的请求会通过Authenticate中间件,系统默认的守卫设置为当前请求的守卫。//获取认证用户的守卫foreach($guardsas$guard){if($this->auth->guard($guard)->check()){返回$this->auth->shouldUse($guard);}}将经过身份验证的守卫设置为默认守卫而不是config('auth.defaults.guard')publicfunctionshouldUse($name){$name=$name?:$this->getDefaultDriver();$this->setDefaultDriver($name);$this->userResolver=function($name=null){return$this->guard($name)->user();}};}最终解决方案所以要获取当前请求的Guard值,可以直接在AuthManager类中传递getDefaultDriver()if($defaultGuard=$app['auth']->getDefaultDriver()){$currentGuard=$defaultGuard;$user=auth($currentGuard)->user();}讨论交流
