Migrations & Schema Builder
Migrations & Schema Builder
Zero splits its data tooling into two layers: DBML (Database Management Layer) handles fluent query building, while the migration API acts as a lightweight DBAL (Database Abstraction Layer) for structural changes. Use DBML when you need to read/write data, and the migration DBAL when you need to create or modify tables.
Connection Charset & Collation
Driver defaults now read from your environment configuration:
MYSQL_CHARSET=utf8mb4
MYSQL_COLLATION=utf8mb4_general_ci
POSTGRES_CHARSET=UTF8
MySQL and PostgreSQL connections pick up these values automatically (falling back to UTF-8 sensible defaults). Override them per connection if you need a different encoding. See .env.example
for the default values that ship with the framework.
Table-Level Defaults
Control default encodings directly from your migration:
use Zero\Lib\DB\Schema;
Schema::create('posts', function ($table) {
$table->charset('utf8mb4');
$table->collation('utf8mb4_general_ci');
$table->id();
$table->string('title');
$table->text('body');
$table->timestamps();
});
When altering an existing table, the same methods emit the relevant ALTER TABLE ... DEFAULT CHARACTER SET / COLLATE
statements on MySQL:
Schema::table('posts', function ($table) {
$table->charset('utf8mb3');
$table->collation('utf8mb3_general_ci');
});
Column Charset & Collation
Column helpers allow per-column overrides:
Schema::create('customers', function ($table) {
$table->id();
$table->string('name')
->charset('utf8mb4')
->collation('utf8mb4_general_ci');
$table->string('legacy_code', 32)
->charset('latin1')
->collation('latin1_swedish_ci');
});
Use charset($value)
to switch the character set and collation($value)
(or the alias collate($value)
) to tweak the collation.
Column Reference
Every column helper returns a ColumnDefinition
, so you can chain modifiers such as nullable()
, default()
, charset()
, collation()
, index()
, or change()
.
Schema::create('example', function ($table) {
$table->id(); // BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY
$table->increments('legacy_id'); // Alias of id()
$table->integer('age'); // INT
$table->bigInteger('views'); // BIGINT
$table->string('name', 255); // VARCHAR(255)
$table->text('biography'); // TEXT
$table->longText('content'); // LONGTEXT
$table->enum('status', ['draft', 'published']);
$table->boolean('is_active', default: true);
$table->uuid('uuid');
$table->uuidPrimary();
$table->timestamp('published_at');
$table->datetime('archived_at', nullable: true);
$table->foreignId('user_id')->constrained();
$table->softDeletes();
$table->timestamps();
});
Column Modifiers
nullable()
– mark the column nullable.default($value)
– define a default (strings are automatically quoted).unsigned()
– available on numeric types.primary()
,unique()
,index()
– quick index/constraint helpers.charset($charset)
– override character set for the column (MySQL only).collation($collation)
/collate($collation)
– override the column collation (MySQL only).useCurrent()
– set TIMESTAMP columns to default toCURRENT_TIMESTAMP
.references($column)
+on($table)
– pair to declare foreign key targets.onDelete($action)
/onUpdate($action)
– specify cascading behaviour for foreign keys.foreignKeyName($name)
– override the generated foreign key constraint name.change()
– alter an existing column when used insideSchema::table()
.
Foreign Keys
Chain the foreign key helpers together for clarity:
Schema::table('orders', function ($table) {
$table->foreignId('user_id')
->constrained('users')
->onDelete('cascade')
->onUpdate('cascade');
});
Prefer the explicit helpers when you need full control over names or cascading rules:
Schema::table('invoices', function ($table) {
$table->unsignedBigInteger('customer_id');
$table->foreignId('customer_id')
->references('id')
->on('customers')
->onDelete('restrict')
->onUpdate('cascade')
->foreignKeyName('invoices_customer_fk');
});
Table Helpers
charset($charset)
/collation($collation)
– set table defaults (MySQL only).timestamps()
– addscreated_at
andupdated_at
.softDeletes()
– adds nullabledeleted_at
.foreignId()
+constrained()
– declare foreign key columns succinctly.dropColumn($name)
– remove a column.renameColumn($from, $to)
– rename a column.raw($definition)
– inject custom SQL when you need something low-level.
Schema Facade Shortcuts
Schema::create($table, $callback)
– create a table.Schema::table($table, $callback)
– alter an existing table.Schema::drop($table)
/dropIfExists($table)
– remove tables.Schema::dropColumn($table, $column)
/dropColumnIfExists($table, $column)
– drop columns imperatively.
Changing Existing Columns
Alter columns in place with change()
:
Schema::table('customers', function ($table) {
$table->string('name', 255)
->charset('utf8mb4')
->collation('utf8mb4_general_ci')
->change();
});
On MySQL this compiles to ALTER TABLE ... MODIFY COLUMN ...
. Combine it with other modifiers (nullable()
, default()
, etc.) as needed. Other drivers may ignore charset/collation directives or require additional statements.
Recap
- Connection defaults originate from
.env
and are applied automatically. - Use
$table->charset()
/$table->collation()
for table-wide defaults. - Override individual columns with
->charset()
/->collation()
. - Call
->change()
withinSchema::table()
when modifying existing columns.
With these additions you can fine-tune encodings, collations, and column definitions without dropping into raw SQL. DBML remains your go-to for querying data, while the migration DBAL keeps schema changes expressive and safe.