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.

Maintainers

Package info

gitlab.com/tereta/library/migration

Homepage

Issues

pkg:composer/tereta/migration

Statistics

Installs: 0

Dependents: 0

Suggesters: 0

Stars: 0

1.0.4 2026-05-02 23:01 UTC

This package is not auto-updated.

Last update: 2026-06-24 05:21:19 UTC


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 .sql and .php files.

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 = true it is ordered by foreign-key relations (parent tables come before dependent ones).
  • $schema->dump($table) β€” dumps a table structure (columns, indexes, foreign keys) into a DOMDocument.
  • $schema->restore($domDocument) β€” recreates the table from an XML document (the result of dump()); 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 DOMDocument in memory β€” dump($table) and restore($document);
  • straight through a file β€” dumpToFile($table, $path) and restoreFromFile($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 a DOMDocument.
  • $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 as dump(), but writes the XML straight to the file $path. Returns $this.
  • $data->restoreFromFile($path) β€” loads the XML from the file $path and restores the rows (like restore()). 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');