Files
rspade_system/app/RSpade/man/migrations.txt
root a5e1c604ab Framework updates
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-01-19 04:33:43 +00:00

376 lines
14 KiB
Plaintext
Executable File

MIGRATIONS(7) RSX Framework Manual MIGRATIONS(7)
NAME
migrations - Database migration system with raw SQL enforcement
SYNOPSIS
php artisan make:migration:safe <name>
php artisan migrate
php artisan migrate:status
DESCRIPTION
The RSX framework enforces a forward-only migration strategy using raw SQL
statements. Laravel's Schema builder is prohibited to ensure clarity,
auditability, and prevent hidden behaviors.
The migrate command automatically handles snapshot protection in development
mode - no manual steps required.
PHILOSOPHY
1. Forward-only migrations - No rollbacks, no down() methods
2. Raw SQL only - Direct MySQL statements, no abstractions
3. Fail loud - Migrations must succeed or fail with clear errors
4. Automatic safety - Development mode creates snapshots automatically
MIGRATION RULES
Schema Builder Prohibition
All migrations MUST use DB::statement() with raw SQL. The following are prohibited:
- Schema::create()
- Schema::table()
- Schema::drop()
- Schema::dropIfExists()
- Schema::rename()
- Blueprint class usage
- $table-> method chains
The migration validator automatically checks for these patterns and will prevent
migrations from running if violations are found.
Required Table Structure
ALL tables MUST have:
id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY
This is non-negotiable. Every table needs this exact ID column (SIGNED for
easier future migrations).
Data Type Standards - What You Need to Know
The framework automatically normalizes data types during migration, so you can
use simpler types and let the system handle optimization:
What You Can Use (System Auto-Converts):
- INT -> automatically becomes BIGINT
- TEXT -> automatically becomes LONGTEXT
- FLOAT -> automatically becomes DOUBLE
- Any charset -> automatically becomes UTF8MB4
- created_at/updated_at -> automatically added with proper defaults
- created_by/updated_by -> automatically added
- deleted_by -> automatically added for soft-delete tables
What You MUST Be Careful About:
- Foreign key columns - Must match the referenced column type exactly
Example: If users.id is BIGINT, then orders.user_id must be BIGINT
- TINYINT(1) - Preserved for boolean values, won't be converted
- Column names ending in _id are assumed to be foreign keys
Recommended for Simplicity:
- Just use INT for integers (becomes BIGINT automatically)
- Just use TEXT for long content (becomes LONGTEXT automatically)
- Just use FLOAT for decimals (becomes DOUBLE automatically)
- Don't add created_at/updated_at (added automatically)
- Don't add created_by/updated_by (added automatically)
down() Method Removal
The migration system automatically removes down() methods from migration files.
Migrations are forward-only - database changes should never be reversed.
AUTOMATIC NORMALIZATION
What migrate:normalize_schema Does For You
After migrations run, the normalize_schema command automatically:
1. Type Conversions:
- INT columns -> BIGINT (except TINYINT(1) for booleans)
- BIGINT UNSIGNED -> BIGINT SIGNED
- TEXT -> LONGTEXT
- FLOAT -> DOUBLE
- All text columns -> UTF8MB4 character set
2. Required Columns Added:
- created_at TIMESTAMP(3) DEFAULT CURRENT_TIMESTAMP(3)
- updated_at TIMESTAMP(3) DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE
- created_by INT(11) NULL
- updated_by INT(11) NULL
- deleted_by INT(11) NULL (only for soft-delete tables)
3. Indexes Added:
- INDEX on created_at
- INDEX on updated_at
- INDEX on site_id (for models extending Rsx_Site_Model_Abstract)
- INDEX on id+version (for Versionable models)
4. Model-Specific Columns:
- site_id BIGINT - for models extending Rsx_Site_Model_Abstract
- version INT(11) DEFAULT 1 - for Versionable/Ajaxable models
5. Precision Upgrades:
- All DATETIME/TIMESTAMP columns -> precision (3) for milliseconds
This means you can write simpler migrations and let the system handle the
optimization and standardization. The only time you need to be explicit about
types is when creating foreign key columns that must match their referenced
column exactly.
VALIDATION SYSTEM
Automatic Validation
When running migrations in development mode, the system automatically:
1. Validates all pending migrations for Schema builder usage
2. Removes down() methods if present
3. Reports violations with colored output and remediation advice
4. Stops at the first violation to allow correction
Validation Output
When a violation is detected, you'll see:
[ERROR] Migration Validation Failed
File: 2025_09_30_create_example_table.php
Line: 28
Violation: Found forbidden Schema builder usage: Schema::create
Code Preview:
----------------------------------------
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('name');
});
----------------------------------------
Remediation: Use DB::statement("CREATE TABLE...") instead
Example:
DB::statement('CREATE TABLE users (
id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255),
created_at TIMESTAMP NULL DEFAULT NULL
)');
MIGRATION WORKFLOW
Development Mode (RSX_MODE=development)
Simply run:
php artisan migrate
The command automatically:
1. Creates a database snapshot before running migrations
2. Runs all pending migrations with validation
3. Runs schema quality checks
4. On success: commits changes, regenerates constants, recompiles bundles
5. On failure: automatically rolls back to snapshot
Failed migrations restore the database to its pre-migration state. Fix your
migration files and run "php artisan migrate" again.
Debug/Production Mode (RSX_MODE=debug or production)
Run:
php artisan migrate
In these modes:
- No snapshot protection (source code is read-only)
- Migrations run directly against the database
- Schema normalization still runs
- Constants and bundles are NOT regenerated (already done in development)
Ensure migrations are thoroughly tested in development before running in
debug/production modes.
Framework-Only Migrations
To run only framework migrations (system/database/migrations):
php artisan migrate --framework-only
This skips snapshot protection regardless of mode.
MIGRATION EXAMPLES
Creating a Table (Simple Version - Recommended)
public function up()
{
// You can write this simple version - system auto-normalizes types
DB::statement("
CREATE TABLE products (
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, -- becomes BIGINT
name VARCHAR(255) NOT NULL,
description TEXT, -- becomes LONGTEXT
price DECIMAL(10,2) NOT NULL DEFAULT 0.00,
stock_quantity INT NOT NULL DEFAULT 0, -- becomes BIGINT
is_active TINYINT(1) NOT NULL DEFAULT 1, -- stays TINYINT(1)
category_id INT NULL, -- becomes BIGINT
INDEX idx_category (category_id),
INDEX idx_active (is_active)
-- No need for created_at/updated_at - added automatically
)
");
}
Creating a Table (Explicit Version - If You Prefer)
public function up()
{
// Or be explicit about types if you prefer
DB::statement("
CREATE TABLE products (
id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
description LONGTEXT,
price DECIMAL(10,2) NOT NULL DEFAULT 0.00,
stock_quantity BIGINT NOT NULL DEFAULT 0,
is_active TINYINT(1) NOT NULL DEFAULT 1,
category_id BIGINT NULL,
created_at TIMESTAMP NULL DEFAULT NULL,
updated_at TIMESTAMP NULL DEFAULT NULL,
INDEX idx_category (category_id),
INDEX idx_active (is_active),
INDEX idx_created (created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
");
}
Adding Columns
public function up()
{
DB::statement("ALTER TABLE users ADD COLUMN age BIGINT NULL AFTER email");
DB::statement("ALTER TABLE users ADD INDEX idx_age (age)");
}
Modifying Columns
public function up()
{
// Change column type
DB::statement("ALTER TABLE products MODIFY COLUMN price DECIMAL(12,2)");
// Rename column
DB::statement("ALTER TABLE users CHANGE COLUMN username user_name VARCHAR(100)");
// Add default value
DB::statement("ALTER TABLE posts ALTER COLUMN status SET DEFAULT 'draft'");
}
Managing Indexes
public function up()
{
// Add index
DB::statement("CREATE INDEX idx_email ON users (email)");
// Add unique index
DB::statement("CREATE UNIQUE INDEX idx_unique_slug ON posts (slug)");
// Add composite index
DB::statement("CREATE INDEX idx_user_status ON orders (user_id, status)");
// Drop index
DB::statement("DROP INDEX idx_old_index ON table_name");
}
Foreign Keys (IMPORTANT - Match Types Exactly)
public function up()
{
// CRITICAL: Foreign key columns must match referenced column type
// If users.id is BIGINT, orders.user_id must also be BIGINT
// First, ensure the column has correct type (if not already created)
DB::statement("ALTER TABLE orders ADD COLUMN user_id BIGINT NULL");
// Then add the foreign key constraint
DB::statement("
ALTER TABLE orders
ADD CONSTRAINT orders_user_fk
FOREIGN KEY (user_id) REFERENCES users(id)
ON DELETE CASCADE
");
// To drop a foreign key
DB::statement("ALTER TABLE orders DROP FOREIGN KEY orders_user_fk");
}
// Note: After normalization, all id columns are BIGINT, so foreign keys
// should always use BIGINT to avoid type mismatches
Data Migrations
public function up()
{
// Simple update
DB::statement("UPDATE users SET role = 'member' WHERE role IS NULL");
// Complex migration with temporary column
DB::statement("ALTER TABLE orders ADD COLUMN total_new DECIMAL(10,2)");
DB::statement("UPDATE orders SET total_new = quantity * price");
DB::statement("ALTER TABLE orders DROP COLUMN total");
DB::statement("ALTER TABLE orders CHANGE total_new total DECIMAL(10,2)");
}
ERROR MESSAGES
"Migration validation failed: Schema builder usage detected"
Your migration uses Laravel's Schema builder. Rewrite using DB::statement()
with raw SQL.
"Unauthorized migrations detected!"
Migration files exist that weren't created via make:migration:safe.
Recreate them using the proper command.
"Snapshot-based migrations require Docker environment"
You're in development mode but not running in Docker. Either:
- Run in Docker, or
- Set RSX_MODE=debug in .env to skip snapshot protection
AUTOMATIC ROLLBACK (Development Mode)
In development mode, if a migration fails:
1. The error is displayed
2. Database is automatically restored to pre-migration state
3. You can fix the migration and run "php artisan migrate" again
This happens automatically - no manual intervention required.
SECURITY CONSIDERATIONS
SQL Injection
When using dynamic values in migrations, always use parameter binding:
CORRECT:
DB::statement("UPDATE users SET status = ? WHERE created_at < ?", ['active', '2025-01-01']);
WRONG:
DB::statement("UPDATE users SET status = '$status' WHERE created_at < '$date'");
Production Safety
- Always test migrations in development first
- Keep migrations small and focused
- Never reference models or services in migrations
- Migrations must be self-contained and idempotent where possible
DEBUGGING
Viewing Pending Migrations
php artisan migrate:status
Testing a Migration
In development mode, just run:
php artisan migrate
If it fails, the database is automatically restored. Fix the migration
and try again.
Common Issues
- "Class not found" - Don't reference models in migrations
- "Syntax error" - Check your SQL syntax, test in MySQL client first
- "Foreign key constraint" - Ensure referenced table/column exists
- "Duplicate column" - Check if column already exists before adding
SEE ALSO
rsx:man database - Database system overview
rsx:man coding_standards - General coding standards
rsx:man error_handling - Error handling patterns
AUTHORS
RSX Framework Team
RSX Framework January 2026 MIGRATIONS(7)