自动递增整数不能用于存在潜在冲突(冲突)的分布式数据库拓扑中的主键。
关于 UUID 与自动递增整数主题的现有文献非常多,基本原则已被广泛理解。然而,与此同时,似乎没有关于如何在 Laravel 中实现这一点的单一、全面的解释,支持 Eloquent 模型和关系。
以下文章很有值(value),并解释了在 VARCHAR(36) 中存储主键所产生的性能开销。/CHAR(36)与通常用于自动递增键的 4/8 字节整数相比。我们应该注意这个建议(尤其是作者在全文中指出的出版后更正):
https://tomharrisonjr.com/uuid-or-guid-as-primary-keys-be-careful-7b2aa3dcb439
同样有值(value)的是来自讨论的评论,它是广泛的:
https://news.ycombinator.com/item?id=14523523
下面的文章解释了如何在 Laravel Eloquent 模型中使用 UUID 实现主键,但它没有解释如何为 Eloquent 关系实现相同的主键,例如“数据透视表”的多对多(按照 Laravel 的说法)。
https://medium.com/@steveazz/setting-up-uuids-in-laravel-5-552412db2088
其他人也问过类似的问题,比如Laravel eloquent UUID in a pivot table ,但在那种情况下,提问者正在使用 MySQL 触发器生成要插入到数据透视表中的 UUID,我宁愿避免这种做法,而是采用纯粹的 Eloquent 方法。
另一个类似的问题是在 How To Cast Eloquent Pivot Parameters? 提出的,但问题的关键是如何转换枢轴属性,而不是如何在附加或同步关系时为 ID 列生成自定义值。
需要明确的是,我们可以通过将可选的数组参数传递给 attach() 来轻松实现这一点。方法:
->attach($modelAId, $modelBId, ['id' => Uuid::generate()]);
attach() 时都必须这样做。在任何一种模型上,这都很麻烦并且违反了 DRY 原则。最佳答案
免责声明:这是一个正在进行的工作 .到目前为止,这种技术只关注多对多 Eloquent 关系,而不是更奇特的类型,例如 Has-Many-Through 或 Polymorphics。
当前 Laravel v5.5.*
Laravel 的 UUID 生成包
在开始之前,我们需要一种机制来生成 UUID。
最流行的 UUID 生成包如下:
https://github.com/webpatser/laravel-uuid
为 Eloquent 模型实现 UUID
模型使用 UUID 作为其主键的能力可以通过扩展 Laravel 的基本 Model 类或通过实现 trait 来授予。每种方法都有其优点和缺点,并且因为 Steve Azzopardi 的 medium.com 文章(上面引用)已经解释了 trait 方法(尽管它早于 Eloquent 的 $keyType = 'string'; 属性),我将演示模型扩展方法,当然,可以很容易地适应一个特征。
无论我们使用模型还是特征,关键方面是 $incrementing = false;和 protected $keyType = 'string'; .由于 PHP 的单继承设计,扩展基本 Model 类会带来限制,但它消除了在每个应该使用 UUID 主键的模型中包含这两个关键属性的需要。相比之下,当使用 trait 时,忘记在每个使用该 trait 的模型中都包含这两者会导致失败。
基本 UUID 模型类:
<?php
namespace Acme\Rocket\Models;
use Illuminate\Database\Eloquent\Model;
use Webpatser\Uuid\Uuid;
class UuidModel extends Model
{
public $incrementing = false;
protected $keyType = 'string';
public function __construct(array $attributes = [])
{
parent::__construct($attributes);
}
public static function boot()
{
parent::boot();
self::creating(function ($model) {
$model->{$model->getKeyName()} = Uuid::generate()->string;
});
}
}
User和 Role ,它们以多对多的能力相关。User模型:<?php
namespace Acme\Rocket\Models;
use Acme\Rocket\Models\UuidModel;
class User extends UuidModel
{
public function __construct(array $attributes = [])
{
parent::__construct($attributes);
}
}
id列将自动填充新生成的 UUID。id ) 的自动递增,并将其类型从 int 更改为至 string ,就像我们在 UuidModel 中所做的那样类,以上。<?php
namespace Acme\Rocket\Models;
use Illuminate\Database\Eloquent\Relations\Pivot;
class RoleUser extends Pivot
{
public $incrementing = false;
protected $keyType = 'string';
}
User ) 模型:<?php
namespace Acme\Rocket\Models;
use Webpatser\Uuid\Uuid;
use Illuminate\Database\Eloquent\Model;
use Acme\Rocket\Models\UuidModel;
use Acme\Rocket\Models\Role;
use Acme\Rocket\Models\RoleUser;
class User extends UuidModel
{
protected $fillable = ['name'];
public function __construct(array $attributes = [])
{
parent::__construct($attributes);
}
public function roles()
{
return $this->belongsToMany(Role::class)
->using(RoleUser::class);
}
public function newPivot(Model $parent, array $attributes, $table, $exists, $using = NULL) {
$attributes[$this->getKeyName()] = Uuid::generate()->string;
return new RoleUser($attributes, $table, $exists);
}
}
roles() 中的自定义枢轴模型。方法,->using(RoleUser::class) ,以及 newPivot()方法覆盖;两者都是将 UUID 插入到数据透视表的 id 所必需的。每当模型为 attach()编。Role模型,本质上是相同的,但多对多关系颠倒了:<?php
namespace Acme\Rocket\Models;
use Webpatser\Uuid\Uuid;
use Illuminate\Database\Eloquent\Model;
use Acme\Rocket\Models\UuidModel;
use Acme\Rocket\Models\User;
use Acme\Rocket\Models\RoleUser;
class Role extends UuidModel
{
protected $fillable = ['name'];
public function __construct(array $attributes = [])
{
parent::__construct($attributes);
}
public function users()
{
return $this->belongsToMany(User::class)
->using(RoleUser::class);
}
public function newPivot(Model $parent, array $attributes, $table, $exists, $using = NULL) {
$attributes[$this->getKeyName()] = Uuid::generate()->string;
return new RoleUser($attributes, $table, $exists);
}
}
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
//use Webpatser\Uuid\Uuid;
use Acme\Rocket\Models\User;
use Acme\Rocket\Models\Role;
class UuidTest extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('users', function (Blueprint $table) {
$table->uuid('id');
$table->primary('id');
$table->string('name');
$table->timestamps();
});
Schema::create('roles', function (Blueprint $table) {
$table->uuid('id');
$table->primary('id');
$table->string('name');
$table->timestamps();
});
Schema::create('role_user', function (Blueprint $table) {
$table->uuid('id');
$table->primary('id');
$table->unique(['user_id', 'role_id']);
$table->string('user_id');
$table->string('role_id');
});
$user = User::create([
'name' => 'Test User',
]);
$role = Role::create([
'name' => 'Test Role',
]);
// The commented portion demonstrates the inline equivalent of what is
// happening behind-the-scenes.
$user->roles()->attach($role->id/*, ['id' => Uuid::generate()->string]*/);
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('role_users');
Schema::drop('users');
Schema::drop('roles');
}
}
role_user表看起来像这样:MariaDB [laravel]> SELECT * FROM `role_user`;
+--------------------------------------+--------------------------------------+--------------------------------------+
| id | user_id | role_id |
+--------------------------------------+--------------------------------------+--------------------------------------+
| 6f7b3820-6b48-11e8-8c2c-1b181bec620c | 6f76bf80-6b48-11e8-ac88-f93cf1c70770 | 6f78e070-6b48-11e8-8b2c-8fc6cc4722fc |
+--------------------------------------+--------------------------------------+--------------------------------------+
1 row in set (0.00 sec)
>>> (new \Acme\Rocket\Models\User)->first()->with('roles')->get();
=> Illuminate\Database\Eloquent\Collection {#2709
all: [
Acme\Rocket\Models\User {#2707
id: "1d8bf370-6b1f-11e8-8c9f-8b67b13b054e",
name: "Test User",
created_at: "2018-06-08 13:23:21",
updated_at: "2018-06-08 13:23:21",
roles: Illuminate\Database\Eloquent\Collection {#2715
all: [
Acme\Rocket\Models\Role {#2714
id: "1d8d4310-6b1f-11e8-9c1b-d33720d21f8c",
name: "Test Role",
created_at: "2018-06-08 13:23:21",
updated_at: "2018-06-08 13:23:21",
pivot: Acme\Rocket\Models\RoleUser {#2712
user_id: "1d8bf370-6b1f-11e8-8c9f-8b67b13b054e",
role_id: "1d8d4310-6b1f-11e8-9c1b-d33720d21f8c",
id: "89658310-6b1f-11e8-b150-bdb5619fb0a0",
},
},
],
},
},
],
}
sync() , syncWithoutDetaching() , 和 toggle() ,虽然我没有彻底测试它们。VARCHAR(36) 之间转换的一些挑战。/CHAR(36)字符串和 BINARY(16)表示。显然,后者要快得多。关于php - 对于 Laravel Eloquent 模型及其关系,如何实现由 UUID 组成的主键,而不是自动递增的整数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50766476/
很好奇,就使用rubyonrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提
关闭。这个问题需要detailsorclarity.它目前不接受答案。想改进这个问题吗?通过editingthispost添加细节并澄清问题.关闭8年前。Improvethisquestion为什么SecureRandom.uuid创建一个唯一的字符串?SecureRandom.uuid#=>"35cb4e30-54e1-49f9-b5ce-4134799eb2c0"SecureRandom.uuid方法创建的字符串从不重复?
我收到这个错误:RuntimeError(自动加载常量Apps时检测到循环依赖当我使用多线程时。下面是我的代码。为什么会这样?我尝试多线程的原因是因为我正在编写一个HTML抓取应用程序。对Nokogiri::HTML(open())的调用是一个同步阻塞调用,需要1秒才能返回,我有100,000多个页面要访问,所以我试图运行多个线程来解决这个问题。有更好的方法吗?classToolsController0)app.website=array.join(',')putsapp.websiteelseapp.website="NONE"endapp.saveapps=Apps.order("
我的问题的一个例子是体育游戏。一场体育比赛有两支球队,一支主队和一支客队。我的事件记录模型如下:classTeam"Team"has_one:away_team,:class_name=>"Team"end我希望能够通过游戏访问一个团队,例如:Game.find(1).home_team但我收到一个单元化常量错误:Game::team。谁能告诉我我做错了什么?谢谢, 最佳答案 如果Gamehas_one:team那么Rails假设您的teams表有一个game_id列。不过,您想要的是games表有一个team_id列,在这种情况下
HashMap中为什么引入红黑树,而不是AVL树呢1.概述开始学习这个知识点之前我们需要知道,在JDK1.8以及之前,针对HashMap有什么不同。JDK1.7的时候,HashMap的底层实现是数组+链表JDK1.8的时候,HashMap的底层实现是数组+链表+红黑树我们要思考一个问题,为什么要从链表转为红黑树呢。首先先让我们了解下链表有什么不好???2.链表上述的截图其实就是链表的结构,我们来看下链表的增删改查的时间复杂度增:因为链表不是线性结构,所以每次添加的时候,只需要移动一个节点,所以可以理解为复杂度是N(1)删:算法时间复杂度跟增保持一致查:既然是非线性结构,所以查询某一个节点的时候
我们目前正在为ROR3.2开发自定义cms引擎。在这个过程中,我们希望成为我们的rails应用程序中的一等公民的几个类类型起源,这意味着它们应该驻留在应用程序的app文件夹下,它是插件。目前我们有以下类型:数据源数据类型查看我在app文件夹下创建了多个目录来保存这些:应用/数据源应用/数据类型应用/View更多类型将随之而来,我有点担心应用程序文件夹被这么多目录污染。因此,我想将它们移动到一个子目录/模块中,该子目录/模块包含cms定义的所有类型。所有类都应位于MyCms命名空间内,目录布局应如下所示:应用程序/my_cms/data_source应用程序/my_cms/data_ty
📢博客主页:https://blog.csdn.net/weixin_43197380📢欢迎点赞👍收藏⭐留言📝如有错误敬请指正!📢本文由Loewen丶原创,首发于CSDN,转载注明出处🙉📢现在的付出,都会是一种沉淀,只为让你成为更好的人✨文章预览:一.分辨率(Resolution)1、工业相机的分辨率是如何定义的?2、工业相机的分辨率是如何选择的?二.精度(Accuracy)1、像素精度(PixelAccuracy)2、定位精度和重复定位精度(RepeatPrecision)三.公差(Tolerance)四.课后作业(Post-ClassExercises)视觉行业的初学者,甚至是做了1~2年
我最喜欢的Google文档功能之一是它会在我工作时不断自动保存我的文档版本。这意味着即使我在进行关键更改之前忘记在某个点进行保存,也很有可能会自动创建一个保存点。至少,我可以将文档恢复到错误更改之前的状态,并从该点继续工作。对于在MacOS(或UNIX)上运行的Ruby编码器,是否有具有等效功能的工具?例如,一个工具会每隔几分钟自动将Gitcheckin我的本地存储库以获取我正在处理的文件。也许我有点偏执,但这点小保险可以让我在日常工作中安心。 最佳答案 虚拟机有些人可能讨厌我对此的回应,但我在编码时经常使用VIM,它具有自动保存功
我有很多这样的文档:foo_1foo_2foo_3bar_1foo_4...我想通过获取foo_[X]的所有实例并将它们中的每一个替换为foo_[X+1]来转换它们。在这个例子中:foo_2foo_3foo_4bar_1foo_5...我可以用gsub和一个block来做到这一点吗?如果不是,最干净的方法是什么?我真的在寻找一个优雅的解决方案,因为我总是可以暴力破解它,但我觉得有一些正则表达式技巧值得学习。 最佳答案 我(完全)不懂Ruby,但类似这样的东西应该可以工作:"foo_1foo_2".gsub(/(foo_)(\d+)/
如果names为nil,则以下中断。我怎样才能让这个map只有在它不是nil时才执行?self.topics=names.split(",").mapdo|n|Topic.where(name:n.strip).first_or_create!end 最佳答案 其他几个选项:选项1(在其上执行map时检查split的结果):names_list=names.try(:split,",")self.topics=names_list.mapdo|n|Topic.where(name:n.strip).first_or_create!e