Laravel 自动转换长整型雪花 ID 为字符串的实现


Posted in PHP onOctober 27, 2020

在设计 API 时,出于安全性等因素考虑,有时需要放弃使用自增 ID,使 ID 非连续且不可猜测。通常可以使用 Hash id,UUID,雪花 ID 等来实现。

在最近的一个项目中,我尝试使用雪花 ID。一通折腾下来发现,逼格挺高,实现也挺简单。然而当我继续撸起袖子与前端部分对接时,却出现了 JS 精度丢失问题,因为存储的 ID 是一个 unsigned bigint 型的值。(至于为什么会有精度丢失现象,这里就不具体解释了,不清楚的可以自行搜索),本文主要介绍解决办法。

想要解决这问题,基本原理也很简单,就是把 ID 转成字符串再返回给前端。

错误尝试

一开始我想到的是使用 Laravel Eloquent 模型的模型访问器。只要给需要转换的模型加一个 getIdAttribute,将 ID 转成字符串不就行了嘛?

如:AppModelsUser 模型里这样写:

/**
 * @return string
 */
public function getIdAttribute()
{
  return strval($this->attributes['id']);
}

但事实并非如此,属性访问器确实能让 API 返回给前端的 ID 变为字符串。但同时也会影响关联模型插入、修改时的结果,例如,user 关联的了 post 模型,使用 $user->posts()->saveMany(...); 这种方式保存的新的 posts 记录,对应的 user_id 会为空。

这也不难理解,因为模型访问器是要参与模型相关处理的,访问器将 ID 由数字转为了字符串,自然会导致数据错乱。

正确姿势

冷静下来决定先认真思考再动手,查阅了官方文档,才发现 Resource 正是我想要的。Resource 只会影响返回给前端的数据,我们可以通过自定义 Resource 来实现 API 返回结果的结构、类型转换等功能。转换个 ID 自然也不在话下。

为了省事,我直接修改 AppHttpResource 这个基类。只需要重载它的 toArray() 方法,在其中使用递归,对可能超出 JS 安全数值范围的值进行转换就可以了。大家也可以根据自己的实际情况,新建 Resource 类,如 UserResource 来处理。

<?php

namespace App\Http\Resources;

use Illuminate\Http\Resources\Json\JsonResource;

class Resource extends JsonResource
{
  /**
   * Transform the resource into an array.
   *
   * @param \Illuminate\Http\Request $request
   *
   * @return array
   */
  public function toArray($request)
  {
    $parentReturn = parent::toArray($request);

    foreach (array_keys($parentReturn) as $key) {
      // 为方便演示这里把所有整型字段都转成字符串
      if (is_int($parentReturn[$key])) {
        $parentReturn[$key] = strval($parentReturn[$key]);
      }

      // 关联的字段,如 $user->post,相当于递归处理
      if (is_array($parentReturn[$key])) {
        $parentReturn[$key] = new Resource($parentReturn[$key]);
      }
    }

    return $parentReturn;
  }
}

然后,在接口控制器中返回 Resource 返回数据,整型字段值就会自动变为字符串了。

<?php

namespace App\Http\Controllers;

use App\Http\Resources\Resource;
use App\Models\User;
use Illuminate\Http\Request;

class TestController extends Controller
{
  /**
   * @return \App\Http\Resources\Resource
   */
  public function __invoke(Request $request)
  {
    $user = User::first();

    return new Resource($user);
  }
}

结果如下图:

Laravel 自动转换长整型雪花 ID 为字符串的实现

注意事项

因为这种办法使用了遍历,而且有递归处理,当数据结构复杂、数据量较大时可能会对性能造成一定影响。我这里算是比较偷懒取巧的写法,如果对性能有追求,自定义 Resource 类,然后根据特定的已知的字段名来进行转换会比较好
因为返回给前端的 ID 转为了字符串,前端在进行比较判断,特别是 === 判断时要特别注意

到此这篇关于Laravel 自动转换长整型雪花 ID 为字符串的实现的文章就介绍到这了,更多相关Laravel 长整型雪花ID转换为字符串内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

PHP 相关文章推荐
php实现的遍历文件夹下所有文件,编辑删除
Jan 05 PHP
php读取xml实例代码
Jan 28 PHP
WordPress判断用户是否登录的代码
Mar 17 PHP
PHP设计模式之代理模式的深入解析
Jun 13 PHP
PHP源码分析之变量的存储过程分解
Jul 03 PHP
PHP递归复制、移动目录的自定义函数分享
Nov 18 PHP
PHP对文件进行加锁、解锁实例
Jan 23 PHP
php禁止某ip或ip地址段访问的方法
Feb 25 PHP
php好代码风格的阶段性总结
Jun 25 PHP
PHP实现的数独求解问题示例
Apr 18 PHP
在云虚拟主机部署thinkphp5项目的步骤详解
Dec 21 PHP
PHP实现浏览器格式化显示XML的方法示例
Jan 22 PHP
Laravel配合jwt使用的方法实例
Oct 25 #PHP
数据结构之利用PHP实现二分搜索树
Oct 25 #PHP
如何运行/调试你的PHP代码
Oct 23 #PHP
php redis setnx分布式锁简单原理解析
Oct 23 #PHP
PHP如何通过带尾指针的链表实现'队列'
Oct 22 #PHP
php使用event扩展的io复用测试的示例
Oct 20 #PHP
Aliyun Linux 编译安装 php7.3 tengine2.3.2 mysql8.0 redis5的过程详解
Oct 20 #PHP
You might like
javascript 建设银行登陆键盘
2008/06/10 Javascript
JQuery 学习笔记 选择器之五
2009/07/23 Javascript
跨浏览器的 mouseenter mouseleave 以及 compareDocumentPosition的使用说明
2010/05/04 Javascript
javascript跑马灯悬停放大效果实现代码
2012/12/12 Javascript
JavaScript不使用prototype和new实现继承机制
2014/12/29 Javascript
浅谈js中的闭包
2015/03/16 Javascript
JQuery插入DOM节点的方法
2015/06/11 Javascript
jQuery中的ready函数与window.onload谁先执行
2016/06/21 Javascript
jQuery弹出窗口打开链接的实现代码
2016/12/24 Javascript
JS实现类似百叶窗下拉菜单效果
2016/12/30 Javascript
如何正确理解javascript的模块化
2017/03/02 Javascript
解决Jquery下拉框数据动态获取的问题
2018/01/25 jQuery
在Vuex使用dispatch和commit来调用mutations的区别详解
2018/09/18 Javascript
js变量值传到php过程详解 将php解析成数据
2019/06/26 Javascript
javascript使用Blob对象实现的下载文件操作示例
2020/04/18 Javascript
Vue自定义表单内容检查rules实例
2020/10/30 Javascript
讲解Python中for循环下的索引变量的作用域
2015/04/15 Python
django 实现电子支付功能的示例代码
2018/07/25 Python
Python 使用类写装饰器的小技巧
2018/09/30 Python
python opencv读mp4视频的实例
2018/12/07 Python
Django调用百度AI接口实现人脸注册登录代码实例
2020/04/23 Python
Python如何实现后端自定义认证并实现多条件登陆
2020/06/22 Python
Python实现画图软件功能方法详解
2020/07/28 Python
PyTorch中Tensor的数据类型和运算的使用
2020/09/03 Python
任意一块网页内容实现“活”的背景(目前火狐浏览器专有)
2014/05/07 HTML / CSS
adidas旗下高尔夫装备供应商:TaylorMade Golf(泰勒梅高尔夫)
2016/08/28 全球购物
Vans英国官方网站:美国南加州的原创极限运动潮牌
2017/01/20 全球购物
匡威西班牙官网:Converse西班牙
2019/10/01 全球购物
WEB控件可以激发服务端事件,请谈谈服务端事件是怎么发生并解释其原理?自动传回是什么?为什么要使用自动传回?
2012/02/21 面试题
给校长的建议书100字
2014/05/16 职场文书
乡镇安全生产目标责任书
2014/07/23 职场文书
党课培训心得体会
2014/09/02 职场文书
老公给老婆的检讨书(精华篇)
2014/10/18 职场文书
武侯祠导游词
2015/02/04 职场文书
撤诉申请怎么写
2015/05/19 职场文书
springboot为异步任务规划自定义线程池的实现
2022/06/14 Java/Android