GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — docs/migrations.md at 0.4 · hypervel/docs

Expand file tree

/

migrations.md

Copy path
More file actions
More file actions

Latest commit

 

History

History
History
1887 lines (1312 loc) · 72.8 KB
/

migrations.md

Copy path

File metadata and controls

1887 lines (1312 loc) · 72.8 KB
Raw
Copy raw file
Download raw file
Outline
Edit and raw actions

Database: Migrations

  • Introduction
  • Generating Migrations
    • Squashing Migrations
  • Migration Structure
  • Running Migrations
    • Rolling Back Migrations
  • Tables
    • Creating Tables
    • Updating Tables
    • Renaming / Dropping Tables
    • Partitioning Tables
  • Columns
    • Creating Columns
    • Available Column Types
    • Column Modifiers
    • Modifying Columns
    • Renaming Columns
    • Dropping Columns
  • Indexes
    • Creating Indexes
    • Renaming Indexes
    • Dropping Indexes
    • Foreign Key Constraints
  • Events

Introduction

Migrations are like version control for your database, allowing your team to define and share the application's database schema definition. If you have ever had to tell a teammate to manually add a column to their local database schema after pulling in your changes from source control, you've faced the problem that database migrations solve.

The Hypervel Schema facade provides database agnostic support for creating and manipulating tables across all of Hypervel's supported database systems. Typically, migrations will use this facade to create and modify database tables and columns.

Generating Migrations

You may use the make:migration Artisan command to generate a database migration. The new migration will be placed in your database/migrations directory. Each migration filename contains a timestamp that allows Hypervel to determine the order of the migrations:

php artisan make:migration create_flights_table

Hypervel will use the name of the migration to attempt to guess the name of the table and whether or not the migration will be creating a new table. If Hypervel is able to determine the table name from the migration name, Hypervel will pre-fill the generated migration file with the specified table. Otherwise, you may simply specify the table in the migration file manually.

If you would like to specify a custom path for the generated migration, you may use the --path option when executing the make:migration command. The given path should be relative to your application's base path.

Note

Migration stubs may be customized using stub publishing.

Squashing Migrations

As you build your application, you may accumulate more and more migrations over time. This can lead to your database/migrations directory becoming bloated with potentially hundreds of migrations. If you would like, you may "squash" your migrations into a single SQL file. To get started, execute the schema:dump command:

php artisan schema:dump

# Dump the current database schema and prune all existing migrations...
php artisan schema:dump --prune

To dump the schema without including rows from the migrations table, use the --without-migration-data option:

php artisan schema:dump --without-migration-data

When you execute this command, Hypervel will write a "schema" file to your application's database/schema directory. The schema file's name will correspond to the database connection. Now, when you attempt to migrate your database and no other migrations have been executed, Hypervel will first execute the SQL statements in the schema file of the database connection you are using. After executing the schema file's SQL statements, Hypervel will execute any remaining migrations that were not part of the schema dump.

If your application's tests use a different database connection than the one you typically use during local development, you should ensure you have dumped a schema file using that database connection so that your tests are able to build your database. You may wish to do this after dumping the database connection you typically use during local development:

php artisan schema:dump
php artisan schema:dump --database=testing --prune

You should commit your database schema file to source control so that other new developers on your team may quickly create your application's initial database structure.

To prevent schema dumps in production, call DumpCommand::prohibit from the boot method of your application's AppServiceProvider:

use Hypervel\Database\Console\DumpCommand;

DumpCommand::prohibit($this->app->isProduction());

When prohibited, schema:dump exits without dumping the schema or pruning migrations.

Warning

Migration squashing is only available for the MariaDB, MySQL, PostgreSQL, and SQLite databases and utilizes the database's command-line client.

Migration Structure

A migration class contains two methods: up and down. The up method is used to add new tables, columns, or indexes to your database, while the down method should reverse the operations performed by the up method.

Within both of these methods, you may use the Hypervel schema builder to expressively create and modify tables. To learn about all of the methods available on the Schema builder, check out its documentation. For example, the following migration creates a flights table:

<?php

use Hypervel\Database\Migrations\Migration;
use Hypervel\Database\Schema\Blueprint;
use Hypervel\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('flights', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->string('airline');
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::drop('flights');
    }
};

Setting the Migration Connection

If your migration will be interacting with a database connection other than your application's default database connection, you should set the $connection property of your migration:

/**
 * The database connection that should be used by the migration.
 */
protected ?string $connection = 'pgsql';

/**
 * Run the migrations.
 */
public function up(): void
{
    // ...
}

If one of your application's database connections should use a different connection while running migrations, you may define a migrations_connection option in your database configuration. This is useful when your application uses a pooled connection for normal queries but should run schema operations through a dedicated migration connection:

'pgsql' => [
    'driver' => 'pgsql',
    // ...
    'migrations_connection' => 'pgsql-migrations',
],

'pgsql-migrations' => [
    'driver' => 'pgsql',
    // ...
],

The migration connection must be a terminal target. The target connection may omit migrations_connection or point to itself, but connection chains such as primary to schema to admin are not supported.

Hypervel records migration history in the migrations table on the connection resolved by the command, even when individual migrations run on another connection.

Skipping Migrations

Sometimes a migration might be meant to support a feature that is not yet active and you do not want it to run yet. In this case you may define a shouldRun method on the migration. If the shouldRun method returns false, the migration will be skipped:

use Hypervel\Support\Facades\Config;

/**
 * Determine if this migration should run.
 */
public function shouldRun(): bool
{
    return Config::boolean('features.flights_enabled');
}

Running Migrations

To run all of your outstanding migrations, execute the migrate Artisan command:

php artisan migrate

Before running any migration, Hypervel inspects the default connection and every connection declared by the migration files. If a supported MySQL, MariaDB, PostgreSQL, or SQLite database does not exist, Hypervel offers to create it before any migration is executed. The --force option allows creation without another prompt, while non-interactive commands must provide --force. The --pretend option never creates a database and will fail if a required database is missing.

If you would like to see which migrations have already run and which are still pending, you may use the migrate:status Artisan command:

php artisan migrate:status

Before the first pending migration, migrations whose shouldRun method returns false are shown as skipped. Later unapplied migrations remain pending because their conditions may depend on earlier migrations.

If you provide the --step option to the migrate command, the command will run each migration as its own batch, allowing you to roll back individual migrations later using the migrate:rollback command:

php artisan migrate --step

If you would like to see the SQL statements that will be executed by the migrations without actually running them, you may provide the --pretend flag to the migrate command:

php artisan migrate --pretend

If you would like Hypervel to load a schema dump from a custom path before running outstanding migrations, you may provide the --schema-path option:

php artisan migrate --schema-path=database/schema/testing-schema.sql

If you would like the migrate command to return a successful exit code when an error occurs, you may provide the --graceful option:

php artisan migrate --graceful

Isolating Migration Execution

If you are deploying your application across multiple servers and running migrations as part of your deployment process, you likely do not want two servers attempting to migrate the database at the same time. To avoid this, you may use the isolated option when invoking the migrate command.

When the isolated option is provided, Hypervel will acquire an atomic lock using your application's cache driver before attempting to run your migrations. All other attempts to run the migrate command while that lock is held will not execute; however, the command will still exit with a successful exit status code:

php artisan migrate --isolated

Warning

To utilize this feature, your application must be using the redis, database, file, or array cache driver as your application's default cache driver. If migrations may be started from multiple servers or containers, use a shared driver such as redis or database.

Forcing Migrations to Run in Production

Some migration operations are destructive, which means they may cause you to lose data. In order to protect you from running these commands against your production database, you will be prompted for confirmation before the commands are executed. To force the commands to run without a prompt, use the --force flag:

php artisan migrate --force

To prevent destructive database commands from running in production at all, even with --force, call the DB facade's prohibitDestructiveCommands method from the boot method of your application's AppServiceProvider. This prohibits the db:wipe, migrate:fresh, migrate:refresh, migrate:reset, and migrate:rollback commands:

use Hypervel\Support\Facades\DB;

DB::prohibitDestructiveCommands($this->app->isProduction());

Rolling Back Migrations

To roll back the latest migration operation, you may use the rollback Artisan command. This command rolls back the last "batch" of migrations, which may include multiple migration files:

php artisan migrate:rollback

You may roll back a limited number of migrations by providing the step option to the rollback command. For example, the following command will roll back the last five migrations:

php artisan migrate:rollback --step=5

You may roll back a specific "batch" of migrations by providing the batch option to the rollback command, where the batch option corresponds to a batch value within your application's migrations database table. For example, the following command will roll back all migrations in batch three:

php artisan migrate:rollback --batch=3

If you would like to see the SQL statements that will be executed by the migrations without actually running them, you may provide the --pretend flag to the migrate:rollback command:

php artisan migrate:rollback --pretend

The migrate:reset command will roll back all of your application's migrations:

php artisan migrate:reset

Roll Back and Migrate Using a Single Command

The migrate:refresh command will roll back all of your migrations and then execute the migrate command. This command effectively re-creates your entire database:

php artisan migrate:refresh

# Refresh the database and run all database seeds...
php artisan migrate:refresh --seed

You may roll back and re-migrate a limited number of migrations by providing the step option to the refresh command. For example, the following command will roll back and re-migrate the last five migrations:

php artisan migrate:refresh --step=5

Drop All Tables and Migrate

The migrate:fresh command will drop all tables from the databases managed by your migration files and then execute the migrate command:

php artisan migrate:fresh

php artisan migrate:fresh --seed

Hypervel reads each migration's $connection property or getConnection method before making any changes. It resolves migrations_connection for those connections, creates supported databases that do not exist, and wipes every declared database that already exists. A reachable database is wiped even when it does not contain a migrations table.

Before making any changes, the command lists the connections that will be created and the connections that will be wiped. In production, declining the confirmation leaves every database unchanged.

The --database option selects the default migration connection. Migrations that declare another connection are still included. The connection name should correspond to a connection defined in your application's database configuration file:

php artisan migrate:fresh --database=admin

A migration's connection should remain stable for that migration. A migration is included in the reset even when its shouldRun method currently returns false, since it may have created tables during an earlier run.

Hypervel cannot discover a connection that is only selected by calling Schema::connection('other') inside the up method. Split cross-database work into separate migrations and declare the connection on each migration so migrate:fresh can reset every database before rebuilding it.

Warning

The migrate:fresh command will drop all database tables from every declared migration connection, regardless of their prefix. This command should be used with caution when developing on databases that are shared with other applications.

Tables

Creating Tables

To create a new database table, use the create method on the Schema facade. The create method accepts two arguments: the first is the name of the table, while the second is a closure which receives a Blueprint object that may be used to define the new table:

use Hypervel\Database\Schema\Blueprint;
use Hypervel\Support\Facades\Schema;

Schema::create('users', function (Blueprint $table) {
    $table->id();
    $table->string('name');
    $table->string('email');
    $table->timestamps();
});

When creating the table, you may use any of the schema builder's column methods to define the table's columns.

Determining Table / Column Existence

You may determine the existence of a table, column, index, or foreign key using the hasTable, hasColumn, hasIndex, and hasForeignKey methods:

if (Schema::hasTable('users')) {
    // The "users" table exists...
}

if (Schema::hasColumn('users', 'email')) {
    // The "users" table exists and has an "email" column...
}

if (Schema::hasIndex('users', ['email'], 'unique')) {
    // The "users" table exists and has a unique index on the "email" column...
}

if (Schema::hasForeignKey('posts', ['user_id'])) {
    // The "posts" table has a foreign key on the "user_id" column...
}

The hasForeignKey method accepts either the foreign key name or its column list.

To modify a table only when a column or index exists, use whenTableHasColumn or whenTableHasIndex. Their whenTableDoesntHaveColumn and whenTableDoesntHaveIndex counterparts run the callback only when the column or index is absent:

Schema::whenTableDoesntHaveColumn('users', 'email', function (Blueprint $table) {
    $table->string('email');
});

Schema::whenTableDoesntHaveIndex('users', ['email'], function (Blueprint $table) {
    $table->unique('email');
}, 'unique');

The index methods accept an index name or an array of column names as their second argument. You may pass an index type, such as unique, as the fourth argument.

Database Connection and Table Options

If you want to perform a schema operation on a database connection that is not your application's default connection, use the connection method:

Schema::connection('sqlite')->create('users', function (Blueprint $table) {
    $table->id();
});

In addition, a few other properties and methods may be used to define other aspects of the table's creation. The engine property may be used to specify the table's storage engine when using MariaDB or MySQL:

Schema::create('users', function (Blueprint $table) {
    $table->engine('InnoDB');

    // ...
});

The charset and collation properties may be used to specify the character set and collation for the created table when using MariaDB or MySQL:

Schema::create('users', function (Blueprint $table) {
    $table->charset('utf8mb4');
    $table->collation('utf8mb4_unicode_ci');

    // ...
});

The temporary method may be used to indicate that the table should be "temporary". Temporary tables are only visible to the current connection's database session and are dropped automatically when the connection is closed:

Schema::create('calculations', function (Blueprint $table) {
    $table->temporary();

    // ...
});

If you would like to add a "comment" to a database table, you may invoke the comment method on the table instance. Table comments are currently only supported by MariaDB, MySQL, and PostgreSQL:

Schema::create('calculations', function (Blueprint $table) {
    $table->comment('Business calculations');

    // ...
});

Updating Tables

The table method on the Schema facade may be used to update existing tables. Like the create method, the table method accepts two arguments: the name of the table and a closure that receives a Blueprint instance you may use to add columns or indexes to the table:

use Hypervel\Database\Schema\Blueprint;
use Hypervel\Support\Facades\Schema;

Schema::table('users', function (Blueprint $table) {
    $table->integer('votes');
});

Renaming / Dropping Tables

To rename an existing database table, use the rename method:

use Hypervel\Support\Facades\Schema;

Schema::rename($from, $to);

To drop an existing table, you may use the drop or dropIfExists methods:

Schema::drop('users');

Schema::dropIfExists('users');

Renaming Tables With Foreign Keys

Before renaming a table, you should verify that any foreign key constraints on the table have an explicit name in your migration files instead of letting Hypervel assign a convention based name. Otherwise, the foreign key constraint name will refer to the old table name.

Partitioning Tables

PostgreSQL can split a large table into partitions, each holding a range of its rows. Your queries still use the table itself, but old data can be removed by dropping a whole partition instead of deleting rows one at a time, which is much faster and leaves no bloat behind. This makes partitioning a good fit for large, append-heavy tables such as logs and event histories. Range partitioning requires PostgreSQL 10 or later; primary keys and unique indexes on the partitioned table, as in the example below, require PostgreSQL 11 or later.

To partition a table, call the partitionByRange method with the columns that decide which partition each row belongs to. The table's primary key and unique indexes must include all of these columns:

Schema::create('events', function (Blueprint $table) {
    $table->id();
    $table->string('type');
    $table->timestamp('created_at');

    $table->primary(['id', 'created_at']);
    $table->partitionByRange('created_at');
});

A row can only be stored once a partition covers it, so create partitions ahead of time using the createRangePartition method. Each bound lists one value per partition column. The lower bound is inclusive and the upper bound is exclusive:

Schema::createRangePartition('events', 'events_2026_10', from: ['2026-10-01'], to: ['2026-11-01']);

The getPartitions method returns a table's partitions and their bounds. The getTables method lists partitions as well, and the partition_of key of each partition contains the schema-qualified name of the table it belongs to:

$partitions = Schema::getPartitions('events');

// [['name' => 'events_2026_10', 'schema' => 'public', 'schema_qualified_name' => 'public.events_2026_10', 'bounds' => "FOR VALUES FROM ('2026-10-01 00:00:00') TO ('2026-11-01 00:00:00')"]]

A partition is a table of its own, so you may drop it, along with all of its rows, using the drop method. Dropping a partition briefly locks the whole partitioned table, so it waits for any queries that are using the table. To avoid waiting behind a long-running query, set a lock timeout in the same transaction. If the timeout passes, the drop fails and leaves the partition in place, so you may try again later:

use Hypervel\Support\Facades\DB;

DB::transaction(function () {
    DB::statement("set local lock_timeout = '5s'");

    Schema::drop('events_2026_01');
});

Columns

Creating Columns

The table method on the Schema facade may be used to update existing tables. Like the create method, the table method accepts two arguments: the name of the table and a closure that receives a Hypervel\Database\Schema\Blueprint instance you may use to add columns to the table:

use Hypervel\Database\Schema\Blueprint;
use Hypervel\Support\Facades\Schema;

Schema::table('users', function (Blueprint $table) {
    $table->integer('votes');
});

Available Column Types

The schema builder blueprint offers a variety of methods that correspond to the different types of columns you can add to your database tables. Each of the available methods are listed in the table below:

<style> .collection-method-list > p { columns: 10.8em 3; -moz-columns: 10.8em 3; -webkit-columns: 10.8em 3; } .collection-method-list a { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .collection-method code { font-size: 14px; } .collection-method:not(.first-collection-method) { margin-top: 50px; } </style>

Boolean Types

boolean

String & Text Types

char longText mediumText string text tinyText

Numeric Types

bigIncrements bigInteger decimal double float id increments integer mediumIncrements mediumInteger smallIncrements smallInteger tinyIncrements tinyInteger unsignedBigInteger unsignedInteger unsignedMediumInteger unsignedSmallInteger unsignedTinyInteger

Date & Time Types

dateTime dateTimeTz date time timeTz timestamp timestamps timestampsTz softDeletes softDeletesTz year

Binary Types

binary longBinary mediumBinary

Object & Json Types

json jsonb

UUID & ULID Types

ulid ulidMorphs uuid uuidMorphs nullableUlidMorphs nullableUuidMorphs

Spatial Types

geography geometry

Relationship Types

foreignId foreignIdFor foreignUlid foreignUlidFor foreignUuid foreignUuidFor morphs nullableMorphs

Specialty Types

enum set macAddress ipAddress rememberToken vector tsvector

bigIncrements() {.collection-method .first-collection-method}

The bigIncrements method creates an auto-incrementing UNSIGNED BIGINT (primary key) equivalent column:

$table->bigIncrements('id');

bigInteger() {.collection-method}

The bigInteger method creates a BIGINT equivalent column:

$table->bigInteger('votes');

binary() {.collection-method}

The binary method creates a BLOB equivalent column:

$table->binary('photo');

When utilizing MySQL or MariaDB, you may pass length and fixed arguments to create VARBINARY or BINARY equivalent column:

$table->binary('data', length: 16); // VARBINARY(16)

$table->binary('data', length: 16, fixed: true); // BINARY(16)

On MySQL and MariaDB, a BLOB column holds up to 64 KB. Use the mediumBinary or longBinary methods for larger values.

boolean() {.collection-method}

The boolean method creates a BOOLEAN equivalent column:

$table->boolean('confirmed');

char() {.collection-method}

The char method creates a CHAR equivalent column with of a given length:

$table->char('name', length: 100);

dateTimeTz() {.collection-method}

The dateTimeTz method creates a DATETIME (with timezone) equivalent column with an optional fractional seconds precision:

$table->dateTimeTz('created_at', precision: 0);

dateTime() {.collection-method}

The dateTime method creates a DATETIME equivalent column with an optional fractional seconds precision:

$table->dateTime('created_at', precision: 0);

date() {.collection-method}

The date method creates a DATE equivalent column:

$table->date('created_at');

decimal() {.collection-method}

The decimal method creates a DECIMAL equivalent column with the given precision (total digits) and scale (decimal digits):

$table->decimal('amount', total: 8, places: 2);

double() {.collection-method}

The double method creates a DOUBLE equivalent column:

$table->double('amount');

enum() {.collection-method}

The enum method creates a ENUM equivalent column with the given valid values:

$table->enum('difficulty', ['easy', 'hard']);

Of course, you may use the Enum::cases() method instead of manually defining an array of allowed values:

use App\Enums\Difficulty;

$table->enum('difficulty', Difficulty::cases());

float() {.collection-method}

The float method creates a FLOAT equivalent column with the given precision:

$table->float('amount', precision: 53);

foreignId() {.collection-method}

The foreignId method creates an UNSIGNED BIGINT equivalent column:

$table->foreignId('user_id');

foreignIdFor() {.collection-method}

The foreignIdFor method adds a {column}_id equivalent column for a given model class. The column type will be UNSIGNED BIGINT, CHAR(36), or CHAR(26) depending on the model key type:

$table->foreignIdFor(User::class);

foreignUlid() {.collection-method}

The foreignUlid method creates a ULID equivalent column:

$table->foreignUlid('user_id');

foreignUlidFor() {.collection-method}

The foreignUlidFor method adds a ULID column for a model that uses ULID keys. By default, the column name, referenced table, and referenced model key are derived from the model:

$table->foreignUlidFor(User::class);

foreignUuid() {.collection-method}

The foreignUuid method creates a UUID equivalent column:

$table->foreignUuid('user_id');

foreignUuidFor() {.collection-method}

The foreignUuidFor method adds a UUID column for a model that uses UUID keys. By default, the column name, referenced table, and referenced model key are derived from the model:

$table->foreignUuidFor(User::class);

geography() {.collection-method}

The geography method creates a GEOGRAPHY equivalent column with the given spatial type and SRID (Spatial Reference System Identifier):

$table->geography('coordinates', subtype: 'point', srid: 4326);

Note

Support for spatial types depends on your database driver. Please refer to your database's documentation. If your application is utilizing a PostgreSQL database, you must install the PostGIS extension before the geography method may be used.

geometry() {.collection-method}

The geometry method creates a GEOMETRY equivalent column with the given spatial type and SRID (Spatial Reference System Identifier):

$table->geometry('positions', subtype: 'point', srid: 0);

Note

Support for spatial types depends on your database driver. Please refer to your database's documentation. If your application is utilizing a PostgreSQL database, you must install the PostGIS extension before the geometry method may be used.

id() {.collection-method}

The id method is an alias of the bigIncrements method. By default, the method will create an id column; however, you may pass a column name if you would like to assign a different name to the column:

$table->id();

increments() {.collection-method}

The increments method creates an auto-incrementing UNSIGNED INTEGER equivalent column as a primary key:

$table->increments('id');

integer() {.collection-method}

The integer method creates an INTEGER equivalent column:

$table->integer('votes');

ipAddress() {.collection-method}

The ipAddress method creates a VARCHAR equivalent column:

$table->ipAddress('visitor');

When using PostgreSQL, an INET column will be created.

json() {.collection-method}

The json method creates a JSON equivalent column:

$table->json('options');

When using SQLite, a TEXT column will be created.

jsonb() {.collection-method}

The jsonb method creates a JSONB equivalent column:

$table->jsonb('options');

When using SQLite, a TEXT column will be created.

longBinary() {.collection-method}

The longBinary method creates a LONGBLOB equivalent column, which holds up to 4 GB on MySQL and MariaDB:

$table->longBinary('archive');

longText() {.collection-method}

The longText method creates a LONGTEXT equivalent column:

$table->longText('description');

To store binary data, use the longBinary method instead.

macAddress() {.collection-method}

The macAddress method creates a column that is intended to hold a MAC address. Some database systems, such as PostgreSQL, have a dedicated column type for this type of data. Other database systems will use a string equivalent column:

$table->macAddress('device');

mediumBinary() {.collection-method}

The mediumBinary method creates a MEDIUMBLOB equivalent column, which holds up to 16 MB on MySQL and MariaDB:

$table->mediumBinary('payload');

mediumIncrements() {.collection-method}

The mediumIncrements method creates an auto-incrementing UNSIGNED MEDIUMINT equivalent column as a primary key:

$table->mediumIncrements('id');

mediumInteger() {.collection-method}

The mediumInteger method creates a MEDIUMINT equivalent column:

$table->mediumInteger('votes');

mediumText() {.collection-method}

The mediumText method creates a MEDIUMTEXT equivalent column:

$table->mediumText('description');

To store binary data, use the mediumBinary method instead.

morphs() {.collection-method}

The morphs method is a convenience method that adds a {column}_type VARCHAR equivalent column and a {column}_id equivalent column. The column type for the {column}_id will be UNSIGNED BIGINT, CHAR(36), or CHAR(26) depending on the model key type.

This method is intended to be used when defining the columns necessary for a polymorphic Eloquent relationship. In the following example, taggable_type and taggable_id columns would be created:

$table->morphs('taggable');

nullableMorphs() {.collection-method}

The method is similar to the morphs method; however, the columns that are created will be "nullable":

$table->nullableMorphs('taggable');

nullableUlidMorphs() {.collection-method}

The method is similar to the ulidMorphs method; however, the columns that are created will be "nullable":

$table->nullableUlidMorphs('taggable');

nullableUuidMorphs() {.collection-method}

The method is similar to the uuidMorphs method; however, the columns that are created will be "nullable":

$table->nullableUuidMorphs('taggable');

rememberToken() {.collection-method}

The rememberToken method creates a nullable, VARCHAR(100) equivalent column that is intended to store the current "remember me" authentication token:

$table->rememberToken();

set() {.collection-method}

The set method creates a SET equivalent column with the given list of valid values:

$table->set('flavors', ['strawberry', 'vanilla']);

smallIncrements() {.collection-method}

The smallIncrements method creates an auto-incrementing UNSIGNED SMALLINT equivalent column as a primary key:

$table->smallIncrements('id');

smallInteger() {.collection-method}

The smallInteger method creates a SMALLINT equivalent column:

$table->smallInteger('votes');

softDeletesTz() {.collection-method}

The softDeletesTz method adds a nullable deleted_at TIMESTAMP (with timezone) equivalent column with an optional fractional seconds precision. This column is intended to store the deleted_at timestamp needed for Eloquent's "soft delete" functionality:

$table->softDeletesTz('deleted_at', precision: 0);

softDeletes() {.collection-method}

The softDeletes method adds a nullable deleted_at TIMESTAMP equivalent column with an optional fractional seconds precision. This column is intended to store the deleted_at timestamp needed for Eloquent's "soft delete" functionality:

$table->softDeletes('deleted_at', precision: 0);

string() {.collection-method}

The string method creates a VARCHAR equivalent column of the given length:

$table->string('name', length: 100);

text() {.collection-method}

The text method creates a TEXT equivalent column:

$table->text('description');

When utilizing MySQL or MariaDB, you may apply a binary character set to the column in order to create a BLOB equivalent column:

$table->text('data')->charset('binary'); // BLOB

timeTz() {.collection-method}

The timeTz method creates a TIME (with timezone) equivalent column with an optional fractional seconds precision:

$table->timeTz('sunrise', precision: 0);

time() {.collection-method}

The time method creates a TIME equivalent column with an optional fractional seconds precision:

$table->time('sunrise', precision: 0);

timestampTz() {.collection-method}

The timestampTz method creates a TIMESTAMP (with timezone) equivalent column with an optional fractional seconds precision:

$table->timestampTz('added_at', precision: 0);

timestamp() {.collection-method}

The timestamp method creates a TIMESTAMP equivalent column with an optional fractional seconds precision:

$table->timestamp('added_at', precision: 0);

timestampsTz() {.collection-method}

The timestampsTz method creates created_at and updated_at TIMESTAMP (with timezone) equivalent columns with an optional fractional seconds precision:

$table->timestampsTz(precision: 0);

timestamps() {.collection-method}

The timestamps method creates created_at and updated_at TIMESTAMP equivalent columns with an optional fractional seconds precision:

$table->timestamps(precision: 0);

tinyIncrements() {.collection-method}

The tinyIncrements method creates an auto-incrementing UNSIGNED TINYINT equivalent column as a primary key:

$table->tinyIncrements('id');

tinyInteger() {.collection-method}

The tinyInteger method creates a TINYINT equivalent column:

$table->tinyInteger('votes');

tinyText() {.collection-method}

The tinyText method creates a TINYTEXT equivalent column:

$table->tinyText('notes');

When utilizing MySQL or MariaDB, you may apply a binary character set to the column in order to create a TINYBLOB equivalent column:

$table->tinyText('data')->charset('binary'); // TINYBLOB

unsignedBigInteger() {.collection-method}

The unsignedBigInteger method creates an UNSIGNED BIGINT equivalent column:

$table->unsignedBigInteger('votes');

unsignedInteger() {.collection-method}

The unsignedInteger method creates an UNSIGNED INTEGER equivalent column:

$table->unsignedInteger('votes');

unsignedMediumInteger() {.collection-method}

The unsignedMediumInteger method creates an UNSIGNED MEDIUMINT equivalent column:

$table->unsignedMediumInteger('votes');

unsignedSmallInteger() {.collection-method}

The unsignedSmallInteger method creates an UNSIGNED SMALLINT equivalent column:

$table->unsignedSmallInteger('votes');

unsignedTinyInteger() {.collection-method}

The unsignedTinyInteger method creates an UNSIGNED TINYINT equivalent column:

$table->unsignedTinyInteger('votes');

ulidMorphs() {.collection-method}

The ulidMorphs method is a convenience method that adds a {column}_type VARCHAR equivalent column and a {column}_id CHAR(26) equivalent column.

This method is intended to be used when defining the columns necessary for a polymorphic Eloquent relationship that use ULID identifiers. In the following example, taggable_type and taggable_id columns would be created:

$table->ulidMorphs('taggable');

uuidMorphs() {.collection-method}

The uuidMorphs method is a convenience method that adds a {column}_type VARCHAR equivalent column and a {column}_id CHAR(36) equivalent column.

This method is intended to be used when defining the columns necessary for a polymorphic Eloquent relationship that use UUID identifiers. In the following example, taggable_type and taggable_id columns would be created:

$table->uuidMorphs('taggable');

ulid() {.collection-method}

The ulid method creates a ULID equivalent column:

$table->ulid('id');

uuid() {.collection-method}

The uuid method creates a UUID equivalent column:

$table->uuid('id');

vector() {.collection-method}

The vector method creates a vector equivalent column:

$table->vector('embedding', dimensions: 1536);

Vector columns are supported on PostgreSQL connections using the pgvector extension and MariaDB 11.7 or later. When utilizing PostgreSQL, pgvector must be loaded before vector columns can be created:

Schema::ensureVectorExtensionExists();

To speed up vector similarity queries, you may add a vector index to the column. Calling the index method on a vector column creates a vector index using cosine distance:

$table->vector('embedding', dimensions: 1536)->index();

tsvector() {.collection-method}

The tsvector method creates a PostgreSQL tsvector equivalent column for storing precomputed full-text search vectors:

$table->tsvector('search_vector');

year() {.collection-method}

The year method creates a YEAR equivalent column:

$table->year('birth_year');

Column Modifiers

In addition to the column types listed above, there are several column "modifiers" you may use when adding a column to a database table. For example, to make the column "nullable", you may use the nullable method:

use Hypervel\Database\Schema\Blueprint;
use Hypervel\Support\Facades\Schema;

Schema::table('users', function (Blueprint $table) {
    $table->string('email')->nullable();
});

The following table contains all of the available column modifiers. This list does not include index modifiers:

Modifier Description
->after('column') Place the column "after" another column (MariaDB / MySQL).
->autoIncrement() Set INTEGER columns as auto-incrementing (primary key).
->charset('utf8mb4') Specify a character set for the column (MariaDB / MySQL).
->collation('utf8mb4_unicode_ci') Specify a collation for the column.
->comment('my comment') Add a comment to a column (MariaDB / MySQL / PostgreSQL).
->default($value) Specify a "default" value for the column.
->first() Place the column "first" in the table (MariaDB / MySQL).
->from($integer) Set the starting value of an auto-incrementing field (MariaDB / MySQL / PostgreSQL).
->instant() Add or modify the column using an instant operation (MySQL).
->invisible() Make the column "invisible" to SELECT * queries (MariaDB / MySQL).
->lock($mode) Specify a lock mode for the column operation (MySQL).
->nullable($value = true) Allow NULL values to be inserted into the column.
->storedAs($expression) Create a stored generated column (MariaDB / MySQL / PostgreSQL / SQLite).
->unsigned() Set INTEGER columns as UNSIGNED (MariaDB / MySQL).
->using($expression) Specify a casting expression when changing the column type (PostgreSQL).
->useCurrent() Set TIMESTAMP columns to use CURRENT_TIMESTAMP as default value.
->useCurrentOnUpdate() Set TIMESTAMP columns to use CURRENT_TIMESTAMP when a record is updated (MariaDB / MySQL).
->virtualAs($expression) Create a virtual generated column (MariaDB / MySQL / PostgreSQL 18+ / SQLite).
->generatedAs($expression) Create an identity column with specified sequence options (PostgreSQL).
->always() Defines the precedence of sequence values over input for an identity column (PostgreSQL).

Default Expressions

The default modifier accepts a value or a Hypervel\Database\Query\Expression instance. Using an Expression instance will prevent Hypervel from wrapping the value in quotes and allow you to use database specific functions. One situation where this is particularly useful is when you need to assign default values to JSON columns:

<?php

use Hypervel\Support\Facades\Schema;
use Hypervel\Database\Schema\Blueprint;
use Hypervel\Database\Query\Expression;
use Hypervel\Database\Migrations\Migration;

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('flights', function (Blueprint $table) {
            $table->id();
            $table->json('movies')->default(new Expression('(JSON_ARRAY())'));
            $table->timestamps();
        });
    }
};

Warning

Support for default expressions depends on your database driver, database version, and the field type. Please refer to your database's documentation.

Column Order

When using the MariaDB or MySQL database, the after method may be used to add columns after an existing column in the schema:

$table->after('password', function (Blueprint $table) {
    $table->string('address_line1');
    $table->string('address_line2');
    $table->string('city');
});

Instant Column Operations

When using MySQL, you may chain the instant modifier onto a column definition to indicate that the column should be added or modified using MySQL's "instant" algorithm. This algorithm allows certain schema changes to be performed without a full table rebuild, making them nearly instantaneous regardless of table size:

$table->string('name')->nullable()->instant();

Instant column additions can only append columns to the end of the table, so the instant modifier cannot be combined with the after or first modifiers. In addition, the algorithm does not support all column types or operations. If the requested operation is incompatible, MySQL will raise an error.

Please refer to MySQL's documentation to determine which operations are compatible with instant column modifications.

DDL Locking

When using MySQL, you may chain the lock modifier onto column, index, or foreign key definitions to control table locking during schema operations. MySQL supports several lock modes: none allows concurrent reads and writes, shared allows concurrent reads but blocks writes, exclusive blocks all concurrent access, and default lets MySQL choose the most appropriate mode:

$table->string('name')->lock('none');

$table->index('email')->lock('shared');

If the requested lock mode is incompatible with the operation, MySQL will raise an error. When the instant modifier is used, MySQL permits only the default lock mode.

Modifying Columns

The change method allows you to modify the type and attributes of existing columns. For example, you may wish to increase the size of a string column. To see the change method in action, let's increase the size of the name column from 25 to 50. To accomplish this, we simply define the new state of the column and then call the change method:

Schema::table('users', function (Blueprint $table) {
    $table->string('name', 50)->change();
});

When modifying a column, you must explicitly include all the modifiers you want to keep on the column definition - any missing attribute will be dropped. For example, to retain the unsigned, default, and comment attributes, you must call each modifier explicitly when changing the column:

Schema::table('users', function (Blueprint $table) {
    $table->integer('votes')->unsigned()->default(1)->comment('my comment')->change();
});

The change method does not change the indexes of the column. Therefore, you may use index modifiers to explicitly add or drop an index when modifying the column:

// Add an index...
$table->bigIncrements('id')->primary()->change();

// Drop an index...
$table->char('postal_code', 10)->unique(false)->change();

PostgreSQL Column Modifications

When changing a column's type on PostgreSQL, you may use the using modifier to specify the expression used to cast the existing values:

Schema::table('users', function (Blueprint $table) {
    $table->date('birthday')->using('birthday::date')->change();
});

On PostgreSQL 17 and later, you may change a stored generated expression using storedAs($expression)->change(). Virtual generated expressions may be changed using virtualAs($expression)->change() on PostgreSQL 18 and later. Changing a stored expression recalculates the column's existing values.

Warning

A PostgreSQL bug can make these expression changes fail on columns with CHECK constraints, or NOT NULL constraints on PostgreSQL 18. For affected columns, issue ALTER TABLE ... ALTER COLUMN ... SET EXPRESSION AS (...) separately through DB::statement() instead of change().

To turn a stored generated column into an ordinary column on PostgreSQL 13 and later, use storedAs(null)->change(). Existing values are preserved, and you may specify a new default in the same definition. PostgreSQL does not support removing a virtual column's expression.

Renaming Columns

To rename a column, you may use the renameColumn method provided by the schema builder:

Schema::table('users', function (Blueprint $table) {
    $table->renameColumn('from', 'to');
});

Dropping Columns

To drop a column, you may use the dropColumn method on the schema builder:

Schema::table('users', function (Blueprint $table) {
    $table->dropColumn('votes');
});

You may drop multiple columns from a table by passing an array of column names to the dropColumn method:

Schema::table('users', function (Blueprint $table) {
    $table->dropColumn(['votes', 'avatar', 'location']);
});

Available Command Aliases

Hypervel provides several convenient methods related to dropping common types of columns. Each of these methods is described in the table below:

Command Description
$table->dropMorphs('morphable'); Drop the morphable_type and morphable_id columns.
$table->dropRememberToken(); Drop the remember_token column.
$table->dropSoftDeletes(); Drop the deleted_at column.
$table->dropSoftDeletesTz(); Alias of dropSoftDeletes() method.
$table->dropTimestamps(); Drop the created_at and updated_at columns.
$table->dropTimestampsTz(); Alias of dropTimestamps() method.

Indexes

Creating Indexes

The Hypervel schema builder supports several types of indexes. The following example creates a new email column and specifies that its values should be unique. To create the index, we can chain the unique method onto the column definition:

use Hypervel\Database\Schema\Blueprint;
use Hypervel\Support\Facades\Schema;

Schema::table('users', function (Blueprint $table) {
    $table->string('email')->unique();
});

Alternatively, you may create the index after defining the column. To do so, you should call the unique method on the schema builder blueprint. This method accepts the name of the column that should receive a unique index:

$table->unique('email');

You may even pass an array of columns to an index method to create a compound (or composite) index:

$table->index(['account_id', 'created_at']);

When creating an index, Hypervel will automatically generate an index name based on the table, column names, and the index type, but you may pass a second argument to the method to specify the index name yourself:

$table->unique('email', 'unique_email');

When a connection uses a table prefix and prefix_indexes is enabled, automatically generated index names include the configured prefix. Explicit names remain unchanged unless you qualify them using the Schema::qualifyIndexName method:

$table->unique('email', Schema::qualifyIndexName('unique_email'));

The qualified name uses the connection resolved by the Schema facade. Within a migration, this is the migration's connection. When qualifying a name elsewhere, you should select the connection explicitly:

$name = Schema::connection('tenant')->qualifyIndexName('unique_email');

Use the same qualified name when checking, renaming, or dropping the index.

Available Index Types

Hypervel's schema builder blueprint class provides methods for creating each type of index supported by Hypervel. Each index method accepts an optional second argument to specify the name of the index. If omitted, the name will be derived from the names of the table and column(s) used for the index, as well as the index type. Each of the available index methods is described in the table below:

Command Description
$table->primary('id'); Adds a primary key.
$table->primary(['id', 'parent_id']); Adds composite keys.
$table->unique('email'); Adds a unique index.
$table->index('state'); Adds an index.
$table->fullText('body'); Adds a full text index (MariaDB / MySQL / PostgreSQL).
$table->fullText('body')->language('english'); Adds a full text index of the specified language (PostgreSQL).
$table->spatialIndex('location'); Adds a spatial index (except SQLite).
$table->rawIndex('(lower(email))', 'users_email_lower_index'); Adds an index from a raw expression.
$table->vectorIndex('embedding'); Adds a vector index (MariaDB / PostgreSQL).

Online Index Creation

By default, creating an index on a large table can lock the table and block reads or writes while the index is being built. When using PostgreSQL, you may chain the online method onto an index definition to create the index without locking the table, allowing your application to continue reading and writing data during index creation:

$table->unique('email')->online();

When using PostgreSQL, this adds the CONCURRENTLY option to the index creation statement.

When using MySQL, you may chain the inplace modifier onto an index or foreign key definition to specify that the operation should use the INPLACE algorithm:

$table->index('email')->inplace();

$table->foreign('user_id')->references('id')->on('users')->inplace();

The inplace modifier may be combined with the lock modifier to control table locking during the operation:

$table->index('email')->inplace()->lock('none');

When using the inplace modifier for a foreign key operation, foreign key checks must be disabled.

Refer to MySQL's documentation to determine which operations support the INPLACE algorithm and lock modes.

Partial Indexes

To create an index that contains only rows where a column is not null, you may chain the whereNotNull method onto an ordinary index definition:

$table->index(
    ['account_id', 'archived_at'],
    'records_archived_index',
)->whereNotNull('archived_at');

PostgreSQL and SQLite create a partial index for this definition. MySQL and MariaDB do not support partial indexes, so they create the same ordinary index over every row. This allows the same migration to run on each supported database.

The whereNotNull modifier must be chained onto the index definition returned by the table-level index or rawIndex method. Column index shorthand does not support this modifier. Calling whereNotNull on a column definition or a primary, unique, full text, spatial, or vector index throws a LogicException.

Renaming Indexes

To rename an index, you may use the renameIndex method provided by the schema builder blueprint. This method accepts the current index name as its first argument and the desired name as its second argument:

$table->renameIndex('from', 'to');

Dropping Indexes

To drop an index, you must specify the index's name. By default, Hypervel automatically assigns an index name based on the table name, the name of the indexed column, and the index type. Here are some examples:

Command Description
$table->dropPrimary('users_id_primary'); Drop a primary key from the "users" table.
$table->dropUnique('users_email_unique'); Drop a unique index from the "users" table.
$table->dropIndex('geo_state_index'); Drop a basic index from the "geo" table.
$table->dropFullText('posts_body_fulltext'); Drop a full text index from the "posts" table.
$table->dropSpatialIndex('geo_location_spatialindex'); Drop a spatial index from the "geo" table (except SQLite).
$table->dropVectorIndex('posts_embeddings_vectorindex'); Drop a vector index from the "posts" table (MariaDB / PostgreSQL).

If you pass an array of columns into a method that drops indexes, the conventional index name will be generated based on the table name, columns, and index type:

Schema::table('geo', function (Blueprint $table) {
    $table->dropIndex(['state']); // Drops index 'geo_state_index'
});

Foreign Key Constraints

Hypervel also provides support for creating foreign key constraints, which are used to force referential integrity at the database level. For example, let's define a user_id column on the posts table that references the id column on a users table:

use Hypervel\Database\Schema\Blueprint;
use Hypervel\Support\Facades\Schema;

Schema::table('posts', function (Blueprint $table) {
    $table->unsignedBigInteger('user_id');

    $table->foreign('user_id')->references('id')->on('users');
});

Since this syntax is rather verbose, Hypervel provides additional, terser methods that use conventions to provide a better developer experience. When using the foreignId method to create your column, the example above can be rewritten like so:

Schema::table('posts', function (Blueprint $table) {
    $table->foreignId('user_id')->constrained();
});

The foreignId method creates an UNSIGNED BIGINT equivalent column, while the constrained method will use conventions to determine the table and column being referenced. If your table name does not match Hypervel's conventions, you may manually provide it to the constrained method. In addition, the name that should be assigned to the generated index may be specified as well. Index prefixing does not modify explicit names, so you should use Schema::qualifyIndexName when the name should include a configured table prefix, as described under creating indexes:

Schema::table('posts', function (Blueprint $table) {
    $table->foreignId('user_id')->constrained(
        table: 'users', indexName: 'posts_user_id'
    );
});

You may also specify the desired action for the "on delete" and "on update" properties of the constraint:

$table->foreignId('user_id')
    ->constrained()
    ->onUpdate('cascade')
    ->onDelete('cascade');

An alternative, expressive syntax is also provided for these actions:

Method Description
$table->cascadeOnUpdate(); Updates should cascade.
$table->restrictOnUpdate(); Updates should be restricted.
$table->nullOnUpdate(); Updates should set the foreign key value to null.
$table->noActionOnUpdate(); No action on updates.
$table->cascadeOnDelete(); Deletes should cascade.
$table->restrictOnDelete(); Deletes should be restricted.
$table->nullOnDelete(); Deletes should set the foreign key value to null.
$table->noActionOnDelete(); Prevents deletes if child records exist.

Any additional column modifiers must be called before the constrained method:

$table->foreignId('user_id')
    ->nullable()
    ->constrained();

Dropping Foreign Keys

To drop a foreign key, you may use the dropForeign method, passing the name of the foreign key constraint to be deleted as an argument. Foreign key constraints use the same naming convention as indexes. In other words, the foreign key constraint name is based on the name of the table and the columns in the constraint, followed by a "_foreign" suffix:

$table->dropForeign('posts_user_id_foreign');

Alternatively, you may pass an array containing the column name that holds the foreign key to the dropForeign method. The array will be converted to a foreign key constraint name using Hypervel's constraint naming conventions:

$table->dropForeign(['user_id']);

To drop a conventionally named foreign key constraint and its column in a single method call, you may use the dropConstrainedForeignId method:

$table->dropConstrainedForeignId('user_id');

You may use the dropConstrainedForeignIdFor method to determine the column name from a model:

$table->dropConstrainedForeignIdFor(User::class);

When dropping an explicitly named foreign key, you may pass the constraint's columns as the first argument and its name as the second:

$table->dropForeign(['tenant_id', 'user_id'], 'posts_user_fk');

When dropping a qualified foreign key name, use the same value returned by Schema::qualifyIndexName.

Toggling Foreign Key Constraints

You may enable or disable foreign key constraints within your migrations by using the following methods:

Schema::enableForeignKeyConstraints();

Schema::disableForeignKeyConstraints();

Schema::withoutForeignKeyConstraints(function () {
    // Constraints disabled within this closure...
});

Note

Hypervel's default SQLite connection enables foreign key constraints. Custom SQLite connections may control this behavior using the foreign_key_constraints configuration option.

Warning

SQLite cannot enable or disable foreign key constraints while a transaction is active, so call these methods before beginning the transaction. On PostgreSQL, they defer only foreign keys created with deferrable(), and only within a transaction. Other foreign keys stay enforced, and on PostgreSQL calling them outside a transaction has no effect.

Events

For convenience, each migration operation will dispatch an event. With the exception of SchemaDumped, SchemaLoaded, and MigrationsPruned, all of the following events implement the Hypervel\Contracts\Database\Events\MigrationEvent interface:

Class Description
Hypervel\Database\Events\DatabaseRefreshed The migrate:fresh or migrate:refresh command has run its migrations, before any seeders run.
Hypervel\Database\Events\MigrationsStarted A batch of migrations is about to be executed.
Hypervel\Database\Events\MigrationsEnded A batch of migrations has finished.
Hypervel\Database\Events\MigrationStarted A single migration is about to be executed.
Hypervel\Database\Events\MigrationEnded A single migration has finished.
Hypervel\Database\Events\MigrationSkipped A single migration has been skipped.
Hypervel\Database\Events\NoPendingMigrations A migration command found no pending migrations.
Hypervel\Database\Events\SchemaDumped A database schema dump has finished.
Hypervel\Database\Events\SchemaLoaded An existing database schema dump has loaded.
Hypervel\Database\Events\MigrationsPruned Existing migration files have been pruned.

The MigrationStarted and MigrationEnded events expose the migration filename through their $name property. The MigrationEnded event also exposes how long the migration took to run, in milliseconds, through its $duration property.

You are visitor 01402520 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.