资讯专栏INFORMATION COLUMN

Laravel 获取当前 Guard 分析 —源自电商购物车的实际需求

daydream / 3202人阅读

摘要:因为客户希望能够直观的看到目前购物车中商品信息,以便推送优惠信息来促使转化。用户在商城中的购物车数据导购使用导购小程序代用户下单或结账时加入的购物车数据,不和用户购物车数据同步。

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

</>复制代码

  1. 源码地址: ibrand/laravel-shopping-cart
原需求

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

用户登录后的购物车数据需要存储在数据库中。因为客户希望能够直观的看到目前购物车中商品信息,以便推送优惠信息来促使转化。虽然我们按照 GA 的标准把数据传送过去了,但是我们发现 GA中数据并不是非常准确。

用户在商城中的购物车数据

导购使用导购小程序代用户下单或结账时加入的购物车数据,不和用户购物车数据同步。

原解决方案

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

</>复制代码

  1. $currentGuard = null;
  2. $user = null;
  3. $guards = array_keys(config("auth.guards"));
  4. foreach ($guards as $guard) {
  5. if ($user = auth($guard)->user()) {
  6. $currentGuard = $guard;
  7. break;
  8. }
  9. }
  10. if ($user) {
  11. //The cart name like `cart.{guard}.{user_id}`: cart.api.1
  12. $cart->name($currentGuard.".".$user->id);
  13. }else{
  14. throw new Exception("Invalid auth.");
  15. }

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

新需求

18年新增的需求:

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

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

用户在商城的购物车数据

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

导购的购物车数据

新需求解决方案

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

</>复制代码

  1. "guards" => [
  2. "web" => [
  3. "driver" => "session",
  4. "provider" => "users",
  5. ],
  6. "admin" => [
  7. "driver" => "session",
  8. "provider" => "admins",
  9. ],
  10. "api" => [
  11. "driver" => "passport",
  12. "provider" => "users",
  13. ],
  14. "shop" => [
  15. "driver" => "passport",
  16. "provider" => "users",
  17. ],
  18. "clerk" => [
  19. "driver" => "passport",
  20. "provider" => "clerk",
  21. ],
  22. ],
问题产生

本以为会运行良好,但是我们忽略了一个细节,apishop 两个 guard 的 provider 是一样的,因为都是属于用户的,而 api 又定义在 shop 前面,所以当执行如下代码时会出现问题,因为循环到 auth("api")->user() 的时候就退出了,导致了 shop guard 的数据也会存储成 api guard.

</>复制代码

  1. foreach ($guards as $guard) {
  2. if ($user = auth($guard)->user()) {
  3. $currentGuard = $guard;
  4. break;
  5. }
  6. }
解决方案

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

思路

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

源码分析

request()->user() 实际调用的代码是 IlluminateHttpRequest class 中 user() 方法

</>复制代码

  1. public function user($guard = null)
  2. {
  3. return call_user_func($this->getUserResolver(), $guard);
  4. }

追踪源码到 IlluminateAuthAuthServiceProvider class,具体执行的代码为:return call_user_func($app["auth"]->userResolver(), $guard);

</>复制代码

  1. protected function registerRequestRebindHandler()
  2. {
  3. $this->app->rebinding("request", function ($app, $request) {
  4. $request->setUserResolver(function ($guard = null) use ($app) {
  5. return call_user_func($app["auth"]->userResolver(), $guard);
  6. });
  7. });
  8. }

继续追踪源码到 IlluminateAuthAuthManager class

</>复制代码

  1. public function shouldUse($name)
  2. {
  3. $name = $name ?: $this->getDefaultDriver();
  4. $this->setDefaultDriver($name);
  5. $this->userResolver = function ($name = null) {
  6. return $this->guard($name)->user();
  7. };
  8. }

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

仍然看源码 IlluminateAuthMiddlewareAuthenticate:

</>复制代码

  1. protected function authenticate(array $guards)
  2. {
  3. if (empty($guards)) {
  4. return $this->auth->authenticate();
  5. }
  6. foreach ($guards as $guard) {
  7. if ($this->auth->guard($guard)->check()) {
  8. return $this->auth->shouldUse($guard);
  9. }
  10. }
  11. throw new AuthenticationException("Unauthenticated.", $guards);
  12. }

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

</>复制代码

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

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

</>复制代码

  1. public function shouldUse($name)
  2. {
  3. $name = $name ?: $this->getDefaultDriver();
  4. $this->setDefaultDriver($name);
  5. $this->userResolver = function ($name = null) {
  6. return $this->guard($name)->user();
  7. };
  8. }
最终方案

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

</>复制代码

  1. if ($defaultGuard = $app["auth"]->getDefaultDriver()) {
  2. $currentGuard = $defaultGuard;
  3. $user = auth($currentGuard)->user();
  4. }
讨论交流

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/28477.html

相关文章

  • Laravel shopping cart : 电商物车包,线上完美运行中

    摘要:购物车在电商场景中基本是必须的一个模块,我们基于进行扩展开发。主要实现了以下扩展购物车数据支持存储增加属性返回。因为购物车可能是或者,因此直接通过属性直接返回相关对象。支持多因为在产品有商城购物车和导购购物车。 我们秉承得益开源社区,也奉献开源社区的原则,我们会陆续将正在线上使用的稳定包提交到 github 上,同时在后续的开源产品中,也会用到,大家可以放心使用。 购物车在电商场景中基...

    孙淑建 评论0 收藏0
  • Laravel 教程 - 实战 iBrand 开源电商 API 系统

    摘要:最佳实践良好的编码规范单元测试持续集成文档,从一开始就形成良好的编码习惯。真实的电商业务所有的业务需求来自真实的客户,并且线上良好运营中。 重要通知: Laravel + 小程序的开源电商版本源码已经在 github 上拉,欢迎提交 issue 和 star :) 开源电商 Server 端: Laravel API源码 开源电商 client 端:小程序源码 iBrand 简介...

    iOS122 评论0 收藏0
  • iBrand 产品工具包:Laravel Database Logger

    摘要:社交新零售电商产品从年月启动至今,已经趋于稳定,而且已经初步得到市场的检验,特别能抗住电商中秒杀时高并发的交易场景。产品包含微商城,小程序,导购小程序端,因此是前后端完全分离的,在这种情况下,没有一个跟踪分析执行效率的工具。 iBrand 社交新零售电商产品从2016年9月启动至今,已经趋于稳定,而且已经初步得到市场的检验,特别能抗住电商中秒杀时高并发的交易场景。 接下来我们团队会逐步...

    Simon 评论0 收藏0
  • Laravel核心解读--用户认证系统实现细节

    摘要:通过装载看守器和用户提供器装载看守器和用户提供器用到的方法比较多,用文字描述不太清楚,我们通过注解这个过程中用到的方法来看具体的实现细节。 用户认证系统的实现细节 上一节我们介绍来Laravel Auth系统的基础知识,说了他的核心组件都有哪些构成,这一节我们会专注Laravel Auth系统的实现细节,主要关注Auth也就是AuthManager是如何装载认证用的看守器(Guard)...

    NicolasHe 评论0 收藏0
  • 电商物车产品在做什么?

    摘要:本文由网易跨境电商部产品经理曹宏授权发表。根据交易链路的环节,分工可以进一步简单细化为商详页产品导购页产品,购物车产品,订单产品,支付产品。那么购物车的产品都在做什么为了避免假大空,就以购物车改版这个项目介绍一下电商购物车产品的工作。 本文由网易跨境电商部产品经理曹宏授权发表。 作为电商交易链路的产品,很多人会问,你们都在做什么? 关于电商产品的工作分类,从流量角度我简单分为以下几类(...

    shixinzhang 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<