jjclane/laravel-sqlite-migrations
Trait for Laravel migrations that makes schema changes SQLite-safe. Automatically transforms unsupported column types and avoids “Cannot add a NOT NULL column with default value NULL” by adding columns safely, letting migrations run cleanly on SQLite.
Installation
composer require jjclane/laravel-sqlite-migrations --dev
Add the trait to your migration class:
use JJCLane\SQLiteMigration\TransformMigration;
First Use Case Apply the trait to any migration where SQLite compatibility is needed:
class CreateUsersTable extends Migration
{
use TransformMigration;
public function up()
{
$this->table('users', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->timestamps();
});
}
}
Run migrations as usual:
php artisan migrate
Where to Look First
NOT NULL constraints, unsupported datatypes).getSQLiteColumnType()).Automatic Transformation
Use the TransformMigration trait to auto-convert Laravel Blueprint methods to SQLite-compatible syntax:
$this->table('posts', function (Blueprint $table) {
$table->bigIncrements('id'); // Auto-converted to `INTEGER PRIMARY KEY AUTOINCREMENT`
$table->string('title', 255);
$table->unsignedDecimal('price', 8, 2); // SQLite lacks `UNSIGNED`; trait handles this
});
Explicit Transformation
For granular control, use transformMigration():
$this->transformMigration('products', function (Blueprint $table) {
$table->json('metadata'); // SQLite supports JSON natively; trait ensures correct syntax
});
Schema-Level Adjustments Leverage the trait’s built-in mappings for unsupported datatypes:
unsignedInteger() → INTEGER (SQLite lacks UNSIGNED).decimal() → REAL (SQLite’s closest equivalent).timestamp() → INTEGER (stored as Unix timestamp).NOT NULL Workarounds
Avoid SQLite’s NOT NULL + DEFAULT NULL conflict by:
$this->table('orders', function (Blueprint $table) {
$table->string('status')->nullable(); // Step 1: Add nullable
// Later migration:
$table->string('status')->default('pending')->nullable(false); // Step 2: Update
});
Foreign Key Handling
SQLite requires FOREIGN KEY constraints to be defined in a separate PRAGMA statement. The trait handles this implicitly:
$this->table('order_items', function (Blueprint $table) {
$table->foreignId('order_id')->constrained(); // Auto-converted to SQLite-compatible FK
});
phpunit.xml for SQLite-specific tests:
<env key="DB_CONNECTION" value="sqlite_testing"/>
sqlite3 installed).faker data (e.g., unsignedInteger() → integer()).Unsupported Datatypes
unsignedBigInteger() or unsignedMediumInteger() will fail silently (SQLite lacks UNSIGNED).bigInteger() and handle unsigned logic in application code.getSQLiteColumnType():
protected function getSQLiteColumnType($type)
{
return str_replace('unsigned', '', parent::getSQLiteColumnType($type));
}
Foreign Key Constraints
PRAGMA foreign_keys = ON; to enable FKs. The trait doesn’t auto-enable this.DatabaseServiceProvider:
DB::statement('PRAGMA foreign_keys = ON;');
Case Sensitivity
snake_case) and test across environments.Migration Order Dependencies
foreignId('user_id')->constrained() before users table).ifNotExists() or manually order migrations.JSON/ENUM Data
JSON but not Laravel’s ENUM or JSON methods directly.$table->text('tags')->comment('JSON'); // Workaround for ENUM-like storage
Enable Query Logging
Add to config/database.php:
'log_queries' => true,
'log' => storage_path('logs/queries.log'),
Check for malformed SQLite queries (e.g., unsupported syntax).
Check Raw SQL
Override getSQLite() to log transformations:
protected function getSQLite($query)
{
\Log::debug('SQLite Query:', [$query]);
return parent::getSQLite($query);
}
Custom Column Mappings
Override getSQLiteColumnType() to handle niche cases:
protected function getSQLiteColumnType($type)
{
if (str_contains($type, 'uuid')) {
return 'TEXT';
}
return parent::getSQLiteColumnType($type);
}
Add SQLite-Specific Methods Extend the trait to support SQLite-only features:
public function sqliteCheck($column)
{
$this->schema->table($this->table, function (Blueprint $table) use ($column) {
$table->raw("ADD CONSTRAINT CHECK ($column IS NOT NULL)");
});
}
Conditional Transformation Skip transformation for specific tables:
public function up()
{
if ($this->table !== 'excluded_table') {
$this->transformMigration('table', function (Blueprint $table) { ... });
}
}
Default Database Connection The trait assumes SQLite is your default connection. Specify explicitly if needed:
$this->transformMigration('table', function (Blueprint $table) {
// ...
}, 'sqlite_testing'); // Custom connection name
Schema Builder Overrides
If using custom schema builders, ensure they’re compatible with the trait’s getSQLite() method.
BEGIN IMMEDIATE overhead.How can I help you explore Laravel packages today?