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

JensRoland's avatar

Bug in Many to Many Seeding?

Hi. I am trying to create a many-to-many relation between Pages and Tags as per the official documentation of polymorphic many to many relations (https://laravel.com/docs/5.2/eloquent-relationships#many-to-many-polymorphic-relations), but the Seeder keeps trying to shove my tag name field into the pivot table where it doesn't belong?

When I try to seed the taggables table, I get the following error:

 [Illuminate\Database\QueryException]
  SQLSTATE[42S22]: Column not found: 1054 Unknown column 'name' in 'field list' (SQL: insert into `taggables` (`created_at`,
  `id`, `name`, `tag_id`, `taggable_id`, `taggable_type`, `updated_at`) values (2016-08-22 20:04:17, 3, recusandae, 2, 1, Bal
  coni\Page, 2016-08-22 20:04:17))

At first I thought it was happening because I had defined the name field as unique, but I've since changed that so now all the tables use the simple auto-incrementing id and no other indexes. And still, the Laravel Seeder thinks it should put the name property into the pivot table? What the...?

Any ideas? Note that I am on the dev (5.3) branch.

The Seeder looks like this:

class TaggedEntitiesSeeder extends Seeder
{
    public function run()
    {
        App\Tag::truncate();
        App\Page::truncate();

        // Create some Tags
        factory(App\Tag::class, 10)->create();

        // Create some Pages and add a number of Tags to each
        factory(App\Page::class, 50)->create()->each(function($bar) {
            $bar->tags()->sync(
                App\Tag::all()->random( rand(1,5) )->toArray()
            );
        });
    }
}

The Migrations and Models look like:

        Schema::create('pages', function (Blueprint $table) {
            $table->increments('id');
            $table->string('slug')->unique();
            $table->string('title');
            $table->text('content');
            $table->timestamps();
            $table->timestamp('published_at')->nullable()->index();
        });
        Schema::create('tags', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name');
            $table->timestamps();
        });
        Schema::create('taggables', function (Blueprint $table) {
            $table->increments('id');

            $table->integer('tag_id')->unsigned();
            $table->foreign('tag_id')->references('id')->on('tags')->onDelete('cascade');

            $table->integer('taggable_id')->unsigned();
            $table->foreign('taggable_id')->references('id')->on('pages')->onDelete('cascade');

            $table->string('taggable_type');

            $table->timestamps();
        });
class Page extends Model
{
    protected $dates = ['published_at'];
    public function tags()
    {
        return $this->morphToMany('App\Tag', 'taggable')->withTimestamps();
    }
}
class Tag extends Model
{
    public function pages()
    {
        return $this->morphedByMany('App\Page', 'taggable')->withTimestamps();
    }
}

All the best,

/Jens Roland

0 likes
2 replies
JensRoland's avatar

I tried doing the same with a regular Many-to-Many relation as well, and using the name property on my Tag model as the primary key (since that just seems like a better table design). So:

class Tag extends Model
{
    protected $primaryKey = 'name';
    public $incrementing = false;

    public function pages()
    {
        return $this->belongsToMany('App\Page', 'page_tag', 'tag_name', 'page_id')->withTimestamps();
    }
}

and with the Migrations:

        Schema::create('tags', function (Blueprint $table) {
            $table->string('name');
            $table->timestamps();
            $table->primary('name');
        });

        Schema::create('page_tag', function (Blueprint $table) {
            $table->increments('id');

            $table->string('tag_name');
            $table->foreign('tag_name')->references('name')->on('tags')->onDelete('cascade');

            $table->integer('page_id')->unsigned();
            $table->foreign('page_id')->references('id')->on('pages')->onDelete('cascade');

            $table->timestamps();
        });

This, however, results in the same error:

[Illuminate\Database\QueryException]
  SQLSTATE[42S22]: Column not found: 1054 Unknown column 'name' in 'field list' (SQL: insert into `page_tag` (`created_at`, `
  name`, `page_id`, `tag_name`, `updated_at`) values (2016-08-22 21:18:30, consequatur, 1, 0, 2016-08-22 21:18:30))

Not only that, but Laravel is actually trying to insert the tag name "consequatur" in the non-existing name field while it is trying to insert 0 in the actual foreign key tag_name field!

Frustrated, I go to bed with no solution in sight.

/Jens Roland

JensRoland's avatar

I think I got it to work now.... I took another look at the documentation for the sync method I use in the Seeder class, and noticed that it expects an array of IDs, not entities. I added a ->pluck('name') to the Tag collection in the Seeder, and I am no longer getting an error!

(note that this was using the standard Many-to-Many relation, not the polymorphic one)

class TaggedEntitiesSeeder extends Seeder
{
    public function run()
    {
        App\Tag::truncate();
        App\Page::truncate();

        // Create some Tags
        factory(App\Tag::class, 10)->create();

        // Create some Pages and add a number of Tags to each
        factory(App\Page::class, 50)->create()->each(function($bar) {
            $bar->tags()->sync(
                App\Tag::all()->random( rand(1,5) )->pluck('name')->toArray()
            );
        });
    }
}

Please or to participate in this conversation.