Laravel Migration to Prisma
Replay Laravel migrations (create, alter, rename, drop) and convert the result to a Prisma 6 or 7 schema, listing every construct it could not read.
Read in your browser and never uploaded; PHP is parsed, never run. Migrations are replayed in order: pasted text in the order you paste it, opened files by file name (Laravel runs them that way). Only the standard Schema and Blueprint calls are read. Importing replaces the schema you are designing.
Add a table on the left, load an example, or open a project file to start designing.
Generated files
Generated files appear here as you design.
Diagnostics appear here after you paste your input.
Processed locally in your browser. Your data never leaves your device.
About this Laravel migration to Prisma converter
Paste the migration files from database/migrations and get the Prisma schema they add up to. The migrations are replayed in order (create, alter, rename, drop), so the result is the schema you would have after running them all, then written as Prisma models with both sides of every relation. Nothing is executed and nothing leaves your browser. This page is the Laravel entry point of the Database Schema Studio: the imported schema is fully editable and can also produce a Laravel project or Prisma for another database.
How to use it
- Paste your migration files (each starts with <?php) or open them from disk, then Import.
- Read the diagnostics first: they list every construct that could not be read, with its line.
- Choose the Prisma version and database, adjust the schema if you like, and copy or download the result.
A worked example
These two migration files:
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email')->unique();
$table->timestamps();
});
}
public function down(): void
{
}
};
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
$table->string('title', 200);
$table->boolean('published')->default(false);
});
}
public function down(): void
{
}
};
become these models (Prisma 7, MySQL):
model User {
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
name String @db.VarChar(255)
email String @unique @db.VarChar(255)
createdAt DateTime? @map("created_at") @db.Timestamp(0)
updatedAt DateTime? @map("updated_at") @db.Timestamp(0)
posts Post[]
@@map("users")
}
model Post {
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
userId BigInt @map("user_id") @db.UnsignedBigInt
title String @db.VarChar(200)
published Boolean @default(false)
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@map("posts")
}Note what was understood: id() is an unsigned big auto-increment primary key; timestamps() is two nullable timestamp columns; foreignId('user_id')->constrained() is a foreign key to users (the table name was assumed from the column, as Laravel does, and the diagnostics say so); and cascadeOnDelete() is onDelete: Cascade.
Type mapping (Blueprint to Prisma on MySQL)
| Blueprint call | Prisma field type |
|---|---|
bigInteger('c') | BigInt |
unsignedBigInteger('c') | BigInt @db.UnsignedBigInt |
integer('c') | Int |
unsignedInteger('c') | Int @db.UnsignedInt |
tinyInteger('c') | Int @db.TinyInt |
boolean('c') | Boolean |
string('c') | String @db.VarChar(255) |
string('c', 100) | String @db.VarChar(100) |
char('c', 36) | String @db.Char(36) |
text('c') | String @db.Text |
longText('c') | String @db.LongText |
decimal('c', 8, 2) | Decimal @db.Decimal(8, 2) |
double('c') | Float @db.Double |
date('c') | DateTime @db.Date |
dateTime('c') | DateTime @db.DateTime(0) |
timestamp('c') | DateTime @db.Timestamp(0) |
json('c') | Json |
uuid('c') | String @db.Char(36) |
binary('c') | Bytes @db.Blob |
year('c') | Unsupported("year") |
Other databases use their own native types (for PostgreSQL, uuid is @db.Uuid). Blueprint types with no Prisma scalar (year, set, spatial types) become Unsupported() fields with a warning.
What it does not read
- Anything computed at run time: variables, config() values, loops, custom macros.
- Raw SQL (DB::statement) and data changes (DB::table(...)->insert): listed, not applied.
- Migrations that only make sense against tables it was not given: reported as unknown tables.
- down() methods (the rollback direction).
Frequently asked questions
- Does it run my migrations or need PHP?
- No. The migration files are parsed as text with a PHP parser and replayed as data: nothing is executed, no database is touched and no PHP is needed. This also means it can only read what is written literally in the files. Values computed at run time (variables, config(), loops) are listed as things it could not read.
- How are several migrations combined?
- They are replayed in order, the way php artisan migrate would: a table created in one file can be altered, renamed or dropped in a later one, and the result is the schema you would have after running them all. Pasted files are used in the order you paste them; files opened with the file picker are ordered by name, which for Laravel migrations means by timestamp.
- Which Laravel methods are understood?
- Schema::create, table, drop, dropIfExists and rename; the standard Blueprint column methods (ids and increments, integers, strings, text, decimals, dates and times, json, uuid, ulid, binary, enum, morphs, timestamps, softDeletes, rememberToken, foreignId with constrained), the usual modifiers (nullable, default, unsigned, autoIncrement, unique, index, primary, comment, useCurrent, storedAs, change), foreign keys with their actions, and the drop and rename methods for columns and keys. down() is ignored.
- What does it do with constrained() and no table name?
- Laravel guesses the table by pluralizing the column prefix (user_id becomes users). So does this converter, using a table with that name from your migrations when there is one, and it says so in the diagnostics each time so you can check the assumption.
- Why did a table or column not appear?
- Each thing skipped has a line in the diagnostics with its line number: raw DB::statement calls (their effect on the schema is not read), loops and conditionals it cannot evaluate, unknown Blueprint methods and macros, Schema::table on a table no pasted migration creates, and values that are not literals. A schema guard such as if (! Schema::hasTable(...)) is assumed to let its body run.
- Is my code uploaded?
- No. Parsing, replay and generation happen in your browser. Nothing is sent anywhere or stored.