简体   繁体   中英

Laravel 8 Multiple Relationships for Factory

In Laravel 8 it is possible to quickly fill relationships with factories. However, I cannot figure out how to generate more than one relationship. How can I create a random or new relationship for each link using the new Laravel 8 syntax?

This factory syntax is only available in Laravel 8. https://laravel.com/docs/8.x/database-testing#factory-relationships

Problem

Consider the following relationship:

  • Each link belongs to a website and a post.
  • Both websites and posts can have many links.
<?php

class Post extends Model
{
    use HasFactory;

    function links()
    {
        return $this->hasMany(Link::class);
    }
}

class Website extends Model
{
    use HasFactory;

    function links()
    {
        return $this->hasMany(Link::class);
    }
}

class Link extends Model
{
    use HasFactory;

    function post()
    {
        return $this->belongsTo(Post::class);
    }

    function website()
    {
        return $this->belongsTo(Website::class);
    }
}


What I tried/want

What I tried below will only generate one model for all the links. How can I create a random or new relationship for each link using the new Laravel 8 syntax?

Link::factory()->count(3)->forPost()->forWebsite()->make()

=> Illuminate\Database\Eloquent\Collection {#4354
     all: [
       App\Models\Link {#4366
         post_id: 1,
         website_id: 1,
       },
       App\Models\Link {#4395
         post_id: 1, // return a different ID
         website_id: 1,
       },
       App\Models\Link {#4370
         post_id: 1, // return a different ID
         website_id: 1, // return a different ID
       },
     ],
   }

Just add this to your LinkFactory :

  public function definition()
  {
    return [
        'post_id' => function () {
            return Post::factory()->create()->id;
        },

        .....
    ];
}

And now you can create new Post for each new Link:

Link::factory()->count(3)->create();//Create 3 links with 3 new posts

or attach new Links to existing Post:

Link::factory()->count(3)->create(['post_id' => Post::first()->id]); //create 3 links and 0 new posts

The laravel magic factory method for allows you to populate the database with one record from the foreign table. See link to documentation https://laravel.com/docs/8.x/database-testing#belongs-to-relationships

In your case, using forPost() and forWebsite() will allow you to populate the database with one id from the Post table and the Website table.

If you want to use different IDs use this syntax instead Link::factory()->count(3)->make()

\App\Models\Category::factory(10)
->has(Product::factory()->count(10), 'products')
->create();

Had a similar problem and was only able to get it working when I attached within the afterCreating() on a single factory. This allows me to create/store the id of each model and then attach to the Link model

I'm choosing to start with WebsiteFactory but you can also start with PostFactory since those are the "highest parent" models. If you try to make a Link without the website_id and the post_id I believe you will get a error asking for both.

class WebsiteFactory extends Factory
{
    public function definition(){...}

    public function configure()
    {
       return $this->afterCreating( function (Website $website){
           // the website model is created, hence after-creating

           // attach Website to a new Post
           $post = Post::factory()->hasAttached($website)->create();
           
           // create new links to attach to both
           $links = Link::factory()->for($website)->for($post)->count(3)->create();

    });

You can leave PostFactory and LinkFactory as simple definition blocks (or add other stuff if you wanted). Now when you create a new Website via factory, it will create a new post and 3 new links. For example, you can now run

php artisan tinker

$w = Website::factory()->create();     // one website-one post-3 links
$ws = Website::factory()->count(5)->create();    // 5 website-5 post- 15 links

Check out the Factory Callbacks here (9.x docs, but they are in 8.x too):

https://laravel.com/docs/9.x/database-testing#factory-callbacks

It would be better if you play around with code. You will understand better.

$user = User::factory()
            ->has(Post::factory()->count(3), 'posts')
            ->create();

The above code will create three post for a single user. It will insert three post row and a user row. On the other hand the code below, seems three post will be inserted for user with name Jessica Aercher, that is it won't insert a user.

$posts = Post::factory()
            ->count(3)
            ->for(User::factory()->state([
                'name' => 'Jessica Archer',
            ]))
            ->create();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM