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 02 PHP
PHP中改变图片的尺寸大小的代码
Jul 17 PHP
详解php的魔术方法__get()和__set()使用介绍
Sep 19 PHP
PHP连接MySQL查询结果中文显示乱码解决方法
Oct 25 PHP
php配合jquery实现增删操作具体实例
Dec 12 PHP
php中socket通信机制实例详解
Jan 03 PHP
PHP+MYSQL实现用户的增删改查
Mar 24 PHP
php封装的连接Mysql类及用法分析
Dec 10 PHP
PHP使用fopen与file_get_contents读取文件实例分享
Mar 04 PHP
详解Yii2 定制表单输入字段的标签和样式
Jan 04 PHP
PHP使用new StdClass()创建空对象的方法分析
Jun 06 PHP
PHP执行linux命令6个函数代码实例
Nov 24 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
比较discuz和ecshop的截取字符串函数php版
2012/09/03 PHP
根据ip调用新浪api获取城市名并转成拼音
2014/03/07 PHP
10个超级有用的PHP代码片段果断收藏
2015/09/23 PHP
php语言注释,单行注释和多行注释
2018/01/21 PHP
PHP微信发送推送消息乱码的解决方法
2019/02/28 PHP
实例化php类时传参的方法分析
2020/06/05 PHP
document.getElementBy(&quot;id&quot;)与$(&quot;#id&quot;)有什么区别
2013/09/22 Javascript
JS兼容浏览器的导出Excel(CSV)文件的方法
2014/05/03 Javascript
PageSwitch插件实现100种不同图片切换效果
2015/07/28 Javascript
C++中的string类的用法小结
2015/08/07 Javascript
js 截取或者替换字符串中的数字实现方法
2016/06/13 Javascript
基于vue.js实现图片轮播效果
2016/12/01 Javascript
ActiveX控件的使用-js实现打印超市小票功能代码详解
2017/11/22 Javascript
微信小程序登录换取token的教程
2018/05/31 Javascript
详解Vue CLI3 多页应用实践和源码设计
2018/08/30 Javascript
webpack-url-loader 解决项目中图片打包路径问题
2019/02/15 Javascript
Vue Components 数字键盘的实现
2019/09/18 Javascript
实例讲解JavaScript 计时事件
2020/07/04 Javascript
Vue封装全局过滤器Filters的步骤
2020/09/16 Javascript
用Python编写一个每天都在系统下新建一个文件夹的脚本
2015/05/04 Python
Python时间戳使用和相互转换详解
2017/12/11 Python
python 计算数组中每个数字出现多少次--“Bucket”桶的思想
2017/12/19 Python
python opencv之SURF算法示例
2018/02/24 Python
python 通过logging写入日志到文件和控制台的实例
2018/04/28 Python
Windows下Python3.6安装第三方模块的方法
2018/11/22 Python
在django-xadmin中APScheduler的启动初始化实例
2019/11/15 Python
python 写函数在一定条件下需要调用自身时的写法说明
2020/06/01 Python
Python3安装模块报错Microsoft Visual C++ 14.0 is required的解决方法
2020/07/28 Python
canvas实现飞机打怪兽射击小游戏的示例代码
2018/07/09 HTML / CSS
Oakley官网:运动太阳镜、雪镜和服装
2016/09/30 全球购物
美津浓美国官网:Mizuno美国
2018/08/07 全球购物
泰国网上购物:Shopee泰国
2018/09/14 全球购物
计算机应用应届生求职信
2014/07/12 职场文书
2014基建处领导班子“四风”对照检查材料思想汇报
2014/10/04 职场文书
公证处委托书
2015/01/28 职场文书
董事长致辞
2015/07/29 职场文书