Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

Nathan's avatar

How do I seed many-to-many polymorphic relationships?

Let's just use the examples in the documentation.

posts
    id - integer
    name - string

videos
    id - integer
    name - string

tags
    id - integer
    name - string

taggables
    tag_id - integer
    taggable_id - integer
    taggable_type - string
class Post extends Eloquent {

    public function tags()
    {
        return $this->morphToMany('App\Tag', 'taggable');
    }

}

class Video extends Eloquent {

    public function tags()
    {
        return $this->morphToMany('App\Tag', 'taggable');
    }

}

class Tag extends Eloquent {

    public function posts()
    {
        return $this->morphedByMany('App\Post', 'taggable');
    }

    public function videos()
    {
        return $this->morphedByMany('App\Video', 'taggable');
    }

}

The seeder:


use Illuminate\Database\Seeder; use App\Post; use App\Tag; class PostTableSeeder extends Seeder { public function run() { // use the factory to create a Faker\Generator instance $faker = Faker\Factory::create(); foreach ((range(1, 20)) as $index) { $post = Post::create([ 'name' => $faker->unique()->name ]); // Do what here? $post->tags } } }
0 likes
16 replies
JarekTkaczyk's avatar

What is your question exactly? Seed them just like any other table. You can use Eloquent for this or simple inserts, whatever suits you.

Nathan's avatar

How do I seed them through the PostTableSeeer or VideoTableSeeder so that the taggable table is populated? The way I would normally create a seeder doesn't apply because I don't have a Taggable model. I'm sure it's not a complex answer, just one I don't have.

bestmomo's avatar
Level 52

Something like that :


public function run() { // use the factory to create a Faker\Generator instance $faker = Faker\Factory::create(); foreach ((range(1, 20)) as $index) { $post = Post::create([ 'name' => $faker->unique()->name ]); $post = Video::create([ 'name' => $faker->unique()->name ]); $post = Tag::create([ 'name' => $faker->unique()->name ]); } foreach ((range(1, 20)) as $index) { DB::table('taggables')->insert( [ 'tag_id' => rand(1, 20), 'taggable_id' => rand(1, 20), 'taggable_type' => rand(0, 1) == 1 ? 'App\Post' : 'App\Video' ] ); } }
4 likes
Nathan's avatar

Thank you, that's the way I thought I'd have to do it but I wasn't sure if there was a more eloquent method. Admittedly, using the DB facade is a much simpler way.

JarekTkaczyk's avatar

@Nathan not only simpler way, but also much faster. It doesn't matter if you have 20 rows, but try with 500 and you will know what I'm talking about.

1 like
akael's avatar

@Nathan

Something I do to cleanup the code a bit is to create a Trait with the following function:

public function tags()
    {
        return $this->morphToMany('App\Tag', 'taggable');
    }

And use the trait in your models instead of repeating the same relationship block over and over for each model that is taggable.

Then if you ever want to make changes like adding timestamps to your pivot table you would only need to add the code to the trait.

I had a situation recently where the client wanted me to show the most recently tagged items, and the project had about 15 models which allowed tags to be applied. Using a trait made that much easier than digging into all models to see which ones were taggable.

Nathan's avatar

One thing I'm having trouble with is retrieving data on the relationship.

$postTags == Post::find(1)->tags;
dd($postTags);

This code only nets me an empty collection when I'm sure there is an entry (tag_id = 1, taggable_id = 1, taggable_type = App\Post).

I've watched it in a lesson before but I can't recall which one and I'm not sure if it's different from 4.2 to 5.0 but how do I dump all of the queries being performed?

Edit: Maybe I should create another topic for that issue if I can't figure it out. For searchability's sake.

bestmomo's avatar

Maybe this is better :

$postTags = Post::find(1)->tags;
Nathan's avatar

The second = was just a typo as I retyped it into the thread. My code is written as yours.

Nathan's avatar

I'm still getting empty collections. I'm pretty tired so I'll do some troubleshooting in the morning once I'm rested and then get back to you guys on what my problem was. I'll have to dig into the framework classes and get a better understanding of eloquent.

browner12's avatar

It looks like this question was answered before factories may have been in use. What I would suggest now, and something I've had good luck with, is using the factory types.

So make a base factory

$factory->define(App\Models\Tag::class, function (Faker\Generator $faker) {

    $taggable = [
        \App\Models\Video::class,
        \App\Models\Post::class,
    ];

    return [
        'taggable_id'   => 1,
        'taggable_type' => $faker->randomElement($taggable),
    ];
});

and then create specific factories for your different polymorphic relationships.

$factory->defineAs(App\Models\Tag::class, 'video', function (Faker\Generator $faker) use ($factory) {

    $follow = $factory->raw(App\Models\Tag::class);

    $extras = [
        'taggable_id'   => $faker->randomElement(\App\Models\Video::pluck('id')->toArray()),
        'taggable_type' => \App\Models\Video::class,
    ];

    return array_merge($follow, $extras);
});
$factory->defineAs(App\Models\Tag::class, 'post', function (Faker\Generator $faker) use ($factory) {

    $follow = $factory->raw(App\Models\Tag::class);

    $extras = [
        'taggable_id'   => $faker->randomElement(\App\Models\Post::pluck('id')->toArray()),
        'taggable_type' => \App\Models\Post::class,
    ];

    return array_merge($follow, $extras);
});

and then in your seed file, you can create those specific Tags

//video tags
factory(App\Models\Tag::class, 'video', 20)->create();

//post tags
factory(App\Models\Tag::class, 'post', 20)->create();

the code may be a tiny bit off, because I just adapted this from one of mine, but it should give you the general idea.

hope this helps!

6 likes
nitaalexandr's avatar

Can you guys stop copy+paste the solutions from Stackoverflow or try to explain them at least? For example, where this code should be added:

$factory->define(App\Models\Tag::class, function (Faker\Generator $faker) {...

Please or to participate in this conversation.