tereta / migration
Database schema facade over tereta/dbal: list tables ordered by foreign-key relations, dump and restore table structure as XML, dump and restore data as XML, apply .sql/.php migrations.
Requires
- php: >=8.2
- ext-dom: *
- ext-pdo: *
- tereta/dbal: ^1.0
Requires (Dev)
- phpstan/phpstan: ^2.0
- phpunit/phpunit: ^11.0
- squizlabs/php_codesniffer: ^3.0
README
π English | Π ΡΡΡΠΊΠΈΠΉ | Π£ΠΊΡΠ°ΡΠ½ΡΡΠΊΠ°
Introduction
Tereta/Migration is a facade over tereta/dbal for working with table schemas. It can:
- get the list of database tables, including ordered by foreign-key relations;
- dump a table structure (columns, indexes, foreign keys) into an XML document;
- restore a table from such an XML document;
- apply migrations from
.sqland.phpfiles.
The package exposes three PDO-bound facades, each over its service:
Tereta\Migration\Schema- dumping and restoring table structure as XML, listing tables;Tereta\Migration\Data- dumping and restoring table rows as XML;Tereta\Migration\Migration- applying migrations.
SQL generation is delegated to tereta/dbal, so the same drivers from that package are supported (see the tereta/dbal package README.md)
Requirements
- PHP 8.2+
- ext-pdo
- ext-xml
- tereta/dbal
Installation
Install via Composer:
composer require tereta/migration
Getting started
Each facade takes a PDO instance in its constructor, so the connection is not passed on every call:
use Tereta\Migration\Schema;
use Tereta\Migration\Migration;
use Tereta\Migration\Data;
$schema = new Schema($pdo); // structure: dump / restore / list
$migration = new Migration($pdo); // migrations
$data = new Data($pdo); // rows: dump / restore
Working with schemas (Tereta\Migration\Schema)
Schema is about the structure of tables, not the data inside them. It does three things:
list the tables, dump a table structure (columns, keys, indexes) into XML, and recreate the table from that XML.
Handy when you need to move the database structure elsewhere or save it to a file.
Usually it is enough to pass only the connection β new Schema($pdo). The other constructor
parameters are needed only if you want to swap the internal implementation (DI); by default
they are created automatically.
public __construct(
private PDO $pdo, // the database connection
?DbalSchema $schema = null, // the tereta/dbal schema engine (its own by default)
?SchemaInterface $structure = null // the service that builds and reads XML
)
A usage example of the functions:
$schema = new Schema($pdo);
// list of tables (with true β ordered by foreign-key relations)
$tables = $schema->list();
$ordered = $schema->list(true);
// dump a table structure into a DOMDocument and recreate it from it
$document = $schema->dump('book');
$schema->restore($document);
Functions
$schema->list($sort)β the list of table names; with$sort = trueit is ordered by foreign-key relations (parent tables come before dependent ones).$schema->dump($table)β dumps a table structure (columns, indexes, foreign keys) into aDOMDocument.$schema->restore($domDocument)β recreates the table from an XML document (the result ofdump()); returns$this, so calls can be chained.
Working with data (Tereta\Migration\Data)
Data is about the data (rows) of a table, not its structure. It can dump the rows into XML and insert them back.
Handy when you need to move a table's contents into another database or save them to a file.
There are two options to choose from:
- via a
DOMDocumentin memory βdump($table)andrestore($document); - straight through a file β
dumpToFile($table, $path)andrestoreFromFile($path).
Restore runs inside a single transaction: if something fails midway, the rows already inserted are rolled back - the table will not be left half-filled.
Usually a PDO connection is enough β new Data($pdo).
The second constructor parameter is for swapping the implementation via DI (?DataInterface); by default its own service is created.
// $customData implements Tereta\Migration\Interfaces\Data
new Data($pdo, $customData);
The default implementation of Tereta\Migration\Interfaces\Data uses DOMDocument, so the whole dump is held in memory.
If you need lazy loading and dumping β plug in your own implementation of Tereta\Migration\Interfaces\Data.
A usage example of the functions:
$data = new Data($pdo);
// dump into a DOMDocument and restore it back
$document = $data->dump('widget'); // returns a DOMDocument
$data->restore($document); // accepts a DOMDocument
// dump straight to a file and restore from a file
$data->dumpToFile('widget', '/tmp/widget.xml'); // saves to a file
$data->restoreFromFile('/tmp/widget.xml'); // reads from a file
Functions
$data->dump($table)β dumps the table rows into aDOMDocument.$data->restore($document)β inserts the rows from XML into the table (it must already exist); everything in a single transaction β on failure what was already inserted is rolled back. Returns$this.$data->dumpToFile($table, $path)β the same asdump(), but writes the XML straight to the file$path. Returns$this.$data->restoreFromFile($path)β loads the XML from the file$pathand restores the rows (likerestore()). Returns$this.
Working with migrations (Tereta\Migration\Migration)
Migration applies migrations from files - .sql and .php - one after another.
Each file is applied only once: what has already been applied is recorded in a tracking table (migrations by default), and a file is identified by its name.
So a repeated migrate() call is safe - already applied files are skipped.
Each file runs inside its own transaction.
Files are applied in the order of the numeric prefix before the first _ (001_, 002_, β¦), regardless of the order in which you passed the array.
$migration = new Migration($pdo);
$migration->migrate([
__DIR__ . '/migrations/001_create_widget.sql',
__DIR__ . '/migrations/002_seed_widget.php',
]);
A .php migration returns a function that receives the connection (and optionally the tereta/dbal builder):
<?php
use Tereta\Dbal\Builder;
return function (PDO $pdo, ?Builder $builder = null): void {
(new Builder($pdo))->insert('widget')->value('name', 'one')->execute();
};
Usually new Migration($pdo) is enough. The second constructor parameter is for swapping the implementation via DI (?MigrationInterface); by default its own service is created.
// $customMigration implements Tereta\Migration\Interfaces\Migration
new Migration($pdo, $customMigration);
A usage example of the functions:
$migration = new Migration($pdo);
// a single file
$migration->migrate(__DIR__ . '/migrations/001_create_widget.sql');
// a custom tracking table instead of 'migrations'
$migration->migrate(__DIR__ . '/migrations/002_seed_widget.php', 'my_migrations');
// migrate() returns $this β calls can be chained
$migration
->migrate(__DIR__ . '/migrations/003_index.sql')
->migrate(__DIR__ . '/migrations/004_data.php');