Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Laravel Factory: Manual Increment of Column

For the following factory definition, the column order needs to be sequential. There is already a column id that is auto-incremented. The first row's order should start at 1 and each additional row's order should be the next number (1,2,3, etc.)

$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
    ];
});

It should be setting the order column to be 1 more than the previous row, however, it results in all rows being assigned 1.

like image 607
bnahin Avatar asked Jul 08 '17 07:07

bnahin


Video Answer


3 Answers

Here's something that might work.

$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++
    ];
});

It just keeps a counter internal to that function.

Update:

Laravel 8 introduced new factory classes so this request becomes:

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++
        ];
    }
}
like image 64
apokryfos Avatar answered Sep 16 '22 12:09

apokryfos


The answer by @apokryfos is a good solution if you're sure the factory model generations will only be run in sequential order and you're not concerned with pre-existing data.

However, this can result in incorrect order values if, for example, you want to generate models to be inserted into your test database, where some records already exist.

Using a closure for the column value, we can better automate the sequential 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;
        }
    ];
});

You almost had it right in your example, the problem is that you were running the order value execution at the time of defining the factory rather than the above code, which executes at the time the individual model is generated.

By the same principle, you should also enclose the user_id code in a closure, otherwise all of your factory generated models will have the same user ID.

like image 45
Soulriser Avatar answered Sep 17 '22 12:09

Soulriser


To achieve true autoIncrement rather use this approach:

   $__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 ).
         */
      ];

   });
like image 31
edam Avatar answered Sep 20 '22 12:09

edam