TutorialMar 20, 20267 min read

How to Generate Laravel Migrations from Your ER Diagram

Laravel's migration system is one of the framework's best features β€” but writing migrations by hand is tedious. What if you could design your database visually and generate all the migration files automatically? That's exactly what ER Flow does.

Laravel's migration system is one of the framework's best features β€” version-controlled database changes that your whole team can run with a single php artisan migrate command. But writing migrations by hand is tedious, especially at the start of a project when you're creating dozens of tables. What if you could design your database visually and generate all the migration files automatically?

That's exactly what ER Flow does. Design your schema on a visual canvas, click generate, and get production-ready Laravel migrations with proper up() and down() methods. Here's how to set up this workflow from scratch.

Why Visual Design + Generated Migrations?

Before we dive in, let's address why you'd want this workflow instead of just writing migrations directly.

When you write migrations by hand, you're thinking about database structure in a linear, file-by-file way. You create the users migration, then the posts migration, then realize you forgot the foreign key constraint and create another migration to add it. By the end of a project, you have 30+ migration files that are hard to reason about as a whole.

Visual design lets you see your entire schema at once. Relationships are visible as lines between tables. Missing foreign keys are obvious. Circular dependencies stand out. And when you generate migrations from this complete picture, the output is clean and correct from the start.

The other advantage is communication. Showing a Laravel developer a visual diagram of 25 tables with relationships is instantly understandable. Showing them 25 migration files requires them to mentally reconstruct the same picture.

Setting Up Your Project in ER Flow

Start by creating a free account at erflow.io and creating a new workspace. When prompted to choose a database type, select MySQL or PostgreSQL β€” whichever your Laravel project uses. This ensures that column types in your diagram match what Laravel expects.

Creating your first table

Let's build a simple blog application schema. Click to create a new table and name it users. Add the columns that Laravel's default user migration includes: id (bigInteger, auto-increment, primary key), name (string), email (string, unique), email_verified_at (timestamp, nullable), password (string), remember_token (string, nullable), and the standard created_at and updated_at timestamps.

Now create a posts table: id, user_id (bigInteger, foreign key to users), title (string), slug (string, unique), body (text), excerpt (text, nullable), featured_image (string, nullable), status (enum: draft, published, archived), published_at (timestamp, nullable), and timestamps.

Draw a relationship line from posts.user_id to users.id β€” this establishes the one-to-many relationship.

Building out the schema

Continue adding tables for a complete blog: categories (with parent_id for nested categories), post_categories (junction table for the many-to-many relationship), tags, post_tags (another junction table), comments (with user_id and post_id foreign keys, plus a parent_id for threaded comments), and media (for image/file uploads associated with posts).

As you add each table and draw relationships, your visual diagram grows into a complete picture of the blog's data model. You can see at a glance that posts connect to users, categories, tags, and comments β€” and that comments can be nested via self-referencing.

Generating Laravel Migrations

Once your schema is complete, it's time to generate the migration files.

Creating a checkpoint

First, create a checkpoint of your current schema. This serves as the "baseline" for migration generation. Think of it like a Git commit β€” it captures the state of your schema at this moment.

Generating the files

Click the migration generation button and select Laravel as the output format. ER Flow generates individual migration files for each table, properly ordered to respect foreign key dependencies. The users table migration is generated before posts (because posts references users), and junction tables come after both parent tables.

Each migration includes a complete up() method with Schema::create(), all column definitions with proper Laravel column types, index definitions, and foreign key constraints. The down() method includes Schema::dropIfExists() with foreign keys dropped in the correct order.

What the output looks like

For the posts table, you'd get something like this: a migration class with Schema::create('posts', ...) in the up() method, defining $table->id(), $table->foreignId('user_id')->constrained()->cascadeOnDelete(), $table->string('title'), $table->string('slug')->unique(), $table->text('body'), and so on. The down() method cleanly drops the table.

The column type mapping is database-aware β€” if your workspace is set to PostgreSQL, text columns use PostgreSQL's TEXT type; enum columns map correctly; and JSON columns use the proper jsonb type.

Incremental Migrations

The real power comes when your schema evolves. Say you need to add a views_count column to posts and create a new newsletters table.

Make these changes in your ER Flow diagram, then create a new checkpoint. Now, when you generate migrations, ER Flow compares the two checkpoints and generates only the incremental changes. You get an add_views_count_to_posts migration with $table->integer('views_count')->default(0) in the up() method and $table->dropColumn('views_count') in down(), plus a full create_newsletters_table migration for the new table.

This is vastly better than trying to remember what changed and writing the migration by hand. The diff is automatic, complete, and includes both up() and down() methods.

Using with AI (MCP Server + Cursor)

For Laravel developers using Cursor, you can combine visual design with AI assistance. Connect Cursor to ER Flow's MCP Server, then use prompts like:

"I need to add a subscription system to my SaaS app. Users should have plans with monthly/yearly billing, and I need to track payment history."

Cursor reads your existing schema through MCP, creates the new tables (plans, subscriptions, payments) with proper foreign keys to your existing users table, and the changes appear on your ER Flow canvas. Then generate the Laravel migrations for just those new tables.

This three-way workflow β€” visual design + AI assistance + migration generation β€” is the fastest way to go from idea to production-ready Laravel database code.

Best Practices

Name tables in plural, snake_case β€” users, blog_posts, order_items. This matches Laravel's conventions and ensures generated migrations follow the framework's expectations.

Always use `foreignId()` with `constrained()` β€” ER Flow generates these automatically when you draw relationship lines, ensuring referential integrity.

Set cascade rules intentionally β€” decide whether deleting a user should cascade-delete their posts (cascadeOnDelete()) or set the foreign key to null (nullOnDelete()). ER Flow lets you configure this per relationship.

Create checkpoints before major changes β€” this gives you clean diffs and the ability to roll back if a design direction doesn't work out.

Review generated migrations before running β€” while ER Flow generates correct code, always review the output. You might want to add custom logic, seed data, or adjust the migration order.

From Diagram to Database in Minutes

The traditional Laravel workflow is: think about schema β†’ write migrations β†’ run them β†’ realize you forgot something β†’ write another migration β†’ repeat. The ER Flow workflow is: design visually β†’ see the complete picture β†’ generate all migrations at once β†’ run them β†’ iterate visually when changes are needed.

For new projects, this can save hours of initial setup time. For existing projects, the incremental migration generation means you can adopt ER Flow at any point β€” import your existing schema and start generating migrations for future changes.