The migrate command executes all pending database migrations in your application.
Syntax
Basic Usage
Run all pending migrations:
Output:
Running migrations for: sqlite
Running: 2026_03_08_143022_create_users_table
Migrated: 2026_03_08_143022_create_users_table
Running: 2026_03_08_143045_create_products_table
Migrated: 2026_03_08_143045_create_products_table
Migrations completed!
The migrate command only runs migrations that haven’t been executed yet. Previously run migrations are tracked in the migrations table.
How It Works
1. Database Connection
The command reads your database configuration from .env:
DB_CONNECTION=sqlite
DB_DATABASE=database/database.sqlite
Supported databases:
- SQLite - File-based database (default)
- PostgreSQL - Advanced relational database
- MySQL - Popular relational database
- MariaDB - MySQL-compatible database
2. Migration Tracking
Lyger creates a migrations table to track which migrations have been run:
CREATE TABLE migrations (
id INTEGER PRIMARY KEY,
migration VARCHAR(255),
batch INTEGER
)
3. Execution Order
Migrations run in chronological order based on their timestamp prefix:
2026_03_08_143022_create_users_table.php ← Runs first
2026_03_08_143045_create_products_table.php ← Runs second
2026_03_08_144510_add_status_to_orders.php ← Runs third
Database Configuration
SQLite (Default)
DB_CONNECTION=sqlite
DB_DATABASE=database/database.sqlite
SQLite is perfect for development and small applications. No server setup required!
PostgreSQL
DB_CONNECTION=pgsql
DB_HOST=127.0.0.1
DB_PORT=5432
DB_DATABASE=lyger
DB_USERNAME=postgres
DB_PASSWORD=secret
MySQL
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=lyger
DB_USERNAME=root
DB_PASSWORD=secret
Migration Process
Initial Run
First time running migrations:
What happens:
- Creates
migrations table if it doesn’t exist
- Scans
database/migrations/ directory
- Runs all migration files
- Records each migration in the
migrations table
Subsequent Runs
Running migrations again:
What happens:
- Checks
migrations table for already-run migrations
- Skips migrations that have already been executed
- Only runs new migrations
- Updates the
migrations table
Source Code
The migration execution logic:
function migrate(string $basePath): void
{
$config = getDatabaseConfig($basePath);
echo "Running migrations for: {$config['connection']}\n\n";
$migrationsPath = $basePath . '/database/migrations';
if (!is_dir($migrationsPath)) {
echo "No migrations folder found.\n";
return;
}
require_once $basePath . '/vendor/autoload.php';
require_once $basePath . '/Lyger/Database/Migration.php';
$files = glob($migrationsPath . '/*.php');
$ran = getRanMigrations($basePath, $config['connection']);
foreach ($files as $file) {
$class = basename($file, '.php');
if (in_array($class, $ran)) continue;
echo "Running: {$class}\n";
require_once $file;
preg_match('/class\s+(\w+)/', file_get_contents($file), $matches);
$className = $matches[1] ?? null;
if ($className && class_exists($className)) {
$migration = new $className();
$migration->up();
recordMigration($basePath, $class, $config['connection']);
echo "Migrated: {$class}\n";
}
}
echo "\nMigrations completed!\n";
}
Helper Functions
Get ran migrations:
function getRanMigrations(string $basePath, string $db): array
{
$pdo = getDbConnection($basePath, $db);
$pdo->exec("CREATE TABLE IF NOT EXISTS migrations (id INTEGER PRIMARY KEY, migration VARCHAR(255), batch INTEGER)");
return $pdo->query('SELECT migration FROM migrations')->fetchAll(\PDO::FETCH_COLUMN);
}
Record migration:
function recordMigration(string $basePath, string $class, string $db): void
{
$pdo = getDbConnection($basePath, $db);
$stmt = $pdo->prepare('INSERT INTO migrations (migration, batch) VALUES (?, 1)');
$stmt->execute([$class]);
}
Complete Workflow
php rawr make:migration create_users_table
php rawr make:migration create_posts_table
// database/migrations/..._create_users_table.php
public function up(): void
{
$this->getSchema()->create('users', function ($table) {
$table->id();
$table->string('name');
$table->string('email')->unique();
$table->string('password');
$table->timestamps();
});
}
Running migrations for: sqlite
Running: 2026_03_08_143022_create_users_table
Migrated: 2026_03_08_143022_create_users_table
Running: 2026_03_08_143045_create_posts_table
Migrated: 2026_03_08_143045_create_posts_table
Migrations completed!
Common Scenarios
Fresh Installation
Setting up a new database:
# Create migrations
php rawr make:migration create_users_table
php rawr make:migration create_products_table
php rawr make:migration create_orders_table
# Run all migrations
php rawr migrate
Adding New Tables
After initial setup:
# Create new migration
php rawr make:migration create_categories_table
# Run only the new migration
php rawr migrate
Output:
Running migrations for: sqlite
Running: 2026_03_08_150000_create_categories_table
Migrated: 2026_03_08_150000_create_categories_table
Migrations completed!
Team Development
When pulling changes with new migrations:
# Pull latest code
git pull origin main
# Run any new migrations
php rawr migrate
migrate:rollback
Rollback the last migration batch:
php rawr migrate:rollback
Rollback functionality is being implemented. Currently shows:
migrate:status
Check which migrations have been run:
Output:
Migration status for: sqlite
Full status display is in development and will show a table of all migrations with their run status.
Error Handling
No Migrations Folder
Output:
No migrations folder found.
Solution:
mkdir -p database/migrations
php rawr make:migration create_users_table
php rawr migrate
Database Connection Error
If database connection fails, check your .env file and ensure:
- Database file exists (SQLite)
- Database server is running (MySQL/PostgreSQL)
- Credentials are correct
- Database exists
Migration Error
If a migration fails:
- Check the migration file for syntax errors
- Verify table/column names don’t conflict
- Ensure foreign key constraints are valid
- Check database permissions
Best Practices
Do:
- Run migrations in development before committing
- Test migrations can run on fresh database
- Keep migrations in version control
- Create migrations for all schema changes
Don’t:
- Modify migrations after they’ve been run in production
- Delete migration files
- Skip migrations
- Manually modify the migrations table
Production Considerations
Before Running Migrations
-
Backup your database
# SQLite
cp database/database.sqlite database/database.backup.sqlite
# MySQL/PostgreSQL
mysqldump -u root -p lyger > backup.sql
-
Test in staging environment
# Run migrations in staging first
php rawr migrate
-
Review migration files
ls -la database/migrations/
Running in Production
# Enable maintenance mode (if available)
# Run migrations
php rawr migrate
# Verify tables
# Disable maintenance mode
Troubleshooting
Migrations Not Running
Check if migrations have already been run:
Duplicate Migration
If you accidentally create a duplicate:
- Delete the newer migration file
- The older one will remain tracked in the database
Reset All Migrations
This will drop all tables and data!
# Delete database file (SQLite)
rm database/database.sqlite
# Run migrations again
php rawr migrate
Example Migration Set
Complete example for a blog application:
php rawr make:migration create_users_table
php rawr make:migration create_categories_table
php rawr make:migration create_posts_table
php rawr make:migration create_comments_table
Running migrations for: sqlite
Running: 2026_03_08_100000_create_users_table
Migrated: 2026_03_08_100000_create_users_table
Running: 2026_03_08_100001_create_categories_table
Migrated: 2026_03_08_100001_create_categories_table
Running: 2026_03_08_100002_create_posts_table
Migrated: 2026_03_08_100002_create_posts_table
Running: 2026_03_08_100003_create_comments_table
Migrated: 2026_03_08_100003_create_comments_table
Migrations completed!
Next Steps
Make Migration
Create new migration files
Make Model
Create models for your tables
Schema Builder
Learn about available schema methods
Seeding
Populate tables with test data