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 相关文章推荐
在windows iis5下安装php4.0+mysql之我见
Oct 09 PHP
PHP+JS无限级可伸缩菜单详解(简单易懂)
Jan 02 PHP
CI框架中集成CKEditor编辑器的教程
Jun 09 PHP
php使用递归函数实现数字累加的方法
Mar 16 PHP
WordPress中获取指定分类及其子分类下的文章数目
Dec 31 PHP
ThinkPHP中order()使用方法详解
Apr 19 PHP
php 如何获取文件的后缀名
Jun 05 PHP
php自动载入类用法实例分析
Jun 24 PHP
php实现批量删除挂马文件及批量替换页面内容完整实例
Jul 08 PHP
PHP在innodb引擎下快速代建全文搜索功能简明教程【基于xunsearch】
Oct 14 PHP
PHP数据库操作三:redis用法分析
Aug 16 PHP
tp5修改(实现即点即改)
Oct 18 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
全国FM电台频率大全 - 27 陕西省
2020/03/11 无线电
用 php 编写的日历
2006/10/09 PHP
php顺序查找和二分查找示例
2014/03/27 PHP
PHP数据库万能引擎类adodb配置使用以及实例集锦
2014/06/12 PHP
PHP面向对象编程之深入理解方法重载与方法覆盖(多态)
2015/12/24 PHP
php网页版聊天软件实现代码
2016/08/12 PHP
使用javascript访问XML数据的实例
2006/12/27 Javascript
如何确保JavaScript的执行顺序 之实战篇
2011/03/03 Javascript
nodejs批量修改文件编码格式
2015/01/22 NodeJs
JavaScript学习笔记之Function对象
2015/01/22 Javascript
分享9点个人认为比较重要的javascript 编程技巧
2015/04/27 Javascript
jQuery实现的多滑动门,多选项卡效果代码
2016/03/28 Javascript
Js类的静态方法与实例方法区分及jQuery拓展的两种方法
2016/06/03 Javascript
BootStrap table表格插件自适应固定表头(超好用)
2016/08/24 Javascript
浅谈javascript中遇到的字符串对象处理
2016/11/18 Javascript
JS实现页面进入和返回定位到具体位置
2016/12/08 Javascript
javascript中数组(Array)对象和字符串(String)对象的常用方法总结
2016/12/15 Javascript
javascript过滤数组重复元素的实现方法
2017/05/03 Javascript
Angular 2父子组件数据传递之@Input和@Output详解 (上)
2017/07/05 Javascript
Vue2.0基于vue-cli+webpack父子组件通信(实例讲解)
2017/09/14 Javascript
vue的一个分页组件的示例代码
2017/12/25 Javascript
使用Vue开发动态刷新Echarts组件的教程详解
2018/03/22 Javascript
微信小程序返回上一页传参并刷新过程解析
2019/12/13 Javascript
JavaScript 引用类型实例详解【数组、对象、严格模式等】
2020/05/13 Javascript
Selenium chrome配置代理Python版的方法
2018/11/29 Python
python 五子棋如何获得鼠标点击坐标
2019/11/04 Python
五分钟学会怎么用Pygame做一个简单的贪吃蛇
2021/01/06 Python
phonegap常用事件总结(必看篇)
2017/03/31 HTML / CSS
GANT葡萄牙官方商店:拥有美国运动服传统的生活方式品牌
2018/10/18 全球购物
国家励志奖学金个人先进事迹材料
2014/05/04 职场文书
离婚起诉书范本
2015/05/18 职场文书
考试后的感想
2015/08/07 职场文书
学校运动会感想
2015/08/10 职场文书
教你漂亮打印Pandas DataFrames和Series
2021/05/29 Python
ORM模型框架操作mysql数据库的方法
2021/07/25 MySQL
spring cloud eureka 服务启动失败的原因分析及解决方法
2022/03/17 Java/Android