我正在开发服务的后端,用户可以在其中设置多个其他实体(多媒体、活动、其他用户)的“喜欢”(在我的例子中称为“检查”)。
这些检查对于每个可检查实体和检查执行者必须是唯一的,如下所示:
Schema::create('checks', function (Blueprint $table) {
// Artificial auto-increment primary key (PM is set by default using increments() method)
$table->increments('id');
$table->unsignedInteger('user_id');
// Automatically adds checkable_id and checkable_type
// checkable_type represents the table in which search for the checkable_id
// and can be equal to 'multimedia', 'users', 'activities' or 'tags'
$table->morphs('checkable');
// Setup creation timestamp functionality
// updated_at is not useful here, that's why we are not using timestamps()
$table->timestamp('created_at')->nullable();
$table->unique(['user_id', 'checkable_id', 'checkable_type']);
});
当我测试系统时一切正常,但是当我尝试使用工厂用随机数据填充数据库时遇到了一些问题。我在工厂中强制执行唯一性约束,但有时我使用的代码没有捕获重复的选择,并且我收到一个违反唯一性约束的 PDO 异常。这里的执行代码:
$factory->define(Check::class, function (Faker\Generator $faker) {
// Get the checker user
$checkUserId = User::withTrashed()->get()->random()->id;
// Chose where the check have been done
$checkableType = $faker->randomElement(['multimedia', 'users', 'activities']);
// Get all checkables of the specified type
$checkables = call_user_func('App\\' . studly_case(str_singular($checkableType)) .
'::withTrashed');
// Apply type-related restrictions
switch ($checkableType) {
// Attachments multimedia and categories avatars cannot be checked
case 'multimedia':
$checkables = $checkables->where('model_type', '!=', 'messages')
->where('model_type', '!=', 'categories');
break;
// An user cannot check himself
case 'users':
$checkables = $checkables->where('id', '!=', $checkUserId);
break;
}
// Retrieves data from database
$checkables = $checkables->get();
// Picks a random checkable id until it finds one not already checked by the current user
do {
$checkableId = $checkables->random()->id;
$check = DB::table('checks')->where([
['checkable_id', $checkableId],
['checkable_type', $checkableType],
['user_id', $checkUserId]
])->first();
} while ($check != null);
return [
'user_id' => $checkUserId,
'checkable_id' => $checkableId,
'checkable_type' => $checkableType,
'created_at' => $faker->dateTime,
];
});
我尝试了两种不同的方式并遇到了两个不同的问题。
第一个选项:
for ($i = 0; $i < self::CHECK_NUM; $i++) {
factory(Check::class)->create();
}
通过这种方式,对于唯一性约束,一切似乎都可以正常工作,但是如果要创建的元素太多(目前它试图生成 1900 个随机检查),生成只会在某个点停止,而在 1300 个元素之后的某处没有说什么(它永远不会相同的号码)。我通过使用第二个 SSH 连接登录并在播种发生时直接监视数据库上的检查号发现了这一点:它一直在增长,在某些时候它只是停止了,但播种机仍在运行。
第二种选择:
factory(Check::class, self::CHECK_NUM)->create();
我最喜欢这个,但在某些时候它只是抛出一个像这样的 PDO 错误
Integrity constraint violation: 1062 Duplicate entry '22-44-activities' for key 'checks_user_id_checkable_id_checkable_type_unique'
并停止播种机。重复与以前相同的过程,我看到数据库中的检查次数保持为 0,直到发生错误,然后它变成一个可变数(大约 150)。我猜 Laravel 内部并没有真正在数据库上写入记录,直到它完成生成它们,但是通过一些调试,我发现我放置的约束执行代码实际上在某些时候可以防止重复,这意味着数据库外观可以看到以某种方式创建但尚未写入的记录,这让我更加困惑为什么检查不能一直工作(据我所知,生成脚本不是在多线程上运行)。
关于 Laravel 如何低级管理这些事情或如何解决这些问题的任何想法/见解?我对第二个选项特别感兴趣,但如果我也能理解第一个选项的问题,我会更高兴:D