gpt4 book ai didi

php - Laravel:使用 Faker 播种多个独特的列

转载 作者:可可西里 更新时间:2023-11-01 07:05:02 26 4
gpt4 key购买 nike

简介

伙计们,我有一个关于模型工厂和多个唯一列的问题:

背景

我有一个名为 Image 的模型。此模型具有存储在单独模型 ImageText 中的语言支持。 ImageText 有一个 image_id 列、一个语言列和一个文本列。

ImageTextMySQL 中有一个约束,即 image_id 和语言的组合必须是唯一的。

class CreateImageTextsTable extends Migration
{

public function up()
{
Schema::create('image_texts', function ($table) {

...

$table->unique(['image_id', 'language']);

...

});
}

...

现在,我希望每个 Image 在播种完成后有多个 ImageText 模型。使用模型工厂和这个播种机很容易:

factory(App\Models\Image::class, 100)->create()->each(function ($image) {
$max = rand(0, 10);
for ($i = 0; $i < $max; $i++) {
$image->imageTexts()->save(factory(App\Models\ImageText::class)->create());
}
});

问题

但是,当使用模型工厂和 faker 进行播种时,您通常会看到以下消息:

[PDOException]                                                                                                                 
SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '76-gn' for key 'image_texts_image_id_language_unique'

这是因为在某个时刻,在那个 for 循环中,造假者会为一张图像随机生成两次相同的语言代码,从而打破 ['image_id', 'language'] 的唯一约束。

您可以更新您的 ImageTextFactory 以这样说:

$factory->define(App\Models\ImageText::class, function (Faker\Generator $faker) {

return [
'language' => $faker->unique()->languageCode,
'title' => $faker->word,
'text' => $faker->text,
];
});

但是,你反而会遇到这样的问题,即在创建了足够多的图像文本后,造假者将用完语言代码。

当前解决方案

目前,这通过为 ImageText 设置两个不同的工厂来解决,其中一个工厂重置语言代码的唯一计数器,播种器调用工厂重置唯一计数器,然后再进入 for 循环以创建更多的 ImageText。但这是代码重复,应该有更好的方法来解决这个问题。

问题

有没有办法将您正在保存的模型发送到工厂?如果是这样,我可以检查工厂内部以查看当前图像是否已经附加了任何 ImageText,如果没有,则重置 languageCodes 的唯一计数器。我的目标是这样的:

$factory->define(App\Models\ImageText::class, function (Faker\Generator $faker) {

$firstImageText = empty($image->imageTexts());

return [
'language' => $faker->unique($firstImageText)->languageCode,
'title' => $faker->word,
'text' => $faker->text,
];
});

当然目前给出:

[ErrorException]           
Undefined variable: image

是否有可能以某种方式实现这一点?

最佳答案

我解决了

我搜索了很多解决这个问题的方法,发现很多其他人也遇到过。如果你只需要关系另一端的一个元素,it's very straight forward .

“多列唯一限制”的添加使这变得复杂。我找到的唯一解决方案是“忘记 MySQL 限制,只用 PDO 异常的 try-catch 围绕工厂创建”。这感觉像是一个糟糕的解决方案,因为其他 PDOException 也会被捕获,而且感觉不“正确”。

解决方案

为了完成这项工作,我将播种器分为 ImageTableSeeder 和 ImageTextTableSeeder,它们都非常简单。他们的运行命令都是这样的:

public function run()
{
factory(App\Models\ImageText::class, 100)->create();
}

魔术发生在 ImageTextFactory 内部:

$factory->define(App\Models\ImageText::class, function (Faker\Generator $faker) {

// Pick an image to attach to
$image = App\Models\Image::inRandomOrder()->first();
$image instanceof App\Models\Image ? $imageId = $image->id : $imageId = null;

// Generate unique imageId-languageCode combination
$imageIdAndLanguageCode = $faker->unique()->regexify("/^$imageId-[a-z]{2}");
$languageCode = explode('-', $imageIdAndLanguageCode)[1];

return [
'image_id' => $imageId,
'language' => $languageCode,
'title' => $faker->word,
'text' => $faker->text,
];
});

就是这样:

$imageIdAndLanguageCode = $faker->unique()->regexify("/^$imageId-[a-z]{2}");

我们在 regexify 表达式中使用 imageId 并添加我们唯一组合中也包含的任何内容,在本例中用“-”字符分隔。这将生成“841-en”、“58-bz”、“96-xx”等结果,其中 imageId 始终是我们数据库中的真实图像,或者为空。

由于我们将唯一标签与 imageId 一起粘贴到语言代码,我们知道image_id 和 languageCode 的组合将是唯一的。这正是我们所需要的!

现在我们可以简单地提取创建的语言代码,或者我们想要生成的任何其他唯一字段,使用:

$languageCode = explode('-', $imageIdAndLanguageCode)[1];

这种方法有以下优点:

  • 不需要捕捉异常
  • 为了便于阅读,可以将 Factories 和 Seeders 分开
  • 代码紧凑

这里的缺点是您只能生成其中一个键可以表示为正则表达式的组合键。只要可能,这似乎是解决此问题的好方法。

关于php - Laravel:使用 Faker 播种多个独特的列,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43202886/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com