-
-
Notifications
You must be signed in to change notification settings - Fork 0
Writing Migrations
Every migration implements MigrationInterface:
interface MigrationInterface
{
public function up(QueryInterface $query): bool;
public function down(QueryInterface $query): bool;
public function getName(): string;
}In practice you extend MigrationAbstract,
which implements getName() for you (it returns the unqualified class name),
leaving you to write only up() and down().
<?php
declare(strict_types=1);
namespace App\Migrations;
use InitPHP\Barbarian\MigrationAbstract;
use InitPHP\Barbarian\QueryInterface;
final class Migration_20240101000000 extends MigrationAbstract
{
public function up(QueryInterface $query): bool
{
$query->query('CREATE TABLE IF NOT EXISTS `users` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`name` VARCHAR(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;');
return true;
}
public function down(QueryInterface $query): bool
{
$query->query('DROP TABLE IF EXISTS `users`');
return true;
}
}| Aspect | Rule |
|---|---|
| File name |
Migration_<timestamp>.php, e.g. Migration_20240101000000.php. Only files matching Migration_*.php are discovered. |
| Class name | Must equal the file name, so the file can be required and the class resolved. |
| Version name | The unqualified class name. It is the key in the version table, so it must be unique across your migration set. |
The create command follows these
conventions automatically and produces Migration_<YmdHis> files.
Because the version name is the unqualified class name, two migrations in different namespaces but with the same short name would collide. Keep your timestamps unique.
Both up() and down() receive a QueryInterface:
public function query(string $sql, ?array $arguments = null): \PDOStatement;Bound parameters — pass them as the second argument:
$query->query('INSERT INTO settings (k, v) VALUES (:k, :v)', [
':k' => 'installed_at',
':v' => date('c'),
]);Reading results — it returns the executed PDOStatement:
$statement = $query->query('SELECT COUNT(*) FROM users');
$count = (int) $statement->fetchColumn();Errors — a failed statement throws a \PDOException rather than returning a
falsy value. The connection is always in PDO::ERRMODE_EXCEPTION mode (the
QueryRunner forces it), so you never have to
check return values for false.
up() and down() must return bool:
| Return | Effect |
|---|---|
true |
The change succeeded; Barbarian records the new status. |
false |
The change did not happen; Barbarian leaves the recorded status untouched, so the migration can be retried. |
public function up(QueryInterface $query): bool
{
if (!$this->preconditionMet($query)) {
return false; // nothing recorded; safe to run again later
}
$query->query('...');
return true;
}Throwing an exception from up()/down() aborts the run entirely and, under
the CLI, is reported as an error (see Exceptions).
| Situation | upMigration() |
downMigration() |
|---|---|---|
| Never recorded | Runs up(), records Up → true
|
Nothing to revert → false
|
| Currently Down | Runs up(), records Up → true
|
Already down → false (unless force) |
| Currently Up | Already up → false (unless force) |
Runs down(), records Down → true
|
up()/down() returns false
|
No state change → false
|
No state change → false
|
The optional force argument re-runs a migration even when it is already in the
target state — see upMigration() and
downMigration().
The SQL you write is yours to keep portable across engines. If you need to
branch on the driver, the object passed to your migration is a
QueryRunner, which exposes driver():
public function up(QueryInterface $query): bool
{
$driver = $query instanceof \InitPHP\Barbarian\QueryRunner ? $query->driver() : null;
// ... emit driver-appropriate SQL ...
return true;
}See Database Drivers for engine-specific notes.
- CLI Commands — running migrations from the terminal.
- Programmatic Usage — running them from code.
- API Reference — every method, typed.
initphp/barbarian · MIT License · part of the InitPHP family
Source · Issues · Discussions · Packagist · Contributing · Security Policy
Getting Started
Guide
Reference
Practical Guides
Help