Search code examples
phplaravellaravel-seeding

Laravel seeder loop with recursive table is unable to see inserted data after every increment


I am creating a Laravel factory for creating product models and then using a seeder file to create 100 product factories. The product model has a self referencing column and I know that if there are no product models in the database that this column should be null. But after that I want to grab a random product id and throw that in the column. I'm checking product model count and if it's greater than 0 I can grab a random product else just set the column to null. But while in the seeder loop, every query is returning 0 columns and I cannot figure out why.

This is what I'm attempting to do in the factory.

<?php

use Faker\Generator as Faker;
use Illuminate\Support\Facades\Log;

$factory->define(App\Product::class, function (Faker $faker) {
    // Logging count to see if it's changing.
    Log::debug(App\Product::count());

    // For the first insert I know there are no products
    // so I want to set the first row to null.
    $product = null;
    // While on the second row the count should be 1
    // but it's returning 0 every time in the seeder loop
    if (App\Product::count() < 0) {
        $product = App\Product::all()->random()->id;
    }

    return [
        'model' => $faker->word,
        'monitor_setup_price' => $faker->optional()->randomFloat(2, 0, 1000),
        'monitor_monthly_price' => $faker->optional()->randomFloat(2, 0, 1000),
        'manage_setup_price' => $faker->optional()->randomFloat(2, 0, 1000),
        'manage_monthly_price' => $faker->optional()->randomFloat(2, 0, 1000),
        'maximize_setup_price' => $faker->optional()->randomFloat(2, 0, 1000),
        'maximize_monthly_price' => $faker->optional()->randomFloat(2, 0, 1000),
        'decommissioned' => 0,
        'category_id' => factory(App\Category::class)->create(),
        'vendor_id' => factory(App\Vendor::class)->create(),
        'type_id' => factory(App\Type::class)->create(),
        'product_id' => $product,
        'created_by' => App\User::all()->random()->id,
        'updated_by' => App\User::all()->random()->id,
        'created_at' => $faker->dateTimeBetween('-2 years', '-1 month'),
        'updated_at' => $faker->dateTimeThisMonth()
    ];
});

And the seeder is just running the factory 100 times.

<?php

use Illuminate\Database\Seeder;

class ProductsTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        factory(App\Product::class, 100)->create();
    }
}

My solution would be in the seeder to manually run a for loop. This just isn't as clean to me and I also want to know exactly why code works the way it does.

Side note: my issue is similar to this [ Seed a recursive table using factories in Laravel ] except the solution in here doesn't work.

  • Laravel 5.7
  • PHP 7.3.1

Seeder loop with recursive table is unable to see inserted data after every increment. Why is it that the count is return 0 after every loop?


Solution

  • I'm guessing it's because the factory first creates a builder for the given model a number of times and then creates the collection of models and persist them to the database, so even if you are getting all products on each iteration, any of them has been persisted to the database yet.

    Maybe you could do like

    $factory->define(App\Product::class, function (Faker $faker) use (&$id) {
        return [
            // ...
            'product_id' => rand(1, $id++),
            // ...
        ];
    });
    

    you might also want to have a look at

    https://github.com/laravel/framework/blob/5.7/src/Illuminate/Foundation/helpers.php#L493

    https://github.com/laravel/framework/blob/5.7/src/Illuminate/Database/Eloquent/Factory.php#L255

    https://github.com/laravel/framework/blob/5.7/src/Illuminate/Database/Eloquent/FactoryBuilder.php

    to see the details of the whole thing.