Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Pivot Table Generator Laravel Package

timwassenburg/laravel-pivot-table-generator

Generate pivot table migrations for Laravel many-to-many relationships with this package. It scaffolds the intermediate tables Eloquent needs, helping you set up and maintain consistent database schemas with less boilerplate.

View on GitHub
Deep Wiki
Context7

Getting Started

Install the package in the project where you actually author models and migrations. Because it only generates migration files at development time, the pragmatic choice is to add it as a require-dev dependency — but be aware that dev-only migrations disappear on production deploys unless you commit the generated files (see Gotchas).

composer require timwassenburg/laravel-pivot-table-generator --dev

Laravel auto-discovers the service provider via composer.json extra.laravel.providers. There is no config to publish and no env vars to set. Confirm the command is registered before assuming a syntax:

php artisan list | grep -i pivot

The command scaffolds a migration for a many-to-many relationship, inferring the pivot table name and foreign key columns from the two model names you pass in. A typical first use case is bootstrapping the role_user table for a standard User ⇄ Role relationship:

php artisan make:pivot User Role   # verify the exact signature via `help`
php artisan migrate

Then wire the relationship in both models:

// app/Models/User.php
public function roles(): BelongsToMany
{
    return $this->belongsToMany(Role::class);
}

// app/Models/Role.php
public function users(): BelongsToMany
{
    return $this->belongsToMany(User::class);
}

That is the entire happy path: generate the migration, migrate, add belongsToMany on both sides. Nothing else needs configuring, which is what makes the package useful — it replaces a small but error-prone piece of manual boilerplate.

Implementation Patterns

Convention-first generation. The package is built around Laravel's implicit pivot naming: alphabetically sorted singular model names joined by an underscore. User + Role yields role_user, with foreign keys derived as user_id and role_id in the sorted order that matches the table name. Rely on this convention wherever possible — the moment you start passing custom names everywhere, you lose the main benefit of the tool.

Custom table and column names. When the convention doesn't fit (e.g., an existing legacy schema, or a pivot that also has a domain meaning like membership), pass explicit names to the command and mirror them in belongsToMany:

return $this->belongsToMany(Role::class, 'memberships')
    ->withPivot('joined_at', 'status')
    ->withTimestamps();

If the command doesn't accept the override, edit the generated migration before running php artisan migrate — the file is plain Laravel migration code and fully editable.

Pair it with a follow-up withPivot step. The generator only scaffolds the columns. If your pivot carries real data (a role_id, a joined_at, a status), add those columns by hand immediately after generation, while the migration is still unrun. Retrofitting them later requires a second migration and a data backfill, which is exactly the friction the package is meant to avoid.

Team workflow. Generate the pivot migration on a feature branch, commit the generated file to database/migrations/, and let review focus on the schema — the tool removes typing but you still want a human to confirm the table name and foreign key order match the belongsToMany declarations on both models. Add a CI lint that checks for orphaned pivot migrations (a migration referencing a model that doesn't exist) if your relationship graph is large.

Seeding and testing. Once the migration exists, use attach()/sync() in seeders and factories exactly as you would with a hand-written pivot. Because the generated migration uses standard foreign keys, you can rely on FK constraints in tests to catch accidental hard deletes of pivot rows before they reach production.

Gotchas and Tips

  • Alphabetical table names are the primary source of "relationship not working" bugs. User + Role is role_user, not user_role. If belongsToMany returns an empty result or a "table does not exist" error, the first thing to check is the actual generated table name — \DB::select('SHOW TABLES LIKE \'%user%\'') or simply read the generated migration file.
  • The package scaffolds migrations, not model relationships. You always have to add belongsToMany on both sides yourself. Don't expect the tool to modify User.php or Role.php.
  • require-dev can bite you in production. Generated migrations live in your repo, so they deploy fine — but the Artisan command won't be available in production. That's the intent. Don't try to run make:pivot from a deploy hook; run it in dev and commit the result.
  • php artisan migrate will not run if the pivot migration timestamp lands ahead of the migrations that create users or roles. Newly generated migrations always get the current timestamp; if you're bootstrapping a fresh project, generate the parent table migrations first. If you're adding a pivot to an existing project, this is normally fine.
  • Foreign key ordering matters for compound indexes. The generated file places the two foreign key columns in the order that matches the table name (alphabetical model order). If you query the pivot predominantly in one direction (e.g., "all roles for a user"), consider swapping the column order or adding an index after generation. Laravel's FK indexes are per-column, so compound index tuning is still on you.
  • UUID / string primary keys are not guaranteed to be handled. The package targets the standard id/unsignedBigInteger case. If your models use HasUuids, inspect the generated migration and change foreignId(...)->constrained() to foreignUuid(...)->constrained() before migrating.
  • Publishing stubs / overriding the template is the extension point you'd reach for if you consistently need withTimestamps()-style columns or different ordering rules. If the package doesn't expose a publish command, fork the migration stub into a local command — the generated file is short enough that maintaining your own is a low-cost hedge against the package going stale.
  • Debugging a failed generation: run the command with -vvv to see whether the failure is in argument resolution (model names not matching namespace) or in file writing (permissions on database/migrations/). Nine times out of ten it's a namespace mismatch — the command resolves models against App\Models by default.
  • Version constraints. The package is on a single 1.x release with a single simple test upstream. Pin the version in composer.json ("^1.0" is fine, but avoid *) and treat any future release as requiring a manual review of the diff, since the test coverage you'd normally lean on isn't there.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator
equipc/laravel-email-database-log
adedsec/nova-translated-labels