phplaravelfaker

How to pass array parameter from seeder into Factory?


In laravel 9 app I need to create dummy data with factory with aften repeating url for using in groups. So in seeder I have :

{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        $itemsCount = 50;
        $faker = \Faker\Factory::create();
        $refUrls = [];

        for ($i = 0; $i < (int) ($itemsCount / 5); $i++) {
            $refUrls[] = $faker->url(); // I generate such array
        }

        BannerClickedCount::factory()
            ->count($itemsCount)
            ->create(['refUrls'=> $refUrls]);

and pass it to factory as parameter, but it does not work as BannerClickedCount model has no refUrls field and I can not use it as passed parameter into database/factories/BannerClickedCountFactory.php:

class BannerClickedCountFactory extends Factory
{
    /**
     * The name of the factory's corresponding model.
     *
     * @var string
     */
    protected $model = BannerClickedCount::class;
    
    /**
     * Define the model's default state.
     *
     * @return array
     */
    public function definition($refUrls = []) // That is empty array
    {
        \Log::info(varDump($refUrls, ' -1 extends Factory $refUrls::'));
        ...

How can I pass array parameter from seeder into Factory ?

ADDTIVE CODE : I tried to modify my code, but got error :

  current(): Argument #1 ($array) must be of type array, string given

  at database/seeders/banner_clicked_countsWithInitData.php:32
     28▕         }
     29▕         BannerClickedCount::factory()->count($itemsCount)
     30▕             ->state(new Sequence(
     31▕             // generate a sequence of reference URLs from the array
  ➜  32▕                 fn () => current($refUrls),
     33▕                 $refUrls
     34▕             ))
     35▕             ->create([]);
     36▕         /* BannerClickedCount::factory()

  1   database/seeders/banner_clicked_countsWithInitData.php:32

ADDTIVE CODE # 2:

$refUrls in my code is an array of strings :

$refUrls[] = $faker->url();

So I think I do not need explode be used, but with I remade seeder file :

$itemsCount = 10;
$faker           = \Faker\Factory::create();
$refUrls = [];
for($i = 0; $i< (int)($itemsCount/2) ; $i++) {
    $refUrls[] = $faker->url();
}
\Log::info(varDump($refUrls, ' -1 RUN Factory $refUrls::'));

BannerClickedCount::factory()->count($itemsCount)
    ->state(new Sequence(
        current($refUrls),
        $refUrls
    ))
    ->create([]); // Error pointing at this line !

I got error :

  array_merge(): Argument #2 must be of type array, string given

  at vendor/laravel/framework/src/Illuminate/Database/Eloquent/Factories/Factory.php:438
    434▕             if ($state instanceof Closure) {
    435▕                 $state = $state->bindTo($this);
    436▕             }
    437▕ 
  ➜ 438▕             return array_merge($carry, $state($carry, $parent));
    439▕         }, $this->definition());
    440▕     }
    441▕ 
    442▕     /**

      +8 vendor frames 
  9   [internal]:0
      Illuminate\Database\Eloquent\Factories\Factory::Illuminate\Database\Eloquent\Factories\{closure}()

      +2 vendor frames 
  12  database/seeders/banner_clicked_countsWithInitData.php:52
...

How to fix it ?


Solution

  • I salved the isue with defining custom method setRefUrl in factory :

    class BannerClickedCountFactory extends Factory
    {
        protected $model = BannerClickedCount::class;
    
        public function definition($refUrls = [])
        {
            $locales = config('translatable.locales', []);
            return [
                'banner_id' => $this->faker->randomElement(Banner::all())['id'],
                'locale' => $this->faker->randomElement($locales),
                'user_id' => $this->faker->randomElement([1, 2, 3, 4, 5]),
                'ip'        => '127.0.0.1',
                'created_at' => $this->faker
                    ->dateTimeBetween('-1 week', '-1 minute')
                    ->format('Y-m-d G:i'),
            ];
        }
        public function setRefUrl($refUrls)
        {
            return $this->state(function (array $attributes) use($refUrls) {
                return [
                    'ref_url' => $this->faker->randomElement($refUrls)
                ];
            });
        }
    

    and calling it in the seeder :

    $itemsCount = 200;
    $faker           = \Faker\Factory::create();
    $refUrls = [];
    for($i = 0; $i< (int)($itemsCount/20) ; $i++) {
        $refUrls[] = $faker->url();
    }
    BannerClickedCount::factory()->count($itemsCount)->setRefUrl($refUrls)