Laravel Factory:列的手动增量
Laravel Factory: Manual Increment of Column
对于以下工厂定义,order
列需要按顺序排列。已经有一个自动递增的列 id
。第一行的 order
应该从 1
开始,每一行的 order
应该是下一个数字(1
、2
、3
等.)
$factory->define(App\AliasCommand::class, function (Faker\Generator $faker) {
return [
'user_id' => App\User::inRandomOrder()->first()->id,
'command' => $faker->word,
'content' => $faker->sentence,
'order' => (App\AliasCommand::count()) ?
App\AliasCommand::orderBy('order', 'desc')->first()->order + 1 : 1
];
});
应该将 order
列设置为比上一行多 1,但是,它会导致所有行都被分配 1
。
这可能有用。
$factory->define(App\AliasCommand::class, function (Faker\Generator $faker) {
static $order = 1;
return [
'user_id' => App\User::inRandomOrder()->first()->id,
'command' => $faker->word,
'content' => $faker->sentence,
'order' => $order++
];
});
它只是在该函数内部保留一个计数器。
更新:
Laravel 8 介绍了新工厂 类 所以这个请求变成:
class AliasCommandFactory extends Factory {
private static $order = 1;
protected $model = AliasCommand::class;
public function definition() {
$faker = $this->faker;
return [
'user_id' => User::inRandomOrder()->first()->id,
'command' => $faker->word,
'content' => $faker->sentence,
'order' => self::$order++
];
}
}
要实现真正的 autoIncrement 而不是使用这种方法:
$__count = App\AliasCommand::count();
$__lastid = $__count ? App\AliasCommand::orderBy('order', 'desc')->first()->id : 0 ;
$factory->define(App\AliasCommand::class,
function(Faker\Generator $faker) use($__lastid){
return [
'user_id' => App\User::inRandomOrder()->first()->id,
'command' => $faker->word,
'content' => $faker->sentence,
'order' => $faker->unique()->numberBetween($min=$__lastid+1, $max=$__lastid+25),
/* +25 (for example here) is the number of records you want to insert
per run.
You can set this value in a config file and get it from there
for both Seeder and Factory ( i.e here ).
*/
];
});
@apokryfos 的回答是一个很好的解决方案,如果您确定工厂模型生成只会按顺序 运行 并且您不关心预先存在的数据。
但是,这可能会导致 order
值不正确,例如,如果您想生成要插入到测试数据库中的模型,其中一些记录已经存在。
对列值使用闭包,我们可以更好地自动化序列顺序。
$factory->define(App\AliasCommand::class, function (Faker\Generator $faker) {
return [
'user_id' => App\User::inRandomOrder()->first()->id,
'command' => $faker->word,
'content' => $faker->sentence,
'order' => function() {
$max = App\AliasCommand::max('order'); // returns 0 if no records exist.
return $max+1;
}
];
});
您的示例几乎是正确的,问题是您在定义工厂时运行执行order
值 而不是上面的代码,它在生成单个模型时执行 。
根据同样的原则,您还应该将 user_id
代码包含在一个闭包中,否则您所有的工厂生成的模型将具有相同的用户 ID。
该解决方案还解决了已经在table条件下的数据:
class UserFactory extends Factory
{
/**
* @var string
*/
protected $model = User::class;
/**
* @var int
*/
protected static int $id = 0;
/**
* @return array
*/
public function definition()
{
if ( self::$id == 0 ) {
self::$id = User::query()->max("id") ?? 0;
// Initialize the id from database if exists.
// If conditions is necessary otherwise it would return same max id.
}
self::$id++;
return [
"id" => self::$id,
"email" => $this->faker->email,
];
}
}
对于以下工厂定义,order
列需要按顺序排列。已经有一个自动递增的列 id
。第一行的 order
应该从 1
开始,每一行的 order
应该是下一个数字(1
、2
、3
等.)
$factory->define(App\AliasCommand::class, function (Faker\Generator $faker) {
return [
'user_id' => App\User::inRandomOrder()->first()->id,
'command' => $faker->word,
'content' => $faker->sentence,
'order' => (App\AliasCommand::count()) ?
App\AliasCommand::orderBy('order', 'desc')->first()->order + 1 : 1
];
});
应该将 order
列设置为比上一行多 1,但是,它会导致所有行都被分配 1
。
这可能有用。
$factory->define(App\AliasCommand::class, function (Faker\Generator $faker) {
static $order = 1;
return [
'user_id' => App\User::inRandomOrder()->first()->id,
'command' => $faker->word,
'content' => $faker->sentence,
'order' => $order++
];
});
它只是在该函数内部保留一个计数器。
更新:
Laravel 8 介绍了新工厂 类 所以这个请求变成:
class AliasCommandFactory extends Factory {
private static $order = 1;
protected $model = AliasCommand::class;
public function definition() {
$faker = $this->faker;
return [
'user_id' => User::inRandomOrder()->first()->id,
'command' => $faker->word,
'content' => $faker->sentence,
'order' => self::$order++
];
}
}
要实现真正的 autoIncrement 而不是使用这种方法:
$__count = App\AliasCommand::count();
$__lastid = $__count ? App\AliasCommand::orderBy('order', 'desc')->first()->id : 0 ;
$factory->define(App\AliasCommand::class,
function(Faker\Generator $faker) use($__lastid){
return [
'user_id' => App\User::inRandomOrder()->first()->id,
'command' => $faker->word,
'content' => $faker->sentence,
'order' => $faker->unique()->numberBetween($min=$__lastid+1, $max=$__lastid+25),
/* +25 (for example here) is the number of records you want to insert
per run.
You can set this value in a config file and get it from there
for both Seeder and Factory ( i.e here ).
*/
];
});
@apokryfos 的回答是一个很好的解决方案,如果您确定工厂模型生成只会按顺序 运行 并且您不关心预先存在的数据。
但是,这可能会导致 order
值不正确,例如,如果您想生成要插入到测试数据库中的模型,其中一些记录已经存在。
对列值使用闭包,我们可以更好地自动化序列顺序。
$factory->define(App\AliasCommand::class, function (Faker\Generator $faker) {
return [
'user_id' => App\User::inRandomOrder()->first()->id,
'command' => $faker->word,
'content' => $faker->sentence,
'order' => function() {
$max = App\AliasCommand::max('order'); // returns 0 if no records exist.
return $max+1;
}
];
});
您的示例几乎是正确的,问题是您在定义工厂时运行执行order
值 而不是上面的代码,它在生成单个模型时执行 。
根据同样的原则,您还应该将 user_id
代码包含在一个闭包中,否则您所有的工厂生成的模型将具有相同的用户 ID。
该解决方案还解决了已经在table条件下的数据:
class UserFactory extends Factory
{
/**
* @var string
*/
protected $model = User::class;
/**
* @var int
*/
protected static int $id = 0;
/**
* @return array
*/
public function definition()
{
if ( self::$id == 0 ) {
self::$id = User::query()->max("id") ?? 0;
// Initialize the id from database if exists.
// If conditions is necessary otherwise it would return same max id.
}
self::$id++;
return [
"id" => self::$id,
"email" => $this->faker->email,
];
}
}