iBrand 产品中关于购物车的需求比较复杂,我们基于 overture/laravel-shopping-cart 扩展出了更加符合电商需求的购物车包,之前有文章进行过简单的介绍: Laravel shopping cart : 电商购物车包,线上完美运行中

源码地址: ibrand/laravel-shopping-cart

原需求

最开始扩展这个包时是因为以下需求:

  • 用户登录后的购物车数据需要存储在数据库中。因为客户希望能够直观的看到目前购物车中商品信息,以便推送优惠信息来促使转化。虽然我们按照 GA 的标准把数据传送过去了,但是我们发现 GA中数据并不是非常准确。
  • 用户在商城中的购物车数据
  • 导购使用导购小程序代用户下单或结账时加入的购物车数据,不和用户购物车数据同步。

原解决方案

最初需求出来的时候,我们通过不同的 Guard 来作为用户购物车数据的区分,因为商城和导购是两种不同的用户系统,所以当时在购物车 ServiceProvider 中的代码如下:

            $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.');
}

通过循环遍历目前所有的 Guards 来获取目前请求中用户所属的 guard 值和用户对象,本来在新需求未增加时,一切都运行的挺正常。

新需求

18年新增的需求:

  • 用户门店扫码(二维码或条形码)自助下单的购物车数据要和商城的购物车数据区分

也就是现在存在三种购物车数据类型

  • 用户在商城的购物车数据
  • 用户在线下门店中自助下单的购物车数据
  • 导购的购物车数据

新需求解决方案

新需求出现的时候,为了区分购物车数据,肯定是直接新建一个 guard,所以在 config/auth.php 中添加了 shop guard 代码如下。

'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 两个 guard 的 provider 是一样的,因为都是属于用户的,而 api 又定义在 shop 前面,所以当执行如下代码时会出现问题,因为循环到 auth('api')->user() 的时候就退出了,导致了 shop guard 的数据也会存储成 api guard.

           foreach ($guards as $guard) {
if ($user = auth($guard)->user()) {
$currentGuard = $guard;
break;
}
}

解决方案

之前工程师未发现合适的方法,所以采用了循环遍历 guards 来判断当前请求已认证 guard,当新需求产生后,这个方法不再适用,但是又不能对当前的购物车包进行大改,所以只有再找解决办法,

思路

在 Laravel 中 request()->user() 都会获取到正确已认证的 guard user 数据,所以准备决定从这里的源码入手。

源码分析

request()->user() 实际调用的代码是 Illuminate\Http\Request class 中 user() 方法

    public function user($guard = null)
{
return call_user_func($this->getUserResolver(), $guard);
}

追踪源码到 Illuminate\Auth\AuthServiceProvider class,具体执行的代码为: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);
});
});
}

继续追踪源码到 Illuminate\Auth\AuthManager class

    public function shouldUse($name)
{
$name = $name ?: $this->getDefaultDriver(); $this->setDefaultDriver($name); $this->userResolver = function ($name = null) {
return $this->guard($name)->user();
};
}

到这里其实就结束了,我们发现 request()->user() 最终执行的代码是 $this->guard($name)->user() 。所以我们需要查看下shouldUser 方法是在哪里调用的。

仍然看源码 Illuminate\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);
}

代码最终到这里基本比较清楚了,已认证用户的请求会通过 Authenticate middleware ,并且把系统默认的 guard 设置为当前请求的 guard.

//获取到已认证用户的 guard
foreach ($guards as $guard) {
if ($this->auth->guard($guard)->check()) {
return $this->auth->shouldUse($guard);
}
}

设置已认证 guard 为默认 guard,代替 config('auth.defaults.guard') 中的值

    public function shouldUse($name)
{
$name = $name ?: $this->getDefaultDriver(); $this->setDefaultDriver($name); $this->userResolver = function ($name = null) {
return $this->guard($name)->user();
};
}

最终方案

所以获取到当前请求的 Guard 值,可以直接通过 AuthManager class 中的 getDefaultDriver() 即可。

 
if ($defaultGuard = $app['auth']->getDefaultDriver()) {
$currentGuard = $defaultGuard;
$user = auth($currentGuard)->user();
}

讨论交流



Laravel 获取当前 Guard 分析 —源自电商购物车的实际需求的更多相关文章

  1. 【Redis】Hash常见应用场景 - 电商购物车

    电商购物车 以用户id为key 商品id为field 商品数量为value 购物车操作 [key(用户id),field(商品id),value(数量)] 添加商品 -> hset cart: ...

  2. 短视频APP+不同类型社交应用发展分析+化妆品电商

    短视频APP——昙花一现还是发展趋势? 在这个互联网与科技并行且飞速发展的时代,各种app不断涌入市场,其中短视频app便是一个典型,美拍,就成功入围2014年十大最火app.而短视频app也势必要成 ...

  3. 走进Vue时代进阶篇(01):重构电商购物车模块

    前言 从这篇文章开始,我准备给大家分享一些关于Vue.js这门框架的技巧性系列文章,正好我们公司项目中也用到了Vue.所以,教是最好的学.进阶篇比较适合于二三线城市,还在小厂打拼的童鞋们.欢迎你们跟着 ...

  4. iOS 电商购物车倒计时时间计算

    /** * 倒计时 * * @param endTime 截止的时间戳 * * @return 返回的剩余时间 */ - (NSString*)remainingTimeMethodAction:(l ...

  5. Demo—cookie电商购物车

    说明:cookie的操作须有域名,简单点说就是需要用发布的方式去访问,查看cookie信息请用开发者模式进入application栏 1.页面布局(结构)(根目录) 商品列表 <!doctype ...

  6. 使用“消息服务框架”(MSF)实现分布式事务的三阶段提交协议(电商创建订单的示例)

    1,示例解决方案介绍 在上一篇 <消息服务框架(MSF)应用实例之分布式事务三阶段提交协议的实现>中,我们分析了分布式事务的三阶段提交协议的原理,现在我们来看看如何使用消息服务框架(MSF ...

  7. 开发者如何快速搭建自己的电商App?

    面向电商购物场景,HMS Core提供了创新的电商解决方案,帮助应用快速获客.提升转化率,实现业务增长.为了帮助开发者了解如何在电商购物类应用中集成HMS Core的各项能力,HMS Core开发了电 ...

  8. SpringBoot+Neo4j在社交电商中,讲述你是怎么被绑定为下线的

    上两篇文章我们主要讲解了Neo4j的基本知识以及Neo4j的基本使用,这篇文章我们就以实例来深入的理解一下,我们以社交电商中的绑定关系为例,使用SpringBoot+Neo4j来实现. Neo4j文章 ...

  9. 中国社交电商最新展望,S-KOL-C正突围而出

    编辑 | 韩星 出品 | 于见(mpyujian) 通信技术.移动互联网的快速发展正加速国内电商平台的深度变革. 在这场以"社交"和"下沉市场"为关键词的电商之 ...

随机推荐

  1. maven mirrorOf

    转载 http://blog.csdn.net/isea533/article/details/21560089 在maven中配置一个mirror时,mirror通常会设置成*,还有可能是一个具体的 ...

  2. 浅谈ORM操作

    2. ORM(对象关系映射) 1. 映射的关系 DB ORM 数据表 <--> 类 数据行 <--> 对象 字段 <--> 属性 2. Django项目使用MySQ ...

  3. 内置函数_eval

    eval功能:将字符串str当成有效的表达式来求值并返回计算结果. 语法: eval(source[, globals[, locals]]) -> value 参数说明: expression ...

  4. shematool -initschema -dbtype mysql error org.apache.hadoop.hive.metastore.hivemetaexception:Failed to get schema version

    命令:schematool -initSchema -dbType mysql Fix the issue: edit /etc/mysql/my.cnf change bind-address   ...

  5. vue--钩子函数1

    最近在学习自定义指令,这里做个整理 vue允许注册自定义指令,在2.0中,代码复用和抽象的主要形式是组件.然而有的情况下仍需要对普通DOM元素进行底层操作,这时就会用到自定义指令. 全局指令direc ...

  6. ERROR:105: Unable to locate a modulefile for 'xxx'

    查看可用的 module:module avail 将xxx替换为屏幕输出中已有的模块.

  7. Django-1 简介

    1.1 MVC与MTV模型 MVCWeb服务器开发领域里著名的MVC模式,所谓MVC就是把Web应用分为模型(M),控制器(C)和视图(V)三层,他们之间以一种插件式的.松耦合的方式连接在一起,模型负 ...

  8. Java 读写Properties配置文件【转】

    1.Properties类与Properties配置文件 Properties类继承自Hashtable类并且实现了Map接口,也是使用一种键值对的形式来保存属性集.不过Properties有特殊的地 ...

  9. formatter easyui 重命名

    function for_method(){    datagridmethod = $('#formethod_datagrid').datagrid({            url : 'loc ...

  10. WSGI学习系列Paste

    Paste has been under development for a while, and has lots of code in it. The code is largely decoup ...