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 04 PHP
php利用iframe实现无刷新文件上传功能的代码
Sep 29 PHP
php设计模式小结
Feb 15 PHP
PHP CURL获取返回值的方法
May 04 PHP
解密ThinkPHP3.1.2版本之独立分组功能应用
Jun 19 PHP
在Win7 中为php扩展配置Xcache
Oct 08 PHP
php获取textarea的值并处理回车换行的方法
Oct 20 PHP
通过php添加xml文档内容的方法
Jan 23 PHP
总结PHP中数值计算的注意事项
Aug 14 PHP
php实现等比例不失真缩放上传图片的方法
Nov 14 PHP
php递归函数怎么用才有效
Feb 24 PHP
PHP小程序支付功能完整版【基于thinkPHP】
Mar 26 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
PHP错误和异长常处理总结
2014/03/06 PHP
php 使用GD库为页面增加水印示例代码
2014/03/24 PHP
关于 Laravel Redis 多个进程同时取队列问题详解
2017/12/25 PHP
Javascript的getYear、getFullYear、getUTCFullYear异同分享
2011/11/30 Javascript
js 编码转换 gb2312 和 utf8 互转的2种方法
2013/08/07 Javascript
jquery插件jquery倒计时插件分享
2013/12/27 Javascript
javascript获取web应用根目录的方法
2014/02/12 Javascript
轻量级的原生js日历插件calendar.js使用指南
2015/04/28 Javascript
JQuery中模拟image的ajaxPrefilter与ajaxTransport处理
2015/06/19 Javascript
javascript创建对象的几种模式介绍
2016/05/06 Javascript
javascript实现数据双向绑定的三种方式小结
2017/03/09 Javascript
javascript 中Cookie读、写与删除操作
2017/03/29 Javascript
JQuery form表单提交前验证单选框是否选中、删除记录时验证经验总结(整理)
2017/06/09 jQuery
理解 Node.js 事件驱动机制的原理
2017/08/16 Javascript
vue实现登陆登出的实现示例
2017/09/15 Javascript
原生JS检测CSS3动画是否结束的方法详解
2019/01/27 Javascript
layui table表格数据的新增,修改,删除,查询,双击获取行数据方式
2019/11/14 Javascript
JavaScript代码异常监控实现过程详解
2020/02/17 Javascript
[02:36]DOTA2上海特锦赛 回忆电竞生涯的重要瞬间
2016/03/25 DOTA
[15:20]DOTA2-DPC中国联赛 正赛 Elephant vs Aster 选手采访
2021/03/11 DOTA
基于进程内通讯的python聊天室实现方法
2015/06/28 Python
Python中内置数据类型list,tuple,dict,set的区别和用法
2015/12/14 Python
Python使用正则表达式过滤或替换HTML标签的方法详解
2017/09/25 Python
深入理解Python中的super()方法
2017/11/20 Python
python统计字母、空格、数字等字符个数的实例
2018/06/29 Python
Python 多个图同时在不同窗口显示的实现方法
2019/07/07 Python
matplotlib jupyter notebook 图像可视化 plt show操作
2020/04/24 Python
怎么快速自学python
2020/06/22 Python
Python 在局部变量域中执行代码
2020/08/07 Python
10种CSS3实现的loading动画,挑一个走吧?
2020/11/16 HTML / CSS
通过HTML5规范搞定i、em、b、strong元素的区别
2017/03/04 HTML / CSS
EGO Shoes美国/加拿大:英国时髦鞋类品牌
2018/08/04 全球购物
公司会计岗位职责
2014/02/13 职场文书
2014年国庆节演讲稿
2014/09/19 职场文书
幼儿园个人总结
2015/02/28 职场文书
招商银行工作证明
2015/06/17 职场文书