Skip to content

SQL to Laravel Migration

Convert a MySQL or MariaDB dump (CREATE TABLE, ALTER TABLE keys) into Laravel 12 and 13 migrations in dependency order, warning about what will not convert.

Generate

Read in your browser and never uploaded. Only CREATE TABLE, ALTER TABLE and CREATE INDEX are read; INSERT data is skipped. 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 SQL to Laravel migration converter

Paste the structure of a MySQL or MariaDB database and get the Laravel migrations that create it: one file per table, ordered so that every foreign key points at a table that already exists. It reads the SQL itself (nothing is executed and nothing leaves your browser). This page is the SQL entry point of the Database Schema Studio: once imported, the schema is fully editable, and you can add models, factories and seeders or download a ZIP.

How to use it

  1. Paste CREATE TABLE statements or a structure-only dump, or open a .sql file, then Import.
  2. Pick the Laravel version and the database (MySQL is preselected).
  3. Read the diagnostics first: they list what was skipped or approximated.
  4. Adjust tables, columns and keys if you like: the migrations update as you edit.
  5. Copy or download each migration, or download them all as one ZIP.

A worked example

This SQL:

CREATE TABLE `users` (
  `id` bigint unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(255) NOT NULL,
  `email` varchar(255) NOT NULL,
  `created_at` timestamp NULL DEFAULT NULL,
  `updated_at` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `users_email_unique` (`email`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

CREATE TABLE `posts` (
  `id` bigint unsigned NOT NULL AUTO_INCREMENT,
  `user_id` bigint unsigned NOT NULL,
  `title` varchar(200) NOT NULL,
  `published` tinyint(1) NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`),
  KEY `posts_user_id_foreign` (`user_id`),
  CONSTRAINT `posts_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

becomes two migrations. The second one (the first has id(), the two timestamp columns and unique() on email) is:

Schema::create('posts', function (Blueprint $table) {
    $table->id();
    $table->foreignId('user_id')->constrained()->cascadeOnDelete();
    $table->string('title', 200);
    $table->boolean('published')->default(false);
});

Note what changed: bigint unsigned NOT NULL AUTO_INCREMENT named id is id(); the foreign key on user_id becomes foreignId()->constrained(); tinyint(1) with DEFAULT '0' is a boolean defaulting to false; and the separate KEY that only supported the foreign key is gone because MySQL creates it from the foreign key. ENGINE and CHARSET are connection and server configuration in Laravel, so they are not repeated in migrations.

Type mapping

How MySQL column types are written in Blueprint
MySQL typeLaravel migration
tinyint(1)boolean('c')
tinyinttinyInteger('c')
smallintsmallInteger('c')
mediumintmediumInteger('c')
intinteger('c')
int unsignedunsignedInteger('c')
bigintbigInteger('c')
bigint unsignedunsignedBigInteger('c')
decimal(8,2)decimal('c', 8, 2)
floatfloat('c')
doubledouble('c')
char(36)char('c', 36)
varchar(100)string('c', 100)
varchar(255)string('c')
texttext('c')
mediumtextmediumText('c')
longtextlongText('c')
datedate('c')
timetime('c')
datetimedateTime('c')
timestamptimestamp('c')
jsonjson('c')
blobbinary('c')
enum('a','b')enum('c', ['a', 'b'])

tinytext, tinyblob and the larger blob types are approximated (text and binary) and year, set, bit and spatial types cannot be created by a Blueprint call: they become a // TODO line and a warning.

What it does not read

  • Views, triggers, stored procedures, functions and events (listed, not imported).
  • CHECK constraints, expression indexes and partitioning (warned about, dropped).
  • Data: INSERT statements are counted and skipped.
  • SQL for other databases than MySQL and MariaDB.

Frequently asked questions

How do I get SQL from my database?
Run mysqldump --no-data your_database > schema.sql, or copy the output of SHOW CREATE TABLE from a client, or use phpMyAdmin Export with "structure only". A dump that includes INSERT statements works too (they are counted and skipped), up to 5 MB.
Which SQL does it read?
CREATE TABLE, ALTER TABLE (ADD column, keys and foreign keys; MODIFY), and CREATE INDEX, in the MySQL and MariaDB dialect. Everything else is counted and listed: data statements and session settings as information, views, triggers, procedures and other statements as warnings.
Why is a column or key missing from the output?
Every diagnostic says what was not carried over and why. Typical ones: CHECK constraints, ON UPDATE CURRENT_TIMESTAMP, index prefix lengths, expression indexes, partitioning, and types Laravel has no builder for (set, year, bit, spatial types).
Why do some indexes disappear?
MySQL creates an index for every foreign key by itself and dumps list it. An index on exactly a foreign key’s columns adds nothing in a Laravel migration, so it is not written separately. Index and key names that MySQL invents (posts_ibfk_1, or a name equal to the column) and names equal to Laravel’s own default are left out for the same reason.
Is my SQL uploaded?
No. Reading and generating happen in your browser (large input in a background worker). The SQL is never sent anywhere or stored, and nothing is executed: it is only read as text.
What about PostgreSQL or SQLite SQL?
Only MySQL and MariaDB are read today. Other dialects will be added as separate readers on the same engine.