phplaravelfactorylaravel-seedinglaravel-factory

How to pass parameters to laravel factory in database seeder?


Is it possible to pass data from seeder to factory?

This is my PictureFactory:

class PictureFactory extends Factory{

    protected $model = Picture::class;

    public function definition($galleryId = null, $news = false){
       if (!is_null($galleryId)){
            $galley = Gallery::find($galleryId);
            $path = 'public/galleries/' . $galley->name;
            $newsId = null;
         }
        if ($news){
            $path = 'public/newsPicture';
            $newsId = News::all()->random(1);
        }

        $pictureName = Faker::word().'.jpg';
        return [
            'userId' => 1,
            'src' =>$this->faker->image($path,400,300, 2, false) ,
            'originalName' => $pictureName,
            'newsId' => $newsId
       ];
    }
}

and I use it like this in database seeder:

News::factory(3)
    ->has(Comment::factory()->count(2), 'comments')
    ->create()
    ->each(function($news) { 
        $news->pictures()->save(Picture::factory(null, true)->count(3)); 
    });

but $galleryId and $news do not pass to PictureFactory. Where did I go wrong? And what should I do? please help me.


Solution

  • This is the sort of thing that factory states were made for. Assuming you are using a current (8.x) version of Laravel, define your factory like this:

    <?php
    
    namespace Database\Factories\App;
    
    use App\Models\{Gallery, News, Picture};
    use Illuminate\Database\Eloquent\Factories\Factory;
    
    class PictureFactory extends Factory
    {
    
        protected $model = Picture::class;
    
        public function definition()
        {
            return [
                'userId' => 1,
                'originalName' => $this->faker->word() . '.jpg',
           ];
        }
    
        public function withGallery($id)
        {
            $gallery = Gallery::findOrFail($id);
            $path = 'public/galleries/' . $gallery->name;
    
            return $this->state([
                'src' => $this->faker->image($path, 400, 300, 2, false),
                'newsId' => null,
            ]);
        }
    
        public function withNews()
        {
             $news = News::inRandomOrder()->first();
             $path = 'public/newsPicture';
    
             return $this->state([
                'src' => $this->faker->image($path, 400, 300, 2, false),
                'newsId' => $news->id,
            ]);
        }
    }
    

    And now you can create your desired models like this:

    Picture::factory()->count(3)->withNews();
    // or
    Picture::factory()->count(3)->withGallery($gallery_id);
    

    I'm not certain, but I believe you should be able to do this to get your desired outcome:

    Picture::factory()
        ->count(3)
        ->withNews()
        ->for(News::factory()->hasComments(2))
        ->create();